== Introduction == This is a short overview of how event propagation works in wxPython, demonstrated on a small example. == What Objects are Involved == * wx.Event * wx.Command``Event == Process Overview == * An event is raised. * It propagates around. * As soon as something handles it, it ''stops'' propagating. (It's done.) * But if the handler calls "Skip" on the event, it keeps on going. Technically, what happens is this: * An event handler is raised, and posted to a {{{wxEventHandler}}}, such as a {{{wxWindow}}}. * The {{{wxEventHandler}}} goes through a series of steps, (in {{{ProcessEvent}}},) looking for something to handle the event. * When a qualified handler (matching the Event Type, with a valid matching ID) is found, it gets to handle the event. * Unless the handler called ".Skip()" on the event, event propagation ends. * If something ''did'' call Skip, event propagation continues. What are the steps that the {{{wxEventHandler}}} goes through? * If the handler is disabled, it sends the event to {{{wxApp::ProcessEvent,}}} and ''stops.'' * If the handler is attached to a {{{wxWindow}}}, and the window has a validator attached, then the validator gets a crack at the event. * Then the handler's event table is searched. * Then the base classes event handler's event table is searched. (And so on.) * If it's a {{{wxWindow}}} that has had additional {{{wxEventHandler}}} instances attached to it with {{{PushEventHandler}}}, then ''those'' get their turn. * If the event {{{ShouldPropagate}}}, (which is true of, by default, only {{{wxCommandEvent}}}s, such as button clicks, then the event is propagated to parent windows. (Within limits, described below.) * Finally, {{{wxApp::ProcessEvent}}} gets a crack at the event. This is fairly complicated; If you are not using validators, and you are not using subclasses, and if you are not pushing special purpose event handlers, or any other of these weird things, then simply consider: * The event goes to the window. * If the event isn't handled, it goes to the parent window. * Finally, {{{wxApp::ProcessEvent}}} has a go at it. * The first handler that gets the event will stop the event. * Unless the handler calls ".Skip()" on the event. == Some Things about Window Hierarchy Propagation == '''Events do not propagate beyond dialogs.''' They ''do'' propagate beyond Frames. This is because there are so many dialogs, but Frames tend to be a bit more "controlled." You can {{{SetExtraStyle(wx.WS_EX_BLOCK_EVENTS)}}} to make another window block events, as if it were a dialog. '''Propagation Level.''' In wxPython Events will propagate up the window hierarchy if they have a propagation level >0. If that is the case, then the {{{ShouldPropagate}}} method of the wx.Event returns True. If you want to get the numerical propagation level of the event, use the Stop``Propagation method. It returns the propagation level. (Don't forget to call Resume``Propagation afterwards). The propagation level determines how far the event will propagate. Suppose the propagation level is 1. Then the event will only propagate to a window's parent. If it has a level of 2, it will propagate to the window's parent and to its grandparent. If it has a level of sys.maxint, it will propagate up the whole window hierarchy. (Stopping at Dialog boundaries, but not Frame boundaries.) By default, only {{{wx.CommandEvents}}} will propagate. A typical example of an event not propagated is the wx.EVT_KEY_DOWN. It is send only to the control having the focus, and will not propagate to its parent. To see what is happening have a look at the following example and play with it. Just set some different propagation levels in the On``Key``Text method and see what happens. == Code Sample == {{{ #!python import wx class MainFrame(wx.Frame): def __init__(self, parent, ID, title): wx.Frame.__init__(self, parent, ID, title, wx.DefaultPosition, wx.Size(200, 100)) Panel = wx.Panel(self, -1) TopSizer = wx.BoxSizer(wx.VERTICAL) Panel.SetSizer(TopSizer) Text = wx.TextCtrl(Panel, -1, "Type text here") TopSizer.Add(Text, 1, wx.EXPAND) Text.Bind(wx.EVT_KEY_DOWN, self.OnKeyText) Panel.Bind(wx.EVT_KEY_DOWN, self.OnKeyPanel) self.Bind(wx.EVT_KEY_DOWN, self.OnKeyFrame) def OnKeyText(self, event): print "OnKeyText" print "\tShould Propagate %i" % event.ShouldPropagate() Level = event.StopPropagation() print "\tPropagate level %i" % Level # Try: event.ResumePropagation(x), x=1,2,3,... event.ResumePropagation(Level) event.Skip() def OnKeyPanel(self, event): print "OnKeyPanel" print "\tShould Propagate %i" % event.ShouldPropagate() Level = event.StopPropagation() print "\tPropagate level %i" % Level event.ResumePropagation(Level) event.Skip() def OnKeyFrame(self, event): print "OnKeyFrame" print "\tShould Propagate %i" % event.ShouldPropagate() Level = event.StopPropagation() print "\tPropagate level %i" % Level event.ResumePropagation(Level) event.Skip() class MyApp(wx.App): def OnInit(self): Frame = MainFrame(None, -1, "Event Propagation Demo") Frame.Show(True) self.SetTopWindow(Frame) return True if __name__ == '__main__': App = MyApp(0) App.MainLoop() }}} == See Also == * [[http://www.wxpython.org/docs/api/wx.EvtHandler-class.html|wxPyDocs on wx.EvtHandler]] * [[http://wiki.wxwidgets.org/docbrowse.cgi/wxwin_eventhandlingoverview.html#eventhandlingoverview|wxWidgets Event Handling Overview]] * LionsGiantNotes -- a giant diagram that loosely describes a lot of this == Comments == Any comments and/or corrections welcome. Feel free to edit this page, or send comments to rothguido@gmx.de.