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.