'Undo-Redo functionality using Command-Pattern in Qt for FitInView feature
I have QGraphicsView which contains some QGraphicsItem. This view has some feature like zoom-in, zoom-out, fitIn, Undo-Redo.
My fitIn feature is not working in Undo-Redo functionality.
( To implement Undo-Redo I have used Command-Pattern in Qt. )
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 fits my design perfectly but it does not work in Undo-Redo feature.
I think I am making a mistake in myCommand::undo() and myCommand::redo() function.
Solution 1:[1]
Based on the many question you posted on the Qt's Undo Framework, it seems to me you are missing an essential part of the Command pattern:
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]
So all changes, should be implemented inside the 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, the change (i.e. QCommand::redo) is automatically performed:
Pushes
cmdon the stack or merges it with the most recently executed command. In either case, executescmdby calling its redo() function. [4]
Steps to create a new QUndoCommand
- Implement the desired changes inside
QUndoCommand::redo(and not somewhere else). - Write the inverse of this command inside
QUndoCommand::undo. If needed, capture the initial state inside theQUndoCommandconstructor, to easily revertQUndoCommand::redo.
Applied to your example
So, the only action that myView::FitIn should perform, is pushing the command on the undo stack:
void myView::FitIn()
{
undoStack->push(new fitCommand(view));
}
Implement your changes inside the redo command:
void fitCommand::redo()
{
mView->FitInView(); // Can be implemented using QGraphicsView::fitInView
}
As the inverse operation of fitting is not uniquely defined, we store the initial state inside the QUndoCommand constructor (to be able to restore the initial state inside QUndoCommand::undo):
fitCommand::fitCommand(myView *view)
{
this->t = view->transform();
this->hScrollBar= view->horizontalScrollBar()->value();
this->vScrollBar= view->verticalScrollBar()->value();
this->mView = view;
}
Now we can implement the undo command to revert the redo command:
void fitCommand::undo()
{
mView->setTransform(t);
mView->horizontalScrollBar()->setValue(hScrollBar);
mView->verticalScrollBar()->setValue(vScrollBar);
}
Usefull reading material:
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | m7913d |
