The way to hack event handlers

Hi, there :wave:

This post is an answer to the old post (10 years ago, no phoenix time) python 2.7 - How Do I Get A Bound Function Object from a wxPython Widget? - Stack Overflow.

You can use the following method to record all events bound to all widgets in your application.

How-to

In wx.core module, there are two thin wrapper functions of EvtHandler for Bind and Unbind.
To override these, place the following code in your code somewhere.

from wx import core

def _EvtHandler_Bind(self, event, handler=None, source=None, id=wx.ID_ANY, id2=wx.ID_ANY):
    """
    Bind an event to an event handler.
    (override) to return handler
    """
    assert isinstance(event, wx.PyEventBinder)
    assert source is None or hasattr(source, 'GetId')
    if handler is None:
        return lambda f: _EvtHandler_Bind(self, event, f, source, id, id2)
    if source is not None:
        id  = source.GetId()
    event.Bind(self, id, id2, handler)
    ## record all handlers: single state machine
    if not hasattr(self, '__deb__handler__'):
        self.__deb__handler__ = {}
    if event.typeId in self.__deb__handler__:
        self.__deb__handler__[event.typeId] += [handler]
    else:
        self.__deb__handler__[event.typeId] = [handler]
    return handler
core.EvtHandler.Bind = _EvtHandler_Bind

def _EvtHandler_Unbind(self, event, source=None, id=wx.ID_ANY, id2=wx.ID_ANY, handler=None):
    """
    Disconnects the event handler binding for event from `self`.
    Returns ``True`` if successful.
    (override) to remove handler
    """
    if source is not None:
        id  = source.GetId()
    ## remove the specified handler or all handlers
    if handler is None:
        self.__deb__handler__[event.typeId].clear()
    else:
        self.__deb__handler__[event.typeId].remove(handler)
    return event.Unbind(self, id, id2, handler)
core.EvtHandler.Unbind = _EvtHandler_Unbind

Now, you can get a list of the typeId of bound event and their handlers for any given widgets.
For example, an instance of wx.lib.platebtn.PlateButton in my program dumped the list as follows,

>>> from pprint import pprint
>>> pprint(self.btn.__deb__handler__)
{10012: [<function Plugin.Init.<locals>.update at 0x000001944C0DD5E0>],
 10030: [<bound method PlateButton.OnLeftDown of <mwx.controls.Button object at 0x000001944C0DD8B0>>],
 10031: [<bound method PlateButton.OnLeftUp of <mwx.controls.Button object at 0x000001944C0DD8B0>>],
 10037: [<function PlateButton.__init__.<locals>.<lambda> at 0x000001944C0DDC10>],
 10038: [<function PlateButton.__init__.<locals>.<lambda> at 0x000001944C0DDCA0>],
 10039: [<function PlateButton.__init__.<locals>.<lambda> at 0x000001944C0DDB80>],
 10042: [<bound method PlateButton.OnFocus of <mwx.controls.Button object at 0x000001944C0DD8B0>>],
 10043: [<bound method PlateButton.OnKillFocus of <mwx.controls.Button object at 0x000001944C0DD8B0>>],
 10058: [<bound method Plat/2eButton.OnKeyUp of <mwx.controls.Button object at 0x000001944C0DD8B0>>],
 10103: [<function PlateButton.__init__.<locals>.<lambda> at 0x000001944C0DDA60>],
 10104: [<bound method PlateButton.OnErase of <mwx.controls.Button object at 0x000001944C0DD8B0>>],
 10109: [<function PlateButton.__init__.<locals>.<lambda> at 0x000001944C0DDD30>]}

The keys are event.typeId, which is well explained here (Investigating mouse events - #2 by ricpol) and the value is a list of actions.
This could be used for debugging tool, or improving event watcher. I think there must be very little overhead. But if you try this and find something wrong, please give me your feedback.

Tiny tips

Sometimes we don’t want handlers put in public. In that case, you would do like this:

    def echo(v):
        v.Skip()
    self.btn.Bind(wx.EVT_MOTION, echo)
    self.btn.Bind(wx.EVT_BUTTON, echo)

With this hack, you can also type nicely as follows:

    @self.btn.Bind(wx.EVT_MOTION)
    @self.btn.Bind(wx.EVT_BUTTON)
    def echo(v):
        v.Skip()
2 Likes