Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Disabling QUndoStack



  • Is there a way to temporarily 'disable' a QUndoStack in the sense that calls to QUndoStack::push will get ignored?

    I have tried QUndoStack::setActive but that does not work... In my specific case, I cannot avoid that QUndoStack::push is called while under the circumstances that I consider, I do not want the respective QUndoCommands to be added to the QUndoStack.

    To clarify the circumstances for which I want to use this. My app uses a clear model-view separation and I have several graphical views. For some of these graphical views, certain properties of the model must be set in case they are still the default. For example, certain model items are represented by a box. By default, the box is of size (0,0). When drawn for the first time, the appropriate (minimum) size is determined (and leads to pushing QUndoCommands to the QUndoStack). However, this change in size should not be undoable by the end user. The fact that I only want a resize command that always pushes QUndoCommands is that this is part of the API for my model that plugin developers for my app may use and in such case the resize action must always be undoable.


  • Moderators

    Is there a way to temporarily 'disable' a QUndoStack in the sense that calls to QUndoStack::push will get ignored?

    No, QUndoStack takes ownership of the command passed. If it "sometimes" ignored it there would be no way of knowing about it and it would result in command objects leaking.

    If you want that behavior you could wrap the direct push call into your own api that would do the push or not and return some info about what happened.


  • Lifetime Qt Champion

    Hi,

    To add to @Chris-Kawa, if you want something not to handled by the QUndoStack, then why use a command for that particular action ?



  • @Chris-Kawa Thanks for your suggestion. So, I have come up with the following idea. I subclass QUndoStack. Let's call that MyStack and let it have an attribute Enabled. I introduce a method MyStack::enable(bool) which sets the Enabled attribute. In addition, I have a method MyStack::mypush() or I override QUndoStack::push with something like the following. Would that work?

    void MyStack::mypush(QUndoCommand *Command) {
    
        if (Enabled)
            push(Command);
        else
            Command->redo();
    }
    

  • Moderators

    Would that work?

    Yeah, except, as I said, that leaks the Command object. If you don't push it you need to delete it:

    void MyStack::mypush(QUndoCommand *Command) {
    
        if (Enabled)
            push(Command);
        else {
            Command->redo();
            delete Command;   // <-- otherwise it will leak
        }
    }
    

    Also, keep in mind that with such setup you need to be very very careful about your commands, as this creates an inconsistent state in the system. stack->push(...) followed by stack->undo() should bring the system to the state before the operation which it does not in case of that solution. You're "pushing" a state that is skipped when undo is invoked and also can't be re-done. This means that sequence mypush -> undo -> redo does unexpected things.

    It can lead to very difficult bugs e.g. when such command deletes or modifies a state of some object mentioned in another undo command.



  • Thanks, I just came back from a coffee with the idea that I forgot the delete :) I don't have a need to call any undo's in this case, so it should be fine. It will be some effort to implement this throughout my app, but with this answer I will give it a go.



  • So, I have now implemented this and it works perfectly. To be complete, I wanted to mention that I also filter the QUndoStack::beginMacro and QUndoStack::endMacro calls.


Log in to reply