Eventually wxPython determines whether the event process should propagate up the container hierarchy to find an event handler. The container hierarchy is the path from a specific widget to the top-level frame, moving from each widget to its parent container, and upward.
If the current object doesn't have a handler for the event, or if the handler called Skip(), wxPython determines if the event should propogate up the container hierarchy. If the answer is No, the process looks once more for a handler, in the wx.App instance, and then stops. If the answer is Yes, the event process starts over using the container of the window currently being searched. The process continues upward until wxPython either finds an appropriate binding, reaches a
top-level frame object with no parent, or reaches a wx.Dialog object (even if the dialog is not top-level). The event is considered to have found an appropriate binding if ProcessEvent() for that object returns True, indicating that processing is complete. The rationale for stopping at a wx.Dialog is to prevent the parent frame from being hit by spurious events coming from dialog boxes that are unrelated and unexpected.
Whether an event should propagate up the container hierarchy is a dynamic property of each event instance, although in practice the default values are almost always the ones used. By default, only instances of wx.CommandEvent, or any subclass thereof, propagate up the container hierarchy. All other events do not.
In listing 3.3, this is where the button click gets handled. Clicking the mouse on the wx.Button generates a wx.EVT_BUTTON type of command event. Since the wx.EVT_ BUTTON is a wx.CommandEvent, after wxPython fails to find a binding in the button object, it looks to the parent, which in this case is the panel. Since there is no matching binding in the panel, the panel's parent, the frame, is checked next. Since the frame does have a matching binding, ProcessEvent() calls the appropriate function, in this case OnButtonClick().
Step 5 also explains why the mouse enter and mouse leave events need to be bound to the button and not to the frame. Since mouse events are not a subclass of wx.CommandEvent, the mouse enter and mouse leave events do not propagate upward to the parent, thus wxPython cannot find a binding from the button's mouse enter event to the frame. If there is a mouse enter or leave event bound to the frame, the event is triggered by wxPython when the mouse enters or leaves the frame as a whole.
Command events are privileged in this way because they are intended to be higher level events indicating that the user is doing something in the application space, rather than in the window system. The assumption is that window system type events are only of interest to the widget that initially receives them, while application-level events may be of interest higher up in the containment hierarchy. This rule does not prevent us from declaring the binding an event anywhere, no matter what object is being bound or what object defines the event handler. For example, even though the mouse click event binding is to the button object, the binding itself is defined inside the frame class, and calls a method of the frame class. In other words, low-level non-command events are typically used for things that happen to the widget or for some system level notification, such as a mouse click, key press, paint request, size, or move. On the other hand, command events, such as a mouse click on a button or a list box selection, are typically generated and emitted by the widget itself. For example, button command events are generated after a mouse down and mouse up event on the appropriate widget.
Finally, if the event is not handled after walking through the containment hierarchy, ProcessEvent() is called on the wx.App object for the application. By default, this does nothing, however, you can add event bindings to your wx.App to route events in some non-standard way. For example, if you were writing a GUI builder, you may want events in your builder window to propagate to your code window, even though they are both top-level windows. One way of doing that is to capture the events in the application object and pass them on to the code window.
Was this article helpful?