1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 """\
25 PyChan event handling (internal).
26 =================================
27
28 Users shouldn't need to use this module directly.
29 L{widgets.Widget.capture} and L{widgets.Widget.mapEvents} provide
30 a convenient API to capture events.
31
32 Nevertheless to understand how its supposed to work
33 take a look at L{EventMapper} and L{EventListenerBase}
34
35 Event callbacks
36 ---------------
37
38 You can either write callbacks yourself or
39 use L{tools.callBackWithArguments} or L{tools.attrSetCallback}
40 to generate suitable callbacks.
41
42 Here's an example callback::
43 def dumpEventInfo(event=0,widget=0):
44 print widget, " received the event ", event
45
46 Note the signature - C{event} and C{widget} are keyword
47 arguments passed to the callback. If doesn't accept either
48 C{event} or C{widget} as argument, these are not passed.
49
50 This way a simple function which ignores C{event} or C{widget}
51 can be used, while they are available if needed.
52
53 Currently only one callback can be set per event. In case
54 you don't want to write your own callback that dispatches
55 to different callbacks you can use L{tools.chainCallbacks}.
56
57 Available Events
58 ----------------
59
60 """
61
62 from compat import guichan
63 import widgets
64
65 import exceptions
66 from internal import get_manager
67 import tools
68 import traceback
69 import weakref
70 from fife.extensions.fife_timer import Timer
71 from fife.extensions.pychan.tools import callbackWithArguments as cbwa
72
73 EVENTS = [
74 "mouseEntered",
75 "mouseExited",
76 "mousePressed",
77 "mouseReleased",
78 "mouseClicked",
79 "mouseMoved",
80 "mouseWheelMovedUp",
81 "mouseWheelMovedDown",
82 "mouseDragged",
83 "action",
84 "keyPressed",
85 "keyReleased",
86 ]
87
88
89 __doc__ += "".join([" - %s\n" % event for event in EVENTS])
90
91
92
93 try: del event
94 except:pass
95
96 MOUSE_EVENT, KEY_EVENT, ACTION_EVENT = range(3)
98 if "mouse" in name:
99 return MOUSE_EVENT
100 if "key" in name:
101 return KEY_EVENT
102 return ACTION_EVENT
103
104
105 CALLBACK_NONE_MESSAGE = """\
106 You passed None as parameter to %s.capture, which would normally remove a mapped event.
107 But there was no event mapped. Did you accidently call a function instead of passing it?
108 """
109
111 """
112 Redirector for event callbacks.
113 Use *only* from L{EventMapper}.
114
115 This class uses the SWIG director feature - overriden
116 virtual methods are called from C++ to - listen to
117 Guichan events.
118
119 """
129
131 """
132 Start receiving events.
133 No need to call this manually.
134 """
135
136 if self.is_attached:
137 return
138 if not self.events:
139 return
140 if self.debug: print "Attach:",self
141 self.doAttach(widget.real_widget)
142 self.widget_ref = weakref.ref(widget)
143 self.is_attached = True
144
146 """
147 Stop receiving events.
148 No need to call this manually.
149 """
150 if not self.is_attached:
151 return
152 if self.debug: print "Detach:",self
153 self.is_attached = False
154
156 self.indent += 4
157 try:
158 event = self.translateEvent(getEventType(name), event)
159 if name in self.events:
160 if self.debug: print "-"*self.indent, name
161 for f in self.events[name].itervalues():
162 def delayed_f(timer, f=f):
163 n_timer = timer()
164 f( event )
165
166
167 del self._deadtimers[:]
168
169 if n_timer in self._timers:
170 self._deadtimers.append(n_timer)
171 self._timers.remove(n_timer)
172
173
174 timer = Timer(repeat=1)
175 timer._callback = cbwa(delayed_f, weakref.ref(timer))
176 timer.start()
177
178 self._timers.append(timer)
179
180 except:
181 print name, repr(event)
182 traceback.print_exc()
183 raise
184
185 finally:
186 self.indent -= 4
187
189 if event_type == MOUSE_EVENT:
190 return get_manager().hook.translate_mouse_event(event)
191 if event_type == KEY_EVENT:
192 return get_manager().hook.translate_key_event(event)
193 return event
194
201
216
224
226 """
227 Handles events and callbacks for L{widgets.Widget}
228 and derived classes.
229
230 Every PyChan widget has an L{EventMapper} instance
231 as attribute B{event_mapper}.
232
233 This instance handles all necessary house-keeping.
234 Such an event mapper can be either C{attached} or
235 C{detached}. In its attached state an L{EventListenerBase}
236 is added to the Guichan widget and will redirect
237 the events to the callbacks.
238
239 In its detached state no events are received from the
240 real Guichan widget.
241
242 The event mapper starts in the detached state.
243 When a new event is captured the mapper attaches itself
244 automatically. The widget doesn't need to handle that.
245 """
257
259 return "EventMapper(%s)" % repr(self.widget_ref())
260
262 for listener in self.listener.values():
263 listener.attach()
264
266 for listener in self.listener.values():
267 listener.detach()
268
269
270 - def capture(self,event_name,callback,group_name):
281
282 - def isCaptured(self,event_name,group_name="default"):
284
292
295
297 listener = self.getListener(event_name)
298 del listener.events[event_name][group_name]
299
300 if not listener.events[event_name]:
301 del listener.events[event_name]
302 if not listener.events:
303 listener.detach()
304
305 del self.callbacks[group_name][event_name]
306 if len(self.callbacks[group_name]) <= 0:
307 del self.callbacks[group_name]
308
309 - def addEvent(self,event_name,callback,group_name):
310 if not callable(callback):
311 raise RuntimeError("An event callback must be either a callable or None - not %s" % repr(callback))
312
313
314 self_ref = weakref.ref(self)
315
316
317 if not self.callbacks.has_key(group_name):
318 self.callbacks[group_name] = {}
319
320 if not self.callbacks[group_name].has_key(event_name):
321 self.callbacks[group_name][event_name] = {}
322
323 self.callbacks[group_name][event_name] = callback
324
325 def captured_f(event):
326 if self_ref() is not None:
327 tools.applyOnlySuitable(self_ref().callbacks[group_name][event_name],event=event,widget=self_ref().widget_ref())
328
329 listener = self.getListener(event_name)
330
331 if event_name not in listener.events:
332 listener.events[event_name] = {group_name : captured_f}
333 else:
334 listener.events[event_name][group_name] = captured_f
335 listener.attach(self.widget_ref())
336
337
339 """ Utility function to split "widgetName/eventName" descriptions into tuples. """
340 L = name.split("/")
341 if len(L) not in (1,2,3):
342 raise exceptions.RuntimeError("Invalid widgetname / eventname combination: " + name)
343 if len(L) == 1:
344 L = L[0],"action"
345 elif L[1] not in EVENTS:
346 raise exceptions.RuntimeError("Unknown event name: " + name)
347 if len(L) == 2:
348 L = L[0],L[1],"default"
349 return L
350