How to implement undo-redo functionality in hiding QGraphicsItem using Command-Pattern in Qt?
-
I am having a QGraphicsView which contains some QGraphicsItem I have a feature (Hide Item) which on mouse right click, hide desired QGraphicsItem(Rectangle) and its connected polylines. I have a Undo-Redo feature also.
Undo - It should cancel the effect of last command executed and show previous transformation. Redo - It will undo the previous Undo.
To implement this Undo-Redo feature I have used Command pattern. I have implemented Undo-Redo feature for ZoomIn-ZoomOut.
Question is : I dont know how to implement Undo-Redo for Hide feature. Means what to push into stack, what to pull ?
Below Undo-Redo code is for ZoomIn-ZoomOut feature. (It is just for reference that I want to implement Hide feature's Undo-Redo something like this. )
myCommand.c
class myCommand: public QUndoCommand { public: myCommand(); myCommand(double scale, QGraphicsScene* scene,QGraphicsView* view); private: QGraphicsItem* mItem; QGraphicsScene* mScene; QGraphicsView* mView; double scaleFactor; void undo(); void redo(); }
myCommand.cpp
myCommand::myCommand(double scale, QGraphicsScene *scene,QGraphicsView* view): mScene(scene), mView(view),scaleFactor(scale) {} void guiCommand::undo() { mView->scale(1/_scaleFactor,1/_scaleFactor); } void myCommand::redo() { mView->scale(_scaleFactor,_scaleFactor); }
myView.cpp
void myView::ZoomIn() { double scaleFactor = 1.1; view->scale(scaleFactor,scaleFactor); myCommand* command1 = new myCommand(scaleFactor,scene,view); undoStack->push(command1); }
myView.h
public: QUndoStack* undoStack;
-
I am having a QGraphicsView which contains some QGraphicsItem I have a feature (Hide Item) which on mouse right click, hide desired QGraphicsItem(Rectangle) and its connected polylines. I have a Undo-Redo feature also.
Undo - It should cancel the effect of last command executed and show previous transformation. Redo - It will undo the previous Undo.
To implement this Undo-Redo feature I have used Command pattern. I have implemented Undo-Redo feature for ZoomIn-ZoomOut.
Question is : I dont know how to implement Undo-Redo for Hide feature. Means what to push into stack, what to pull ?
Below Undo-Redo code is for ZoomIn-ZoomOut feature. (It is just for reference that I want to implement Hide feature's Undo-Redo something like this. )
myCommand.c
class myCommand: public QUndoCommand { public: myCommand(); myCommand(double scale, QGraphicsScene* scene,QGraphicsView* view); private: QGraphicsItem* mItem; QGraphicsScene* mScene; QGraphicsView* mView; double scaleFactor; void undo(); void redo(); }
myCommand.cpp
myCommand::myCommand(double scale, QGraphicsScene *scene,QGraphicsView* view): mScene(scene), mView(view),scaleFactor(scale) {} void guiCommand::undo() { mView->scale(1/_scaleFactor,1/_scaleFactor); } void myCommand::redo() { mView->scale(_scaleFactor,_scaleFactor); }
myView.cpp
void myView::ZoomIn() { double scaleFactor = 1.1; view->scale(scaleFactor,scaleFactor); myCommand* command1 = new myCommand(scaleFactor,scene,view); undoStack->push(command1); }
myView.h
public: QUndoStack* undoStack;
-
I am having a QGraphicsView which contains some QGraphicsItem I have a feature (Hide Item) which on mouse right click, hide desired QGraphicsItem(Rectangle) and its connected polylines. I have a Undo-Redo feature also.
Undo - It should cancel the effect of last command executed and show previous transformation. Redo - It will undo the previous Undo.
To implement this Undo-Redo feature I have used Command pattern. I have implemented Undo-Redo feature for ZoomIn-ZoomOut.
Question is : I dont know how to implement Undo-Redo for Hide feature. Means what to push into stack, what to pull ?
Below Undo-Redo code is for ZoomIn-ZoomOut feature. (It is just for reference that I want to implement Hide feature's Undo-Redo something like this. )
myCommand.c
class myCommand: public QUndoCommand { public: myCommand(); myCommand(double scale, QGraphicsScene* scene,QGraphicsView* view); private: QGraphicsItem* mItem; QGraphicsScene* mScene; QGraphicsView* mView; double scaleFactor; void undo(); void redo(); }
myCommand.cpp
myCommand::myCommand(double scale, QGraphicsScene *scene,QGraphicsView* view): mScene(scene), mView(view),scaleFactor(scale) {} void guiCommand::undo() { mView->scale(1/_scaleFactor,1/_scaleFactor); } void myCommand::redo() { mView->scale(_scaleFactor,_scaleFactor); }
myView.cpp
void myView::ZoomIn() { double scaleFactor = 1.1; view->scale(scaleFactor,scaleFactor); myCommand* command1 = new myCommand(scaleFactor,scene,view); undoStack->push(command1); }
myView.h
public: QUndoStack* undoStack;
@tushu said in How to implement undo-redo functionality in hiding QGraphicsItem using Command-Pattern in Qt?:
Question is : I dont know how to implement Undo-Redo for Hide feature. Means what to push into stack, what to pull ?
Just like you have done for your zoom in/out, you must push some kind of
QUndoCommand
-derived object to the undo stack. It doesn't matter what that contains, so long as it gives you the information for the redo/undo.You have different kinds of actions to undo/redo --- first the zoom, now the visibility, maybe more in future. You have two approaches:
-
Stick with your current single derived class,
myCommand
. Add further members for the information you will need. You presently havedouble scaleFactor;
for the zoom, you would need theQGraphicsItem *
and abool isVisible
for the rectangle hide/show. Then you would also need an indicator of whether the particular instance is a zoom or visibility change. To allow for more actions, you would probably want anenum ActionType { Zoom, Visible, ... }
, and aswitch
statement inundo
/redo()
to decide which action themyCommand
refers to. -
That may get messy over time, as you add more member variables and action types, bigger
switch
statement, etc. You may prefer to define distinct sub-classes for each action type.class ZoomUndoCommand: public QUndoCommand
,class VisibiltyUndoCommand: public QUndoCommand
, ... Now you create an instance and push whichever type for the action you are doing. Each one has only its own variables relevant to its action, and theundo
/redo()
does not need aswitch
statement. If you find there is any commonality/shared data between your classes, you can alwaysclass CommonUndoCommand: public QUndoCommand
, put common code in there, and then derive your specific classes fromCommonUndoCommand
.
-
-
@tushu said in How to implement undo-redo functionality in hiding QGraphicsItem using Command-Pattern in Qt?:
Question is : I dont know how to implement Undo-Redo for Hide feature. Means what to push into stack, what to pull ?
Just like you have done for your zoom in/out, you must push some kind of
QUndoCommand
-derived object to the undo stack. It doesn't matter what that contains, so long as it gives you the information for the redo/undo.You have different kinds of actions to undo/redo --- first the zoom, now the visibility, maybe more in future. You have two approaches:
-
Stick with your current single derived class,
myCommand
. Add further members for the information you will need. You presently havedouble scaleFactor;
for the zoom, you would need theQGraphicsItem *
and abool isVisible
for the rectangle hide/show. Then you would also need an indicator of whether the particular instance is a zoom or visibility change. To allow for more actions, you would probably want anenum ActionType { Zoom, Visible, ... }
, and aswitch
statement inundo
/redo()
to decide which action themyCommand
refers to. -
That may get messy over time, as you add more member variables and action types, bigger
switch
statement, etc. You may prefer to define distinct sub-classes for each action type.class ZoomUndoCommand: public QUndoCommand
,class VisibiltyUndoCommand: public QUndoCommand
, ... Now you create an instance and push whichever type for the action you are doing. Each one has only its own variables relevant to its action, and theundo
/redo()
does not need aswitch
statement. If you find there is any commonality/shared data between your classes, you can alwaysclass CommonUndoCommand: public QUndoCommand
, put common code in there, and then derive your specific classes fromCommonUndoCommand
.
@JonB Thank you for your reply. I tried to implement above feature. But when I Undo it is not showing back my item on the screen.
I try to explain you what I am doing.On every QGraphicsItem I have stored graph node's pointer. When I will click on any QGraphicsItem, to hide it, its paint() method gets called and in there from that QGraphicsItem, I get graph node's pointer and in graph node I have a field _isVisible. I make it false, and then I tell to paint() please hide this item.
So when I do Undo, I expect, nodes address should get, and I will make it's _isVisible = true and will tell paint() please show this item. So everything is working (I put some QDebug statements and checked ) fine but it is not getting showed on the screen. That's the problem.
In between I want to tell you, when I get nodes pointer , I iterate over all its neighbour and make their isVisible = false. So for every visited node, it's paint() gets called and there it checks isVisible = false/true. Accordingly paint() takes decision.
For undo, I call function which will iterate all its neighbour and will make isVisible = true. And paint() will see it and will show that item.
void myRect::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { vPtr = this->getBoostPtr(); if(vPtr->_isVisible == false) this->hide(); else { this->show(); qDebug()<<"Undo Rect"; } }
myCommand
myCommand* command3 = new myCommand(isRectHiddden,vPtr,GraphName); undoStack->push(command3);
isRectHidden -> bool
vPtr -> nodes pointer
GraphName -> string -
-
@JonB Thank you for your reply. I tried to implement above feature. But when I Undo it is not showing back my item on the screen.
I try to explain you what I am doing.On every QGraphicsItem I have stored graph node's pointer. When I will click on any QGraphicsItem, to hide it, its paint() method gets called and in there from that QGraphicsItem, I get graph node's pointer and in graph node I have a field _isVisible. I make it false, and then I tell to paint() please hide this item.
So when I do Undo, I expect, nodes address should get, and I will make it's _isVisible = true and will tell paint() please show this item. So everything is working (I put some QDebug statements and checked ) fine but it is not getting showed on the screen. That's the problem.
In between I want to tell you, when I get nodes pointer , I iterate over all its neighbour and make their isVisible = false. So for every visited node, it's paint() gets called and there it checks isVisible = false/true. Accordingly paint() takes decision.
For undo, I call function which will iterate all its neighbour and will make isVisible = true. And paint() will see it and will show that item.
void myRect::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { vPtr = this->getBoostPtr(); if(vPtr->_isVisible == false) this->hide(); else { this->show(); qDebug()<<"Undo Rect"; } }
myCommand
myCommand* command3 = new myCommand(isRectHiddden,vPtr,GraphName); undoStack->push(command3);
isRectHidden -> bool
vPtr -> nodes pointer
GraphName -> string@tushu
You should not have anything inpaint()
. Do yourhide()
/show()
in theundo
/redo()
, just like you did for the scaling.Whatever you need to do with boost do it there.
When I will click on any QGraphicsItem, to hide it, its paint() method gets called and in there from that QGraphicsItem, I get graph node's pointer and in graph node I have a field _isVisible. I make it false, and then I tell to paint() please hide this item.
Like I said, nothing in
paint()
.paint()
is only for drawing an item, if you need your own drawing (you don't), not for logic/changing state of gfx items. Do whatever work, boost or otherwise, on the item's click event.