在 Qt 中使用 Command-Pattern for FitInView 功能的撤消-重做功能

Undo-Redo functionality using Command-Pattern in Qt for FitInView feature

我有 QGraphicsView,其中包含一些 QGraphicsItem。此视图具有一些功能,例如 zoom-inzoom-outfitInUndo-Redo
我的 fitIn 功能在 Undo-Redo 功能中不起作用。
(为了实现 Undo-Redo 我在 Qt 中使用了 Command-Pattern。)

myCommand.h

class myCommand: public QUndoCommand
{
public:
    myCommand();
    myCommand(QTransform t, int hr, int vr, QGraphicsView* v);
    void undo();
    void redo();
    QTransform t;
    int hScrollBar;
    int  vScrollBar;
    QGraphicsView* mView;
};

myCommand.cpp

myCommand::myCommand(QTransform t, int hr, int vr, QGraphicsView *v)
{
    this->t = t;
    this->hScrollBar= hr;
    this->vScrollBar= vr;
    this->mView = v;
}

void myCommand::undo()
{
    mView->setTransform(t);
    mView->horizontalScrollBar()->setValue(hScrollBar);
    mView->verticalScrollBar()->setValue(vScrollBar);
}

void myCommand::redo()
{
   myView mv;
   mv.FitInView();
}

myView.cpp

 void myView::FitIn()
    {
        FitInView();
        QTransform t = view->transform();
        int hrValue = view->horizontalScrollBar()->value();
        int vrValue = view->verticalScrollBar()->value();
        myCommand* Command1 = new myCommand(t,hrValue,vrValue,view);
        undoStack->push(Command1);   
    }

void myView::DrawDesign()
{
   // Logic for inserting all the Rectangles and polylines.
   QTimer::singleShot(100, this, [&]() {
        FitInView();
    });
}

void myView::FitInView()
{
    QRectF bounds = scene->sceneRect();
    QRectF rect  {0,0,200,200};
    if (bounds.width() < 200)
    {
        rect .setWidth(bounds.width());
        bounds.setWidth(200);
    }
    if (bounds.height() < 200)
    {
        rect.setWidth(bounds.height());
        bounds.setHeight(200);
    }
    view->fitInView(bounds, Qt::KeepAspectRatio);
    view->updateGeometry();
}

myView.h

public:
      QUndoStack* undoStack;

FitInView 非常适合我的设计,但它不适用于 Undo-Redo 功能。
我想我在 myCommand::undo()myCommand::redo() 函数中犯了一个错误。

根据您在 Qt's Undo Framework 上发布的许多问题,在我看来您遗漏了命令模式的重要部分:

The Command pattern is based on the idea that all editing in an application is done by creating instances of command objects. Command objects apply changes to the document and are stored on a command stack. Furthermore, each command knows how to undo its changes to bring the document back to its previous state. [1]

所以所有的改变,都应该在QUndoCommand (follow the link to discover a basic usage example), i.e. inside QUndoCommand::redo. Once the command is pushed on the QUndoStack, using QUndoStack::push里面实现,改变(即QCommand::redo)是自动执行的:

Pushes cmd on the stack or merges it with the most recently executed command. In either case, executes cmd by calling its redo() function. [4]

创建新 QUndoCommand

的步骤
  1. QUndoCommand::redo(而不是其他地方)内实施所需的更改。
  2. QUndoCommand::undo 中写入此命令的反义词。如果需要,捕获 QUndoCommand 构造函数内的初始状态,以轻松恢复 QUndoCommand::redo.

应用于您的示例

因此,myView::FitIn 应该执行的唯一操作是将命令推送到撤消堆栈:

void myView::FitIn()
{
    undoStack->push(new fitCommand(view));   
}

在重做命令中实施您的更改:

void fitCommand::redo()
{
    mView->FitInView(); // Can be implemented using QGraphicsView::fitInView
}

由于拟合的逆运算不是唯一定义的,我们将初始状态存储在QUndoCommand构造函数中(以便能够在QUndoCommand::undo中恢复初始状态):

fitCommand::fitCommand(myView *view)
{
    this->t = view->transform();
    this->hScrollBar= view->horizontalScrollBar()->value();
    this->vScrollBar= view->verticalScrollBar()->value();
    this->mView = view;
}

现在我们可以执行 undo 命令来还原 redo 命令:

void fitCommand::undo()
{
    mView->setTransform(t);
    mView->horizontalScrollBar()->setValue(hScrollBar);
    mView->verticalScrollBar()->setValue(vScrollBar);
}

有用的阅读material: