http://pythoncard.sourceforge.net/framework/events_and_handlers.html
Overview and Requirements
- event handlers are identical to wxPython event handlers
- def on_btn_mouseClick(self, event):
- events delivered to user code event handlers are wxPython events
all PythonCard event handlers use the same naming conventions
- on_componentName_eventName or on_eventName for a handler applicable to all objects
- command events override mouseClick or select for a given component
some components don't have wxCommandEvent type events so they probably should be able to have commands unless we are going to substitute command for an event like mouseUp for those components (e.g. Image, BitmapCanvas, HtmlWindow, etc.)
- events have additional convenience attributes added by the dispatch system
event.target is the primary one which is a convenience over calling event.GetEventObject()
timer events don't have GetEventObject() <- wxWidgets flaw?
- additional attributes (most are event type specific):
- interval
- keyCode, altDown, controlDown, shiftDown
- position, x, y
see DefaultEventBinding._dispatch in event.py for a full list
- all components have gainFocus, loseFocus, timer, and mouse events bound
- the dispatch system creates some additional event types that don't exist in wxWidgets during dispatch as a convenience and to make user code more clear. this is done by checking the event in parens and dispatching to a different handler in user code based on some flag that is available from wxPython
- mouseDrag (mouseMove) Dragging() == True
closeField (loseFocus) requires a call to DiscardEdits on gainFocus
- deactivate (activate)
- even if the Message Watcher isn't used, the dispatcher is still needed if the convenience attributes are going to be added. if there was a centralized wxPython event dispatcher that acted as a bottleneck then maybe we could subclass that and add the convenience attributes there?
we might be able to do that if we were using wx.lib.evtmgr.py EventManager
- the Message Watcher adds itself as a listener to the dispatcher if it is available as a runtime tool
- if the notifyEventListeners strategy isn't used anymore then the dispatcher might need to send a message to the Message Watcher
- a centralized dispatcher (bottleneck) could handle this more easily
- the original system was intended to be very dynamic and allow components and event handlers to be added and removed at runtime
- in the current system all events applicable to a component are bound even if those events are not used in user code
- that is probably not relevant for 1.0 and is YAGNI (you aren't gonna need it). in the situations where you would need that an event system subclass could probably be used
- we would lose the ability to see unused events in the Message Watcher and you would need to run an app that had event handlers for all events to verify event order for all platforms. that is one use of the testevents sample, but there would probably need to be variations for specific components
- a table of handlers is built for a Scriptable subclass when it is created
- the table is used to locate an event for a specific control event handler (e.g. on_btn_mouseClick) or one for all controls (e.g. on_mouseClick)
Event Subsystem Implementation
Components are responsible for defining their own events. For example the Button component defines the ButtonMouseClickEvent in the PythonCard.components.button module. Move all of the Event subclasses from event.py into their respective component modules.
Define wxWidgets binding information in the Event subclasses, and get rid of event.EventMap. This allows the Widget superclass to use the information contained in the Event subclass to bind the event to a Widget. Even better, define a subclass of Event, WxEvent. Define a class method, WxEvent.bind that uses the wx specific class variables to bind the event to the wxPython subsystem. Then the code in Widget.init() simply iterates over the event classes listed in a Widget's spec and calls eventClass.bind( self._dispatch ).
Define widget specific Event subclasses in those cases where we're reusing the same event for multiple widgets.
Define the default events for Widgets, like MouseClick, MouseUp, etc. in the widget module. Just as is done currently, add these events to each WidgetSpec's list of events. They will be bound by the same code that binds component-specific events.
More?
- the bind and dispatch process could be made dramatically more efficient by maintaining a dictionary mapping event types (either ids or names) to the actual method used in the user code
- the dictionary would be built during the bind phase so that the findHandler operation that is currently done during dispatch would be done at bind time instead
- note to self, resourceEditor might have to be revised to deal with components as they are added and removed
- then during dispatch, all we have to do is lookup the event handler in the dictionary and call the handler
- some events such as gainFocus, loseFocus (closeField), mouseMove (mouseDrag), activate (deactivate) will require additional processing before dispatch