我有QGraphicsView
,它包含一些QGraphicsItem
。此视图具有一些特性,如zoom-in
、zoom-out
、fitIn
、Undo-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
的步骤
- 在
QUndoCommand::redo
内部(而不是其他地方)实现所需的更改 - 在
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
的详细说明,包括基本代码示例