Search code examples
pythoneventswxpythonwxwidgets

wxpython's wxEvent.Skip() explained


I'm recently learning wxPython via wxPython in Action, but I got stuck in Chapter 3, especially to the explanation of Figure 3.3's Skip() diamond

In Step 3 Locating the binder object, the book writes:

If a binder isn't found for the object itself, the processing walks up the class hierarchy to find a binder defined in a superclass of the object--(1)this is different than the walk up in the container hierarchy that happens in the next step.

But in the next step, Step 4 Determining whether to continue processing writes:

The event handler asks for more processing by calling the wx.Event method Skip(). If the Skip() method is called, processing continues and (2)any handlers defined in the (3)superclass are found and executed in the step. The Skip() method can be called at any point in the handler. The Skip() method sets a flag in the event instance, which wxPython checks after the handler method is complete. In listing 3.3 the OnButtonClick() doesn't call Skip(), so in that case the event process is complete at the end of the handler method. The other two event handlers do call Skip() (4)so the system will keep searching for a matching event binding, (5)eventually invoking the default functionality for mouse enter and leave events for the native widget, such as mouse-over events.

My questions are related to numbers I marked in the passage:

(1) In my understanding and some other googled materials, propagat is something happens in the container hierarchy, NOT the class hierarchy, is this correct?

(2) Seriously? any handlers are executed? not the ones matching the instance and event combination?

(3) Is superclass here correct? shouldn't it be parent windows?

(4) I think this line contradicts with question2, so maybe not any handlers are executed, just the ones which match the instance and event combination?

(5) What is a default functionality? and how is it invoked?

If anyone is intersted, listing-3.3.py is here:

import wx


class MouseEventFrame(wx.Frame):

    def __init__(self, parent, id):
        wx.Frame.__init__(self, parent, id, 'Mouse Events',
                          size=(300, 200))
        self.panel = wx.Panel(self)
        self.button = wx.Button(self.panel,
                                label='Not Over', pos=(100, 15))
        self.Bind(wx.EVT_BUTTON, self.OnButtonClick, self.button)
        self.button.Bind(wx.EVT_ENTER_WINDOW, self.OnEnterWindow)
        self.button.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow)

    def OnButtonClick(self, event):
        self.panel.SetBackgroundColour('Green')
        self.panel.Refresh()

    def OnEnterWindow(self, event):
        self.button.SetLabel('Over Me!')
        event.Skip()

    def OnLeaveWindow(self, event):
        self.button.SetLabel('Not Over')
        event.Skip()


if __name__ == '__main__':
    app = wx.App()
    frame = MouseEventFrame(parent=None, id=-1)
    frame.Show()
    app.MainLoop()

Solution

  • When an event associated with some object is generated, the order of the handler lookup is:

    1. wxApp::FilterEvent()
    2. Any event handlers pushed on the object itself. I am not even sure if this is exposed in wxPython, and in any case this shouldn't be used any more.
    3. The object itself, starting from the handlers registered in the most derived classes and continuing with the handlers defined in the base classes.
    4. If the event is "propagatable" (which is the case by default for wxCommandEvent but not, say, wxMouseEvent) and if the object is a window, then all the parent windows of the object, recursively.
    5. wxTheApp itself.
    6. And finally, the default handler for the event at the underlying toolkit level is used. What, if anything, it does, depends on the exact event kind and the platform but for some events it's very important to let it be called, notably for the focus events you must let the default native control handlers get them in order to update their focused state.

    Now, what about Skip()? Normally, the first handler found while searching is used and the event processing stops there. However if the handler calls event.Skip() during its execution, this event handler is skipped and the search continues. I.e. the important thing to understand is that Skip() doesn't skip the event, even though it's called on the event object. Instead, it skips the event handler calling it.

    Hopefully you understand now why is it important to call event.Skip() in wxFocusEvent handlers.