使用Qt for FitInView功能中的命令模式撤消重做功能



我有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的Undo Framework上发布的许多问题,在我看来您缺少了命令模式的一个重要部分:

命令模式基于这样一种思想,即应用程序是通过创建命令对象的实例来完成的。命令对象将更改应用于文档并存储在命令中堆栈此外,每个命令都知道如何撤消更改以使文档恢复到以前的状态。[1]

因此,所有更改都应该在QUndoCommand中实现(按照链接查找基本用法示例),即在QUndoCommand::redo中实现。一旦在QUndoStack上按下命令,使用QUndoStack::push,将自动执行更改(即QCommand::redo):

在堆栈上推送cmd或将其与最近执行的命令合并。在任何一种情况下,都会通过调用其redo()函数来执行cmd[4]

创建新QUndoCommand的步骤

  1. QUndoCommand::redo内部(而不是其他地方)实现所需的更改
  2. QUndoCommand::undo中写入该命令的相反部分。如果需要,可以捕获QUndoCommand构造函数内部的初始状态,以便轻松恢复QUndoCommand::redo

应用于您的示例

因此,myView::FitIn应该执行的唯一操作是在undo堆栈上推送命令:

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);
}

有用的阅读材料:

  • Qt撤销框架概述
  • QUndoCommand的详细说明,包括基本代码示例

最新更新