在 Qt 中使用 Command-Pattern for FitInView 功能的撤消-重做功能
Undo-Redo functionality using Command-Pattern in Qt for FitInView feature
我有 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'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
的步骤
- 在
QUndoCommand::redo
(而不是其他地方)内实施所需的更改。
- 在
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:
我有 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'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, executescmd
by calling its redo() function. [4]
创建新 QUndoCommand
的步骤
- 在
QUndoCommand::redo
(而不是其他地方)内实施所需的更改。 - 在
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);
}