Qt表格 QTableWidget QTableView 实现Undo撤回功能

说到撤回功能就能想到栈操作,Qt提供了用于撤回功能的类QUndoStack,栈中存放的是QUndoCommand对象。

实现的思路可以简单理解为,在我们的程序中建立一个QUndoStack对象,然后把我们要Undo/Redo的操作实例为QUndoCommand对象,压入栈,然后就是对栈进行操作了。

关于QUndoStack类和QUndoCommand类的使用,在官方文档中搜索“Overview of Qt's Undo Framework”,Qt还提供了一个例子undoframework。

//----------------------------------------------------------------------------------------分隔线

如果没有使用过QUndoStack类和QUndoCommand类,建议先简单阅读一下官方文档,知道其用法。

下面给出一个我自己使用QUndoStack类和QUndoCommand类实现QTableWidget撤回功能的例子。撤回功能需要知道cell的旧值,我的做法是给QTableWidget设置了委托(TableDelegate类),通过委托记录cell的旧值。

直接上代码,环境Qt_5_12_4_MinGW_32_bit。

ModifCommand类

class ModifCommand : public QUndoCommand
{
public:
    ModifCommand(QTableWidget *pTable, int row, int col, QString oldValue, QString value, QUndoCommand *parent = nullptr);

    void undo() override;
    void redo() override;

private:
    QTableWidget *m_table;
    int m_row;
    int m_col;
    QString m_value;
    QString m_oldValue;
};

ModifCommand::ModifCommand(QTableWidget *pTable, int row, int col, QString oldValue, QString value, QUndoCommand *parent)
    : QUndoCommand(parent)
{
    m_table = pTable;
    m_row = row;
    m_col = col;
    m_oldValue = oldValue;
    m_value = value;
}

void ModifCommand::undo()
{
    m_table->item(m_row, m_col)->setText(m_oldValue);
}

void ModifCommand::redo()
{
    m_table->item(m_row, m_col)->setText(m_value);
    setText(QString("modif %1,%2").arg(m_row+1).arg(m_col+1));
}

TableDelegate类

class TableDelegate : public QStyledItemDelegate
{
    Q_OBJECT
public:
    explicit TableDelegate(QObject *parent = nullptr);

    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &,
                          const QModelIndex &index) const override;
    void setEditorData(QWidget *editor, const QModelIndex &index) const override;
    void setModelData(QWidget *editor, QAbstractItemModel *model,
                      const QModelIndex &index) const override;

signals:
    void beginEdit()const;
};

TableDelegate::TableDelegate(QObject *parent) : QStyledItemDelegate(parent)
{}

// 创建编辑器
QWidget *TableDelegate::createEditor(QWidget *parent,
                                          const QStyleOptionViewItem &,
                                          const QModelIndex &) const
{
    QLineEdit *editor = new QLineEdit(parent);

    emit beginEdit();

    return editor;
}

// 为编辑器设置数据
void TableDelegate::setEditorData(QWidget *editor,
    const QModelIndex &index) const
{
    QLineEdit *edit = qobject_cast(editor);
    if (edit)
    {
        edit->setText(index.model()->data(index, Qt::EditRole).toString());
        return;
    }
}

// 将数据写入到模型
void TableDelegate::setModelData(QWidget *editor,
    QAbstractItemModel *model, const QModelIndex &index) const
{
    QLineEdit *edit = qobject_cast(editor);
    if (edit)
    {
        model->setData(index, edit->text());
        return;
    }
}

MainWindow类

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    resize(800, 600);

    undoStack = new QUndoStack(this);

    /* 创建Action */
    undoAction = undoStack->createUndoAction(this, tr("&Undo"));
    undoAction->setShortcuts(QKeySequence::Undo);
    redoAction = undoStack->createRedoAction(this, tr("&Redo"));
    redoAction->setShortcuts(QKeySequence::Redo);
    QMenu *editMenu = menuBar()->addMenu("&Edit");
    editMenu->addAction(undoAction);
    editMenu->addAction(redoAction);
    menuBar()->show();

    /* 创建表格 */
    table = new QTableWidget(this);
    table->setRowCount(10);
    table->setColumnCount(7);
    for (int i = 0; i < 10; i++)
    {
        for (int j = 0; j < 7; j++)
        {
            QTableWidgetItem *item = new QTableWidgetItem("0");
            table->setItem(i, j, item);
        }
    }
    table->resize(800, 600);
    setCentralWidget(table);
    table->show();

    /* 给表格设置委托 */
    TableDelegate *delegate = new TableDelegate(this);
    table->setItemDelegate(delegate);
    connect(delegate, &TableDelegate::beginEdit, this, [=]()
    {
        QTableWidgetItem *item = table->currentItem();
        item->setData(Qt::UserRole, item->text()); //jz 保存旧值
    });
    connect(delegate, &TableDelegate::closeEditor, this, [=]()
    {
        QTableWidgetItem *item = table->currentItem();
        QString oldValue = item->data(Qt::UserRole).toString();
        if (item->text() != oldValue)
        {
            undoStack->push(new ModifCommand(table, item->row(), item->column(), oldValue, item->text()));
        }
    });

    /* 创建UndoView */
    undoView = new QUndoView(undoStack);
    undoView->setWindowTitle(tr("Command List"));
    undoView->show();
    undoView->setAttribute(Qt::WA_QuitOnClose, false);
}

//----------------------------------------------------------------------------------------分隔线

上面讲的是QTableWidget实现撤回功能,QTableView实现撤回/重做功能的做法跟QTableWidget基本一样。我提供一个QTableView+QStandardItemModel实现撤回/重做功能的例子,见下文链接。

代码已上传到gitee

tableWidgetUndo: 提供一个示例,QTableWidget实现撤回/重做功能

tableViewUndo: 提供一个示例,QTableView+QStandardItemModel实现撤回/重做功能

你可能感兴趣的:(Qt,qt)