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.
CodePudding user response:
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
cmd
on the stack or merges it with the most recently executed command. In either case, executescmd
by 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 theQUndoCommand
constructor, 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);
}