Qt中的撤銷/重做功能

兜尼完發表於2024-04-02

作為一個例子,本例只實現了在列表控制元件“新增項”和“修改項名稱”的2個操作。介面上顯示一個列表框,列表框需要在介面設計器中設為IconMode,這樣就會是圖示在上文字在下的顯示樣式。“新增”按鈕用來在列表框中加一個項。點選圖示下面的文字可以修改文字名稱。程式測試環境是VS2017和Qt5.9,測試結果功能正常,執行效果如下:

標頭檔案:

class QListWidget;

class MAddImage : public QUndoCommand
{
public:
    MAddImage(const QString &text, QListWidget* isrc, QUndoCommand *parent = Q_NULLPTR);
    ~MAddImage() override = default;
    void setActionInfo(const QPixmap& image, int where);
    void undo() override;
    void redo() override;

private:
    QListWidget* src;
    QPixmap pixmap;
    int pos;
};

class MChangeName : public QUndoCommand
{
public:
    MChangeName(const QString &text, QListWidget* isrc, QUndoCommand *parent = Q_NULLPTR);
    ~MChangeName() override = default;
    void setActionInfo(const QString& oldName, const QString& newName, int where);
    void undo() override;
    void redo() override;

private:
    QListWidget* src;
    QString newText;
    QString oldText;
    int pos;
};

CPP檔案:

MAddImage::MAddImage(const QString &text, QListWidget* isrc, QUndoCommand *parent) :
    QUndoCommand(text, parent)
{
    src = isrc;
}

void MAddImage::setActionInfo(const QPixmap& image, int where)
{
    pixmap = image;
    pos = where;
}

void MAddImage::undo()
{
    delete src->takeItem(pos);
}

void MAddImage::redo()
{
    QListWidgetItem* item = new QListWidgetItem(QIcon(pixmap), QString(u8"未命名"));
    item->setFlags(Qt::ItemIsEditable | item->flags());
    src->insertItem(pos, item);
}

MChangeName::MChangeName(const QString &text, QListWidget* isrc, QUndoCommand *parent) : 
    QUndoCommand(text, parent)
{
    src = isrc;
}

void MChangeName::setActionInfo(const QString& oldName, const QString& newName, int where)
{
    oldText = oldName;
    newText = newName;
    pos = where;
}

void MChangeName::undo()
{
    src->blockSignals(true);
    QListWidgetItem* item = src->item(pos);
    item->setText(oldText);
    src->blockSignals(false);
}

void MChangeName::redo()
{
    src->blockSignals(true);
    QListWidgetItem* item = src->item(pos);
    item->setText(newText);
    src->blockSignals(false);
}

在主視窗類中有幾個槽函式需要實現以響應使用者的操作。下面的程式碼中QtTest是主視窗類;ui.lwList是列表控制元件;(ui.)pbAdd是“新增”按鈕;(ui.)pbUndo是“撤銷”按鈕;(ui.)pbRedo是“重做”按鈕。operas是QtTest類成員變數,定義是 QUndoStack operas; ,它是Qt封裝好的用於管理撤銷/重做命令的類,詳情可以查閱Qt幫助文件。

void QtTest::on_lwList_currentRowChanged(int row)
{
    QListWidgetItem* it = ui.lwList->item(row);
    currEditText = it->text();
}

void QtTest::on_lwList_itemChanged(QListWidgetItem *item)
{
    MChangeName* changeAct = new MChangeName(u8"修改名稱", ui.lwList);
    changeAct->setActionInfo(currEditText, item->text(), ui.lwList->row(item));
    operas.push(changeAct); /* 由於push(...)自動觸發redo()操作,因此這裡重新命名兩次 */
}

void QtTest::on_pbAdd_clicked()
{
    QPixmap pixmap(100, 80);
    pixmap.fill(QColor(qrand() % 255, qrand() % 255, qrand() % 255));
    MAddImage* addAct = new MAddImage(u8"新增圖片", ui.lwList);
    addAct->setActionInfo(pixmap, ui.lwList->count());
    operas.push(addAct);
}

void QtTest::on_pbUndo_clicked()
{
    operas.undo();
}

void QtTest::on_pbRedo_clicked()
{
    operas.redo();
}

相關文章