Reputation: 701
Qt::QUndoCommand problem and possible solutions.
We are developing an application of 3D editing in Qt.
We need to implement a "stack of operations" which allow the user to call undo-redo on his operations.
We are using QUndoStack with QUndoCommand Qt classes.
The application is based on the MVC pattern so the View (QGLWidget) knows how to draw the scene structure.
We have sliders (QSlider) to translate/rotate/scale the 3D objects (Meshes), and we need to show the effect of the transformations in real-time.
For example if I have selected an object and I'm moving the "X Translation Slider" I want to see the object moving along the X axis while I'm dragging the slider. The problem is to get the real-time editing working with the stack of operations. In fact the "undoable" operation who should be pushed on the stack is the total slider movement (from the pression to the release of the slider).
We find out 2 ways of doing this:
While I'm dragging the slider (at every valueChanged signal) the transformations are applied to the model and the QGLWidget updates straight after every slider tick. At the release of the slider the command must be pushed in the stack. QUndoStack automatically calls the QUndoCommand::redo() action when a command is pushed. To prevent the operation to be executed twice (the first time to grant the real-time effect, the second at the QUndoStack::push() call), before the call of QUndoStack::push() The inverse transformation is applied to the object (obtained from the total slider movement) and then I push the command to the stack.
QUndoStack tryes to merge command when they have the same result from a call to QUndoCommand::Id(). While I'm dragging the slider (at every valueChanged signal) a QUndoCommand is generated and immediately pushed on the stack, the stack merges it with the command at the top if they have the same Id(), then the stack calls the redo() for the command who is being inserted, then the QGLWidget updates and the real-time effect is obtained.
With the second one an instance of the "Command" class is generated every slider tick, while with the first one the real-time operation get's reverted just to push a command and to stay in a consistent state.
What solution is better in terms of "good programming"?What is better in terms of performance?
Upvotes: 0
Views: 1805
Reputation: 2016
Chris is correct, you could use macros as well as your proposed solutions. But I don't think his criticism of 'overly complicated' is fair: the Qt mergeWith AND macro mechanisms are intended for your purpose.
I would not worry about the performance of any of the solutions, until you find that the performance is a problem. Get it working, then test for performance, and then fix any performance issue. You can't know until you try it whether the generation of excessive commands(that are then merged) or your inverse transformation is a performance problem.
Another solution: on the first tick, push a command and keep a reference to it. On subsequent ticks, update the command and perform an increment of transformation?
(There is a related problem where a command is pushed but a user cancels the operation. For example, what if the user drags the slider, but releases the mouse outside the slider? Is the slide canceled? Search for "transactional undo command". It reimplements redo so that it does nothing the first time it is called as the command is stacked, and has a commit() or rollback() method that is called later.)
(I learned something from your post: when self.mergeWith(other) is called, Qt calls the redo method of other. That's not clear from the documentation.)
Upvotes: 1
Reputation: 17535
I think you're looking for QUndoStack::beginMacro()
and QUndoStack::endMacro()
Which can be used to merge a series of commands in the undo stack so that they're done/undone as an atomic operation.
Upvotes: 3