Unbind all events

As far as I can see in the documentation there is a method to unbind an event given the event code however I can’t see any way to unbind every event without knowing the event codes.

My use for this is that we have a plugin system and the plugins are given access to a canvas to bind events and interact with. When we unload a plugin to load a new one we want to reset the canvas to its default state so that old events do not hang around.

My current workaround is to overwrite the Bind event to store all the events that get bound and add a new method that unbinds each of them. This seems like the kind of thing there should be a native way to achieve it but I can’t see one.

I really don’t know if there is a canonical way to “unbind all” from a widget… I can think of at least one weird trick to pull this out, but it is probably a bit too clever for its own sake.
However, your problem seems to me a design issue in the first place. I would argue that it should be the plugin’s job to clean up after itself. You should require some “unbind_all” routine on the plugin side, that should be called during the plugin’s disconnection process. After all, it was the plugin who binded the events in the beginning: letting someone else unbind them in the end, strikes me as bad OOP.

That said, here is how I would “unbind all” on the side of the main program. I guess the idea requires some explanation. When you “plug” the plugin, I suppose you have to pass a reference of the widget (a canvas, in your case) for it to play with (binding events and so on). Now, instead of letting your pluging play with the actual widget’s event handler, you could push a new handler on top of the stack, and pass this one instead. This way, when you “unplug” the plugin, all you have to do is to pop the event handler to clear any binding it contains. Immediately afterwards, you push a new, blank, event handler on the widget, ready to be used by another plugin.
This is a minimal example:

class MainFrame(wx.Frame):
    def __init__(self, *a, **k):
        wx.Frame.__init__(self, *a, **k)
        p = wx.Panel(self)
        self.b1 = wx.Button(p, -1, 'add plugin', pos=(10, 10))
        self.b2 = wx.Button(p, -1, 'remove plugin', pos=(10, 50))
        self.b3 = wx.Button(p, -1, 'plugin playground!', pos=(10, 90))
        self.b1.Bind(wx.EVT_BUTTON, self.add)
        self.b2.Bind(wx.EVT_BUTTON, self.remove)
        self.plugin = None
        self.handler = wx.EvtHandler()
        self.b3.PushEventHandler(self.handler) # now we are ready to begin

        self.Bind(wx.EVT_CLOSE, self.on_close)

    def add(self, e):
        if not self.plugin:
            # we pass our custom handler, not the widget's original one, to the plugin
            self.plugin = Plugin(self.handler)

    def remove(self, e):
        self.plugin = None
        # we destroy the handler, thus clearing any binding made by the plugin
        # now we push a new handler and we are ready to start over again
        self.handler = wx.EvtHandler()

    def on_close(self, e):

class Plugin:
    def __init__(self, handler):
        handler.Bind(wx.EVT_BUTTON, self.plugin_clic)

    def plugin_clic(self, e):
        print('this is a clic handled by a plugin')

app = wx.App()

Now, I really don’t know if any of this is sound enough to make it to production stage, but you can give it a try…

Ideally yes each plugin would unbind its own events however we are letting any user create their own plugins and I do not want to rely on each plugin creator remembering to unbind all of their events hence why I am doing it for them. Only one plugin will be active at any given time so unbinding all events and re-binding the default events seems like the best solution.

Your solution wouldn’t allow the plugin to have direct access to the canvas to call methods and attributes of the canvas. I think it would also only have access to that button.

Our current solution needs some more work but it boils down to overriding the Bind and Unbind methods to store each of the events, sources and handlers that get bound. Another method is implemented that uses this data to unbind those events. The actual Canvas object is then given directly to the plugin to interact with.

Bind and Unbind:


I do not want to rely on each plugin creator remembering to unbind

Yes, an API is how they call it. At some point, you just can’t let any random plugin play with your code without setting a few ground rules. And yes, you can’t really enforce an API, expecially in Python - but for all you know, a plugin could just wipe out the user’s hard disk.

Or, you could implement a centralized binding system, where a plugin can just ask you to bind an event, but never actually do the binding itself:

class MainFrame(wx.Frame):
    # ...
    def please_bind_me_this(self, evt, callback):
        self.b3.Bind(evt, callback)
        # here you can keep track of what events were binded and so on...

class Plugin:
    def __init__(self, main_program):
        main_program.please_bind_me_this(wx.EVT_BUTTON, self.plugin_clic)

    def plugin_clic(self, e):
        print('this is a clic handled by a plugin')

This also is an API, of course. Basically, if your plugin has direct access to the original widget, you have no way to prevent it from manually binding events to it, and messing around in a dozen different ways.
Of course this is dangerous, but if you don’t mind the danger (and you decided that you don’t), then there is nothing wrong extablishing an API and asking the plugins to conform to it. If they don’t, you don’t have to come up with a plan to fix their mistakes. Yes, there is a decent chance that your program will crash - but then again, if a plugin has direct access to the internals, it can crash your program in so many ways…

Your current soulution is another way of implementing a centralized mechanism, and I think it’s fine… as long as you realize that in any case you are giving the plugin access to your program internals.

Your solution wouldn’t allow the plugin to have direct access to the canvas

Of course it would - indeed, one could say that this is the problem, actually. In all these scenarios, there’s no way to keep your plugin from accessing the widget. From inside a callback you just have to call GetEventObject on the event, to reach your canvas. And when you don’t have an event instance at hand, you may call wx.EvtHandler.GetPreviousHandler to navigate back the stack of the handlers.
But ultimately, you don’t have to. You can simply pass also the canvas instance to the plugin, in addition to the recommended handler for the binding operations. (And please note that I said “recommendend”… because of course, since the plugin has direct access to the original widget anyways, there is nothing to stop it to bind directly to the widget).

Only one plugin will be active at any given time

Yes, this is for the better. If several plugins can listen to the same event for the same widget at the same time, then you have to decide in what order they should intervene, and deal with a few other annoyances. Basically, the event system in wxPython is not well-tailored for such multi-cast signal transmission. You would better off with Pub/Sub.