Mercurial > fife-parpg
diff engine/extensions/pychan/events.py @ 205:54bfd1015b35
* PyChan event handling rework (part I)
** Unified listeners
** ...hopefully more robust attach/detach code.
* Added compat, layout and also the new autopsition feature.
* Documentation
* Minor style fixes in core.
author | phoku@33b003aa-7bff-0310-803a-e67f0ece8222 |
---|---|
date | Sat, 14 Mar 2009 12:13:29 +0000 |
parents | 06dddc96ce54 |
children | e281223a03a6 |
line wrap: on
line diff
--- a/engine/extensions/pychan/events.py Sat Mar 14 12:03:56 2009 +0000 +++ b/engine/extensions/pychan/events.py Sat Mar 14 12:13:29 2009 +0000 @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- #coding: utf-8 """ @@ -11,15 +12,39 @@ Nevertheless to understand how its supposed to work take a look at L{EventMapper} and L{EventListener} +Event callbacks +--------------- + +You can either write callbacks yourself or +use L{tools.callBackWithArguments} or L{tools.attrSetCallback} +to generate suitable callbacks. + +Here's an example callback:: + def dumpEventInfo(event=0,widget=0): + print widget, " received the event ", event + +Note the signature - C{event} and C{widget} are keyword +arguments passed to the callback. If doesn't accept either +C{event} or C{widget} as argument, these are not passed. + +This way a simple function which ignores C{event} or C{widget} +can be used, while they are available if needed. + +Currently only one callback can be set per event. In case +you don't want to write your own callback that dispatches +to different callbacks you can use L{tools.chainCallbacks}. + Available Events ---------------- """ -import fife +from compat import guichan + import exceptions -import manager +from internal import get_manager import tools +import traceback EVENTS = [ "mouseEntered", @@ -37,6 +62,11 @@ # Add the EVENTS to the docs. __doc__ += "".join([" - %s\n" % event for event in EVENTS]) +# The line before seems to leak the variable event into the global namespace ... remove that! +# This is a python problem, addressed in python3 +try: del event +except:pass + MOUSE_EVENT, KEY_EVENT, ACTION_EVENT = range(3) def getEventType(name): if "mouse" in name: @@ -50,8 +80,7 @@ You passed None as parameter to %s.capture, which would normally remove a mapped event. But there was no event mapped. Did you accidently call a function instead of passing it? """ - -class EventListener(fife.GUIEventListener): +class EventListenerBase(object): """ Redirector for event callbacks. Use *only* from L{EventMapper}. @@ -60,45 +89,92 @@ virtual methods are called from C++ to - listen to Guichan events. - When the module is first loaded the event handler - methods are auto-generated from the list L{EVENTS}. - This is effectively the same code as:: - def mouseEntered(self,event): - self._redirectEvent("mouseEntered",event) - - This way L{EVENTS} and the actually receivable events - are forced to be in sync. """ - def __init__(self,debug=True): - super(EventListener,self).__init__() + def __init__(self): + super(EventListenerBase,self).__init__() self.events = {} self.indent = 0 - self.debug = debug - self.guimanager = manager.Manager.manager.guimanager + self.debug = 1 + self.is_attached = False + + def attach(self,widget): + """ + Start receiving events. + No need to call this manually. + """ + + if self.is_attached: + return + if not self.events: + return + if self.debug: print "Attach:",self + self.doAttach(widget.real_widget) + self.widget = widget + self.is_attached = True + + def detach(self): + """ + Stop receiving events. + No need to call this manually. + """ + if not self.is_attached: + return + if self.debug: print "Detach:",self + self.doDetach(self.widget.real_widget) + self.widget = None + self.is_attached = False def _redirectEvent(self,name,event): self.indent += 4 - event = self.translateEvent(getEventType(name), event) - if name in self.events: - if self.debug: print "-"*self.indent, name - for f in self.events[name].itervalues(): - f( event ) - self.indent -= 4 + try: + event = self.translateEvent(getEventType(name), event) + if name in self.events: + if self.debug: print "-"*self.indent, name + for f in self.events[name].itervalues(): + f( event ) + + except: + print name, event + traceback.print_exc() + raise + + finally: + self.indent -= 4 def translateEvent(self,event_type,event): if event_type == MOUSE_EVENT: - return self.guimanager.translateMouseEvent(event) + return get_manager().hook.translate_mouse_event(event) if event_type == KEY_EVENT: - return self.guimanager.translateKeyEvent(event) + return get_manager().hook.translate_key_event(event) return event -def _redirect(name): - def redirectorFunc(self,event): - self._redirectEvent(name,event) - return redirectorFunc +class _ActionEventListener(EventListenerBase,guichan.ActionListener): + def __init__(self):super(_ActionEventListener,self).__init__() + def doAttach(self,real_widget): real_widget.addActionListener(self) + def doDetach(self,real_widget): real_widget.removeActionListener(self) + + def action(self,e): self._redirectEvent("action",e) + +class _MouseEventListener(EventListenerBase,guichan.MouseListener): + def __init__(self):super(_MouseEventListener,self).__init__() + def doAttach(self,real_widget): real_widget.addMouseListener(self) + def doDetach(self,real_widget): real_widget.removeMouseListener(self) -for event_name in EVENTS: - setattr(EventListener,event_name,_redirect(event_name)) + def mouseEntered(self,e): self._redirectEvent("mouseEntered",e) + def mouseExited(self,e): self._redirectEvent("mouseExited",e) + def mousePressed(self,e): self._redirectEvent("mousePressed",e) + def mouseReleased(self,e): self._redirectEvent("mouseReleased",e) + def mouseClicked(self,e): self._redirectEvent("mouseClicked",e) + def mouseMoved(self,e): self._redirectEvent("mouseMoved",e) + def mouseDragged(self,e): self._redirectEvent("mouseDragged",e) + +class _KeyEventListener(EventListenerBase,guichan.KeyListener): + def __init__(self):super(_KeyEventListener,self).__init__() + def doAttach(self,real_widget): real_widget.addKeyListener(self) + def doDetach(self,real_widget): real_widget.removeKeyListener(self) + + def keyPressed(self,e): self._redirectEvent("keyPressed",e) + def keyReleased(self,e): self._redirectEvent("keyReleased",e) class EventMapper(object): """ @@ -106,7 +182,7 @@ and derived classes. Every PyChan widget has an L{EventMapper} instance - as attribute *event_mapper*. + as attribute B{event_mapper}. This instance handles all necessary house-keeping. Such an event mapper can be either *attached* or @@ -124,44 +200,17 @@ def __init__(self,widget): super(EventMapper,self).__init__() self.widget = widget - self.listener = EventListener() + self.listener = { + KEY_EVENT : _KeyEventListener(), + ACTION_EVENT : _ActionEventListener(), + MOUSE_EVENT : _MouseEventListener(), + } self.is_attached = False - self.debug = manager.Manager.manager.debug + self.debug = get_manager().debug - def __del__(self): - self.detach() def __repr__(self): return "EventMapper(%s)" % repr(self.widget) - def attach(self): - """ - Start receiving events. - No need to call this manually. - """ - - if self.is_attached: - return - if not self.listener.events: - return - if self.debug: print "Attach:",self - self.widget.real_widget.addKeyListener( self.listener ) - self.widget.real_widget.addMouseListener( self.listener ) - self.widget.real_widget.addActionListener( self.listener ) - self.is_attached = True - - def detach(self): - """ - Stop receiving events. - No need to call this manually. - """ - - if not self.is_attached: - return - if self.debug: print "Detach:",self - self.widget.real_widget.removeKeyListener( self.listener ) - self.widget.real_widget.removeMouseListener( self.listener ) - self.widget.real_widget.removeActionListener( self.listener ) - self.is_attached = False def capture(self,event_name,callback,group_name): if event_name not in EVENTS: @@ -169,32 +218,48 @@ if callback is None: if self.isCaptured(event_name,group_name): - del self.listener.events[event_name][group_name] - if not self.listener.events[event_name]: - del self.listener.events[event_name] - if not self.listener.events: - self.detach() + self.removeEvent(event_name,group_name) elif self.debug: print CALLBACK_NONE_MESSAGE % str(self.widget) return + self.addEvent(event_name,callback,group_name) + def isCaptured(self,event_name,group_name="default"): + return ("%s/%s" % (event_name,group_name)) in self.getCapturedEvents() + + def getCapturedEvents(self): + events = [] + for event_type, listener in self.listener.items(): + for event_name, group in listener.events.items(): + for group_name in group.keys(): + events.append( "%s/%s" % (event_name, group_name) ) + return events + + def getListener(self,event_name): + return self.listener[getEventType(event_name)] + + def removeEvent(self,event_name,group_name): + listener = self.getListener(event_name) + del listener.events[event_name][group_name] + if not listener.events[event_name]: + del listener.events[event_name] + if not listener.events: + listener.detach() + + def addEvent(self,event_name,callback,group_name): if not callable(callback): raise RuntimeError("An event callback must be either a callable or None - not %s" % repr(callback)) def captured_f(event): tools.applyOnlySuitable(callback,event=event,widget=self.widget) - if event_name not in self.listener.events: - self.listener.events[event_name] = {group_name : captured_f} - else: - self.listener.events[event_name][group_name] = captured_f - self.attach() + listener = self.getListener(event_name) - def isCaptured(self,event_name,group_name="default"): - return event_name in self.listener.events and group_name in self.listener.events[event_name] - - def getCapturedEvents(self): - return self.listener.events.keys() + if event_name not in listener.events: + listener.events[event_name] = {group_name : captured_f} + else: + listener.events[event_name][group_name] = captured_f + listener.attach(self.widget) def splitEventDescriptor(name): @@ -210,3 +275,4 @@ L = L[0],L[1],"default" return L +