How do I bind to an event regardless of source widget ?

From: Josiah Carlson
Sent: 25 May 2007 17:48>

You may find the updated event creation and processing mechanism to be
easier to write and understand:
    CustomEventClasses - wxPyWiki

Thanks for the timely response. I hadn't stumbled across CustomEventClasses.

For your particular issue, I would create a 'CommandEvent', then bind
your event handler to Top.

Alternatively, you could use wx.lib.pubsub and bypass the custom event
hierarchy entirely.

I'm considering pubsub, or dispatcher, or even the whole docview framework since that's what my app is starting to look like.

I ended up skim reading the wxWidgets docs (which I probably should have done first) on event handling,
and binding the child to the Top's event handler by doing:

wx.GetApp().GetTopWindow().Connect(wx.ID_ANY, wx.ID_ANY, EVT_TYPE_FOO, self.OnFoo)

or turned into an external function:
def _bindToSourcelessEvent(typeEvent, handlerBind, evthandlerBind=None):
    """Bind the specified function to the specified event
        regardless of source
        Parameters:
            typeEvent - Type of event to bind
            handlerBind - Callable handler to bind to
            evthandlerBind - Event handler which will receive event
    """
    if evthandlerBind is None: #If no event handler specified
        #Use top level window
        evthandlerBind = wx.GetApp().GetTopWindow()
    evthandlerBind.Connect(wx.ID_ANY, wx.ID_ANY, typeEvent, handlerBind)

#Lets me do something like
class WinFoo(wx.Frame):
    def __init__(self, *a, **kw):
        wx.Frame.__init__(self, *a, **kw)
        _bindToSourcelessEvent(EVT_TYPE_FOO, self.OnEventFoo)

As I understand it this approach relies on the Top window seeing any custom event instance pass through its ProcessEvent, which in practice means that any window which wants to raise an event like the following:

def doSomething(self):
    evtTrigger = events.EventFoo(EVT_TYPE_FOO, 0)
    self.GetEventHandler().ProcessEvent(evtTrigger)

needs to be a child of the Top (so that "GetEventHandler" is looking up the correct hierarchy). As it stands that is not much of a restriction for me at the moment. It seems to do what I want which is allow one window to raise an event without caring about who's interested in it, and another to register an interest in that event via _bindToSourcelessEvent() without worrying about where it came from.

However, it smells like a hack on my part based on incomplete knowledge, so I'll try to make time to read the links you've kindly provided.

···

______________________________________________________________________

This email is intended only for the use of the individual(s) to whom it is addressed and may be privileged and confidential.
Unauthorised use or disclosure is prohibited.If you receive This e-mail in error, please advise immediately and delete the original message.
This message may have been altered without your or our knowledge and the sender does not accept any liability for any errors or omissions in the message.

English, Mark wrote:

From: Josiah Carlson
Sent: 25 May 2007 17:48>

You may find the updated event creation and processing mechanism to be
easier to write and understand:
    CustomEventClasses - wxPyWiki

Thanks for the timely response. I hadn't stumbled across CustomEventClasses.

For your particular issue, I would create a 'CommandEvent', then bind
your event handler to Top.

Alternatively, you could use wx.lib.pubsub and bypass the custom event
hierarchy entirely.

I'm considering pubsub, or dispatcher, or even the whole docview framework since that's what my app is starting to look like.

I would also suggest pubsub. You can send a message from any place in your program, then all matching subscribers will get it and the sender doesn't even need to know if there are subscribers or not.

I ended up skim reading the wxWidgets docs (which I probably should have done first) on event handling,
and binding the child to the Top's event handler by doing:

wx.GetApp().GetTopWindow().Connect(wx.ID_ANY, wx.ID_ANY, EVT_TYPE_FOO, self.OnFoo)

This can be simplified by using

  wx.GetApp().GetTopWindow().Bind(EVT_TYPE_FOO, self.OnFoo)

You can also Bind directly to the app object, since all unhandled (or Skipped) events will eventually make it to the app object.

or turned into an external function:
def _bindToSourcelessEvent(typeEvent, handlerBind, evthandlerBind=None):
    """Bind the specified function to the specified event
        regardless of source
        Parameters:
            typeEvent - Type of event to bind
            handlerBind - Callable handler to bind to
            evthandlerBind - Event handler which will receive event
    """
    if evthandlerBind is None: #If no event handler specified
        #Use top level window
        evthandlerBind = wx.GetApp().GetTopWindow()
    evthandlerBind.Connect(wx.ID_ANY, wx.ID_ANY, typeEvent, handlerBind)

#Lets me do something like
class WinFoo(wx.Frame):
    def __init__(self, *a, **kw):
        wx.Frame.__init__(self, *a, **kw)
        _bindToSourcelessEvent(EVT_TYPE_FOO, self.OnEventFoo)

As I understand it this approach relies on the Top window seeing any custom event instance pass through its ProcessEvent, which in practice means that any window which wants to raise an event like the following:

def doSomething(self):
    evtTrigger = events.EventFoo(EVT_TYPE_FOO, 0)
    self.GetEventHandler().ProcessEvent(evtTrigger)

needs to be a child of the Top (so that "GetEventHandler" is looking up the correct hierarchy). As it stands that is not much of a restriction for me at the moment. It seems to do what I want which is allow one window to raise an event without caring about who's interested in it, and another to register an interest in that event via _bindToSourcelessEvent() without worrying about where it came from.

However, it smells like a hack on my part based on incomplete knowledge, so I'll try to make time to read the links you've kindly provided.

Yes, your code and assumptions are correct, including the limitation that the sender will need to be a child of the receiver. Once you need to allow recievers to be siblings or cousins of the sender then you'll either have to have more knowledge of your app's structure spread throughout the app, or use something like pubsub instead so the senders and receivers don't need to have a parent/child relationship, and in fact they don't even have to be UI objects at all. It's pretty simple to use. In your example you can do it something like this:

     from wx.lib.pubsub import Publisher

The sender code can look like this:

         Publisher().sendMessage("Event.FOO", someDataObject)

To subscribe to the "Event.FOO" topic:

         Publisher().subscribe(self.OnEventFoo, "Event.FOO")

And the listener would look something like this:

     def OnEventFoo(self, data):
  # do something

The topics that are used to bind subscribers to publishers can be simple values or can be hierarchical using the dot notation shown above or as tuples of strings, and a subscriber can listen for a specific message or a general classification of messages simply by specifying more or less of the topic name hierarchy.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!