wx.Command/Processor - how to group execution and undo of commands?

I’m currently developing out undo/redo functionality for my application after 8 years or it being conspicuously missing >.< I’m utilizing the build in classes that wx provides to develop out a command pattern foe the various actions that the application has. I have come across a question though:

Is there a way to group commands together to execute / undo together? For example, I have a command that should execute other commands (lets say two). But that should be considered “one” command, instead of two. Consequently, when the user undos this action, it should actually undo the two “inner” commands.

I’ve developed a small demonstration: I have one command (MyCommand) that, as part of it’s execution, calls two other commands. However, I am forced to Undo both explicitly. One note- I return False for the MyCommand.Do() since I don’t want it on the stack (because then it will force me to do three undos to get back)

import wx

class OtherCommand1(wx.Command):
    def Do(self):
        print('Do OtherCommand 1')
        return True

    def Undo(self):
        print('Undo OtherCommand 1')
        return True

class OtherCommand2(wx.Command):
    def Do(self):
        print('Do OtherCommand 2')
        return True

    def Undo(self):
        print('Undo OtherCommand 2')
        return True

class MyCommand(wx.Command):
    def __init__(self, queue):
        wx.Command.__init__(self, True)
        self.queue = queue

    def Do(self):
        self.queue.Submit(OtherCommand1(True))
        self.queue.Submit(OtherCommand2(True))
        return False

    def Undo(self):
        return True

queue = wx.CommandProcessor()
queue.Submit(MyCommand(queue))
print(queue.Commands)
queue.Undo()
queue.Undo()

``

Output:

Do OtherCommand 1
Do OtherCommand 2
CommandList: [<main.OtherCommand1 object at 0x04602AD0>, <main.OtherCommand2 object at 0x04602B20>]
Undo OtherCommand 2
Undo OtherCommand 1

``

So I’m wondering if there’s a good method to package multiple commands up as one, as it doesn’t look like the API provides a way to do this itself. One way I have thought of is to have MyCommand keep it’s own internal history that it submits the inner commands to, and then on it’s Undo(), loop through and Undo the inner ones. Another method would be to possibly override the CommandProcessor class to extend it’s functionality in a way that allows for this. I’m sure these would be fine ways of doing it, but wanted to gather feedback and thoughts on other ways

For completeness, this the solution I discussed, having an internal queue

import wx

class OtherCommand1(wx.Command):
    def Do(self):
        print('Do OtherCommand 1')
        return True

    def Undo(self):
        print('Undo OtherCommand 1')
        return True

class OtherCommand2(wx.Command):
    def Do(self):
        print('Do OtherCommand 2')
        return True

    def Undo(self):
        print('Undo OtherCommand 2')
        return True

class MyCommand(wx.Command):
    def __init__(self, queue):
        wx.Command.__init__(self, True)
        self.queue = queue
        self.internal = wx.CommandProcessor()

    def Do(self):
        self.internal.Submit(OtherCommand1(True))
        self.internal.Submit(OtherCommand2(True))
        return True

    def Undo(self):
        self.internal.Undo()
        self.internal.Undo()
        return True

queue = wx.CommandProcessor()
queue.Submit(MyCommand(queue))
print(queue.Commands)
queue.Undo()

``

Again, this works (and works well), but wondering if there’s better / more standard ways :slight_smile:

···

On Saturday, July 21, 2018 at 10:19:39 PM UTC-4, Ryan Holmes wrote:

I’m currently developing out undo/redo functionality for my application after 8 years or it being conspicuously missing >.< I’m utilizing the build in classes that wx provides to develop out a command pattern foe the various actions that the application has. I have come across a question though:

Is there a way to group commands together to execute / undo together? For example, I have a command that should execute other commands (lets say two). But that should be considered “one” command, instead of two. Consequently, when the user undos this action, it should actually undo the two “inner” commands.

I’ve developed a small demonstration: I have one command (MyCommand) that, as part of it’s execution, calls two other commands. However, I am forced to Undo both explicitly. One note- I return False for the MyCommand.Do() since I don’t want it on the stack (because then it will force me to do three undos to get back)

import wx


class OtherCommand1(wx.Command):
    def Do(self):
        print('Do OtherCommand 1')
        return True

    def Undo(self):
        print('Undo OtherCommand 1')
        return True


class OtherCommand2(wx.Command):
    def Do(self):
        print('Do OtherCommand 2')
        return True

    def Undo(self):
        print('Undo OtherCommand 2')
        return True


class MyCommand(wx.Command):
    def __init__(self, queue):
        wx.Command.__init__(self, True)
        self.queue = queue

    def Do(self):
        self.queue.Submit(OtherCommand1(True))
        self.queue.Submit(OtherCommand2(True))
        return False

    def Undo(self):
        return True


queue = wx.CommandProcessor()
queue.Submit(MyCommand(queue))
print(queue.Commands)
queue.Undo()
queue.Undo()

``

Output:

Do OtherCommand 1
Do OtherCommand 2
CommandList: [<main.OtherCommand1 object at 0x04602AD0>, <main.OtherCommand2 object at 0x04602B20>]
Undo OtherCommand 2
Undo OtherCommand 1

``

So I’m wondering if there’s a good method to package multiple commands up as one, as it doesn’t look like the API provides a way to do this itself. One way I have thought of is to have MyCommand keep it’s own internal history that it submits the inner commands to, and then on it’s Undo(), loop through and Undo the inner ones. Another method would be to possibly override the CommandProcessor class to extend it’s functionality in a way that allows for this. I’m sure these would be fine ways of doing it, but wanted to gather feedback and thoughts on other ways