From: Kevin A. <ka...@us...> - 2004-05-09 18:51:07
|
Update of /cvsroot/pythoncard/PythonCard In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv8989 Modified Files: event.py model.py Log Message: moved background events from event.py to model.py except IdleEvent fiuxed InsteadOfTypeEvent classes updated Background to use _bindEvents and _dispatch from Widget class added OnClose event handler so that the activate event could be unbound when the background is closed to prevent an exception being thrown when _dispatch tries use the handler of a deleted background added comments about Scriptable and bound/unbound method issues Index: model.py =================================================================== RCS file: /cvsroot/pythoncard/PythonCard/model.py,v retrieving revision 1.166 retrieving revision 1.167 diff -C2 -d -r1.166 -r1.167 *** model.py 6 May 2004 17:33:13 -0000 1.166 --- model.py 9 May 2004 18:50:58 -0000 1.167 *************** *** 182,185 **** --- 182,264 ---- self.SetEventType(wxEVT_LATENT_BACKGROUNDBIND) + # background events + + class ActivateEvent(event.Event, event.InsteadOfTypeEvent): + name = 'activate' + binding = wx.EVT_ACTIVATE + id = wx.wxEVT_ACTIVATE + + def translateEventType(self, aWxEvent): + if aWxEvent.GetActive(): + return self.id + else: + return DeactivateEvent.id + + class DeactivateEvent(ActivateEvent): + name = 'deactivate' + id = wx.NewEventType() + + class CloseEvent(event.Event): + name = 'close' + binding = wx.EVT_CLOSE + id = wx.wxEVT_CLOSE_WINDOW + + class MaximizeEvent(event.Event): + name = 'maximize' + binding = wx.EVT_MAXIMIZE + id = wx.wxEVT_MAXIMIZE + + def decorate(self, aWxEvent, source): + aWxEvent = event.Event.decorate(self, aWxEvent, source) + aWxEvent.maximized = aWxEvent.target.IsMaximized() + return aWxEvent + + class MinimizeEvent(event.Event, event.InsteadOfTypeEvent): + name = 'minimize' + binding = wx.EVT_ICONIZE + id = wx.wxEVT_ICONIZE + + def translateEventType(self, aWxEvent): + if aWxEvent.Iconized(): + return self.id + else: + return RestoreEvent.id + + class RestoreEvent(MinimizeEvent): + name = 'restore' + id = wx.NewEventType() + + class MoveEvent(event.Event): + name = 'move' + binding = wx.EVT_MOVE + id = wx.wxEVT_MOVE + + def decorate(self, aWxEvent, source): + aWxEvent = event.Event.decorate(self, aWxEvent, source) + aWxEvent.position = tuple(aWxEvent.GetPosition()) + return aWxEvent + + class SizeEvent(event.Event): + name = 'size' + binding = wx.EVT_SIZE + id = wx.wxEVT_SIZE + + def decorate(self, aWxEvent, source): + aWxEvent = event.Event.decorate(self, aWxEvent, source) + aWxEvent.size = tuple(aWxEvent.GetSize()) + return aWxEvent + + BackgroundEvents = ( + ActivateEvent, + CloseEvent, + DeactivateEvent, + event.IdleEvent, + MaximizeEvent, + MinimizeEvent, + MoveEvent, + RestoreEvent, + SizeEvent, + ) + class Application(wx.App): *************** *** 395,399 **** --- 474,486 ---- # using the inspect module pythoncardMethods = [] + # KEA 2004-05-09 + # if we use bound methods then we'll have to change the + # initialization order so that Scriptable.__init__ + # is done after the object is actually created + # all _dispatch methods have to be updated to use bound + # methods if this is changed methods = inspect.getmembers(self.__class__, inspect.ismethod) + ## # use bound methods instead of the class unbound methods + ## methods = inspect.getmembers(self, inspect.ismethod) for m in methods: if m[0].split('_')[0] == 'on': *************** *** 533,537 **** self.menuBar = None self.statusBar = None - self._exiting = False # override for application defined position --- 620,623 ---- *************** *** 628,761 **** ! # KEA 2004-04-28 ! # experimental alternative implementation of ! # OnLatentBackgroundBind and _dispatch ! def OnLatentBackgroundBind(self, evt): ! if self.stack.app.mw: bindUnusedEvents = True else: bindUnusedEvents = False - - self.eventIdToHandler = {} ! # this is actually slightly evil because dictionary keys ! # are supposed to be immutable, but you can change classes ! # and the same class imported into a different namespace ! # would be different, so for now I just made this a local variable ! # which is probably okay compared to keeping a reference around in self ! wxRegistry = {event.IdleEvent:(wx.EVT_IDLE, wx.wxEVT_IDLE), ! event.MoveEvent:(wx.EVT_MOVE, wx.wxEVT_MOVE), ! event.SizeEvent:(wx.EVT_SIZE, wx.wxEVT_SIZE), ! event.MinimizeEvent:(wx.EVT_ICONIZE, wx.wxEVT_ICONIZE), ! event.MaximizeEvent:(wx.EVT_MAXIMIZE, wx.wxEVT_MAXIMIZE), ! event.ActivateEvent:(wx.EVT_ACTIVATE, wx.wxEVT_ACTIVATE), ! event.CloseEvent:(wx.EVT_CLOSE, wx.wxEVT_CLOSE_WINDOW)} self.wxEventIdMap = {} ! for key in wxRegistry: ! value = wxRegistry[key] ! self.wxEventIdMap[value[1]] = key ! # helper variable to simplify test for whether to bind virtual events ! boundEvents = [] ! ! # once we aren't using a registry this will be a bit easier to ! # decipher compared to having to index into wxRegistry ! for eventClass in wxRegistry: ! handler = self.findHandler('on_' + eventClass.name) if handler or bindUnusedEvents: ! # only bind events that have an event handler ! # in this scenario unused events are never bound ! # which is more efficient, but the Message Watcher needs ! # to be changed ! # alternatively we can bind everything and then in _dispatch ! # if there isn't a match in eventIdToHandler then we know ! # the event isn't used and we can set used to False ! # the complication would be that we probably have to have to ! # always call Skip() which may or may not be a hassle with components ! self.Bind(wxRegistry[eventClass][0], self._dispatch) ! boundEvents.append(eventClass) if handler: if 0: ! print "binding", eventClass.name, handler.__name__, wxRegistry[eventClass][1] ! self.eventIdToHandler[wxRegistry[eventClass][1]] = handler ! # deactivate is a special case ! # as far as wxPython is concerned it is an activate event ! # so if we've already bound that to _dispatch we don't want ! # to bind it twice ! # if we have an on_deactivate event handler though we need to record ! # that separately for dispatch in eventIdToHandler ! # we'll have the same issue with other "virtual" events like mouseDrag ! handler = self.findHandler('on_' + event.DeactivateEvent.name) ! if handler or bindUnusedEvents: ! if not event.ActivateEvent in boundEvents: ! self.Bind(wxRegistry[event.ActivateEvent][0], self._dispatch) ! if handler: ! # there is no such thing as a deactivate event id, so just use the name ! self.eventIdToHandler[event.DeactivateEvent.name] = handler ! handler = self.findHandler('on_' + event.RestoreEvent.name) ! # now do the same kind of thing for restore ! if handler or bindUnusedEvents: ! if not event.MinimizeEvent in boundEvents: ! self.Bind(wxRegistry[event.MinimizeEvent][0], self._dispatch) ! if handler: ! self.eventIdToHandler[event.RestoreEvent.name] = handler if 0: ! print "\nboundEvents:" ! for evt in boundEvents: ! print " ", evt.name print "\n\n" ! print "\nself.eventIdToHandler:" for id in self.eventIdToHandler: ! print " ", id, self.eventIdToHandler[id]._function print "\n\n" ! # is this cleanup necessary ! # it would be a pain to have to ! # do this everywhere ! handler = None ! boundEvents = None ! def _dispatch(self, aWxEvent): eventType = aWxEvent.GetEventType() ! try: ! # All events should have GetEventObject(). ! aWxEvent.target = aWxEvent.eventObject = aWxEvent.GetEventObject() ! except: ! pass ! eventName = None ! if eventType == wx.wxEVT_MOVE: ! aWxEvent.position = tuple(aWxEvent.GetPosition()) ! elif eventType == wx.wxEVT_SIZE: ! aWxEvent.size = tuple(aWxEvent.GetSize()) ! elif eventType == wx.wxEVT_ICONIZE: ! #aWxEvent.minimized = aWxEvent.Iconized() ! minimized = aWxEvent.Iconized() ! if not minimized: ! eventType = event.RestoreEvent.name ! eventName = event.RestoreEvent.name ! elif eventType == wx.wxEVT_MAXIMIZE: ! aWxEvent.maximized = aWxEvent.target.IsMaximized() ! elif eventType == wx.wxEVT_ACTIVATE and not aWxEvent.GetActive(): ! eventType = event.DeactivateEvent.name ! eventName = event.DeactivateEvent.name ! if not eventName: ! eventName = self.wxEventIdMap[eventType].name ! if self._exiting: ! # Don't display messages when quitting the app ! # the Message Watcher may no longer exist ! # and will cause Python to crash. ! try: ! event.EventLog.getInstance().removeListener(self.stack.app.mw) ! except: ! # already removed Message Watcher or it is not in use ! pass - # it shouldn't be possible to be in _dispatch for an event - # that wasn't bound above, but just in case... handler = self.eventIdToHandler.get(eventType, None) if handler: --- 714,801 ---- ! # KEA 2004-05-09 ! # this is _bindEvents taken from widget.Widget ! # so we can see how moving this to Scriptable will impact binding ! # comments have been removed ! def _bindEvents(self, eventList): ! background = wx.GetTopLevelParent(self) ! if wx.GetApp()._showDebugMenu: bindUnusedEvents = True else: bindUnusedEvents = False ! self.boundEvents = {} ! ! self.eventIdToHandler = {} self.wxEventIdMap = {} ! if 0: ! print "\nBINDING...", self.name ! for eventClass in eventList: ! self.wxEventIdMap[eventClass.id] = eventClass ! if issubclass(eventClass, event.CommandTypeEvent) and self.command: ! handler = background.findHandler('on_' + self.command + '_command') ! if not handler: ! handler = background.findHandler('on_' + self.name + '_' + eventClass.name) ! else: ! handler = background.findHandler('on_' + self.name + '_' + eventClass.name) ! if not handler: ! handler = background.findHandler('on_' + eventClass.name) if handler or bindUnusedEvents: ! if not self.boundEvents.get(eventClass.binding, None): ! self.Bind(eventClass.binding, self._dispatch) ! self.boundEvents[eventClass.binding] = eventClass.name if handler: if 0: ! print " binding", self.name, eventClass.name, handler.__name__, eventClass.id ! self.eventIdToHandler[eventClass.id] = handler if 0: ! print "\n boundEvents:" ! for name in self.boundEvents.values(): ! print " ", name print "\n\n" ! print "\n self.eventIdToHandler:" for id in self.eventIdToHandler: ! print " ", id, self.eventIdToHandler[id] print "\n\n" ! # KEA 2004-05-09 ! # this is _dispatch taken from widget.Widget ! # so we can see how moving this to Scriptable will impact dispatch ! # comments have been removed ! # the only line I added was self.command = None ! # since I'm not sure what we should do about that attribute ! # I suspect that the if test should just be changed so that instead of ! # if self.command ... ! # we use ! # if hasattr(self, 'command') and self.command ... ! # OOPS one other change ! # we're still using unbound methods and after a close event ! # additional events will be sent as the frame is closed and destroyed ! # in particular the last event appears to be a deactivate event ! # to work around this I went ahead and added code def _dispatch(self, aWxEvent): + # this is a temporary workaround, see comment above + self.command = None + eventType = aWxEvent.GetEventType() + eventClass = self.wxEventIdMap[eventType] ! eventClassInstance = eventClass() ! aWxEvent = eventClassInstance.decorate(aWxEvent, self) ! if self.command and isinstance(eventClassInstance, event.CommandTypeEvent): ! eventName = 'command' ! else: ! if isinstance(eventClassInstance, event.InsteadOfTypeEvent): ! eventType = eventClassInstance.translateEventType(aWxEvent) ! eventName = self.wxEventIdMap[eventType].name ! ! eventClass = None ! eventClassInstance = None handler = self.eventIdToHandler.get(eventType, None) if handler: *************** *** 763,786 **** if 0: print "dispatching", handler.__name__ - # make a lowercase alias aWxEvent.skip = aWxEvent.Skip ! # this is what is in event.py ! # aHandler.getFunction()( aOwner, self.getSource(), self ) ! #handler.getFunction()(self, aWxEvent) ! handler(self, aWxEvent) ! # do we have to clean up this alias? aWxEvent.skip = None - # how about this local reference to handler? handler = None ! ! # this check is done here because there is always an on_close handler defined ! # in the Background class ! if eventType == wx.wxEVT_CLOSE_WINDOW and aWxEvent.GetSkipped() and self.GetParent() is None: ! self._exiting = True else: event.EventLog.getInstance().log(eventName, self.name, False) - # hopefully this is all we need to do for "unused events" aWxEvent.Skip() # KEA 2002-06-27 --- 803,823 ---- if 0: print "dispatching", handler.__name__ aWxEvent.skip = aWxEvent.Skip ! ! background = wx.GetTopLevelParent(self) ! ! handler(background, aWxEvent) ! aWxEvent.skip = None handler = None ! background = None else: event.EventLog.getInstance().log(eventName, self.name, False) aWxEvent.Skip() + aWxEvent.target = aWxEvent.eventObject = None + + def OnLatentBackgroundBind(self, evt): + self._bindEvents(BackgroundEvents) # KEA 2002-06-27 *************** *** 997,1003 **** ! def _bindWindowEvents( self ) : wx.EVT_WINDOW_DESTROY(self, self.OnDestroy) # KEA 2002-07-09 # make sure wxSTC text, bitmaps, etc. aren't lost --- 1034,1054 ---- ! def _bindWindowEvents(self): ! wx.EVT_CLOSE(self, self.OnClose) wx.EVT_WINDOW_DESTROY(self, self.OnDestroy) + # KEA 2004-05-09 + # this is necessary so that we don't try and dispatch + # the activate event which is sent after close + # this might not be needed if we start using bound + # events, but that will require more testing + def OnClose(self, evt): + # this will be found when the on_close method above + # or override calls close so that we can still + # disconnect the deactivateevent during close + # otherwise trying to dispatch to + self.Disconnect(-1, -1, ActivateEvent.id) + evt.Skip() + # KEA 2002-07-09 # make sure wxSTC text, bitmaps, etc. aren't lost Index: event.py =================================================================== RCS file: /cvsroot/pythoncard/PythonCard/event.py,v retrieving revision 1.67 retrieving revision 1.68 diff -C2 -d -r1.67 -r1.68 *** event.py 5 May 2004 03:51:53 -0000 1.67 --- event.py 9 May 2004 18:50:58 -0000 1.68 *************** *** 275,296 **** name = 'select' ! ! # background events ! ! class ActivateEvent(Event): ! name = 'activate' ! binding = wx.EVT_ACTIVATE ! id = wx.wxEVT_ACTIVATE ! ! class CloseEvent(Event): ! name = 'close' ! binding = wx.EVT_CLOSE ! id = wx.wxEVT_CLOSE_WINDOW ! ! class DeactivateEvent(Event, InsteadOfTypeEvent): ! name = 'deactivate' ! binding = wx.EVT_ACTIVATE ! id = wx.NewEventType() ! class IdleEvent(Event): name = 'idle' --- 275,287 ---- name = 'select' ! # KEA 2004-05-09 ! # this is referenced in debug.py ! # and I don't want to create a circular import ! # with model.py, so I'm going to go ahead ! # and leave this in event.py ! # this might actually be better as an app level ! # event anyway, since I'm not sure every Background ! # should receive a separate idle event?! ! # need to test/experiment class IdleEvent(Event): name = 'idle' *************** *** 298,327 **** id = wx.wxEVT_IDLE - class MaximizeEvent(Event): - name = 'maximize' - binding = wx.EVT_MAXIMIZE - id = wx.wxEVT_MAXIMIZE - - class MinimizeEvent(Event): - name = 'minimize' - binding = wx.EVT_ICONIZE - id = wx.wxEVT_ICONIZE - - class MoveEvent(Event): - name = 'move' - binding = wx.EVT_MOVE - id = wx.wxEVT_MOVE - - class RestoreEvent(Event, InsteadOfTypeEvent): - name = 'restore' - binding = wx.EVT_ICONIZE - id = wx.NewEventType() - - class SizeEvent(Event): - name = 'size' - binding = wx.EVT_SIZE - id = wx.wxEVT_SIZE - - # focus events --- 289,292 ---- *************** *** 433,441 **** id = wx.wxEVT_LEFT_DOWN - class MouseDragEvent(MouseEvent, InsteadOfTypeEvent): - name = 'mouseDrag' - binding = wx.EVT_MOTION - id = wx.NewEventType() - class MouseEnterEvent(MouseEvent): name = 'mouseEnter' --- 398,401 ---- *************** *** 473,477 **** else: return self.id ! class MouseUpEvent(MouseEvent): name = 'mouseUp' --- 433,441 ---- else: return self.id ! ! class MouseDragEvent(MouseMoveEvent): ! name = 'mouseDrag' ! id = wx.NewEventType() ! class MouseUpEvent(MouseEvent): name = 'mouseUp' |