86 class Emitter: |
87 class Emitter: |
87 |
88 |
88 """Event emitter mixin class.""" |
89 """Event emitter mixin class.""" |
89 |
90 |
90 def add_events(self, *events): |
91 def add_events(self, *events): |
91 """Add events which may be registered by user. |
92 """Add events which may be emitted on instances of this class. |
92 |
93 |
93 This should be called only by subclasses. |
94 This should be called only by subclasses. |
94 This serves also as initializer, other methods of Emitter |
95 It also serves as initializer, other methods of Emitter |
95 will not work if add_events was not called. |
96 will not work if add_events was not called. |
96 |
97 |
97 *events -- Arguments must be given in pairs. |
98 *events -- Arguments must be given in pairs. |
98 |
99 |
99 Each pair consists of event_name, event_class: |
100 Each pair consists of event_name, event_class: |
100 event_name -- a string used in connect(), emit() |
101 event_name -- a string used in add_handler(), emit() etc. |
101 event_class -- class of event payload |
102 event_class -- class of event payload |
102 |
103 |
103 """ |
104 It will also inspect base classes of instance on which add_events() |
|
105 is called, adding all methods meeting naming convention to handler list. |
|
106 The convention for method names is "on_event_name", "after_event_name". |
|
107 Prefix on/after determines whether the handler is added as first or last. |
|
108 |
|
109 """ |
|
110 if len(events) % 2: |
|
111 raise ValueError('add_events(): Event names and classes must be passed in pairs.') |
104 if not hasattr(self, '_event_handlers'): |
112 if not hasattr(self, '_event_handlers'): |
105 self._event_handlers = dict() |
113 self._event_handlers = dict() |
106 self._event_class = dict() |
114 self._event_class = dict() |
107 for event_name, event_class in zip(events[::2], events[1::2]): |
115 for event_name, event_class in zip(events[::2], events[1::2]): |
|
116 if not isinstance(event_name, str): |
|
117 raise TypeError('add_events(): Event name must be a string: %r.' % event_name) |
|
118 if not issubclass(event_class, Event): |
|
119 raise TypeError('add_events(): Class %s does not inherit from Event.' % event_class) |
108 self._event_handlers[event_name] = [] |
120 self._event_handlers[event_name] = [] |
109 self._event_class[event_name] = event_class |
121 self._event_class[event_name] = event_class |
110 # add default dummy handler if no handler exists for this event |
122 self._add_default_handlers(event_name) |
111 handler_name = '_handle_' + event_name |
123 |
112 if not hasattr(Emitter, handler_name): |
124 def add_handler(self, event_name, handler, last=False): |
113 setattr(Emitter, handler_name, lambda self, ev: False) |
125 """Add handler to event name. |
114 |
126 |
115 def connect(self, event_name, handler): |
127 last=False -- Add handler as first in handler list. |
116 """Connect event handler to event name. |
128 last=True -- Add handler to the end of handler list. |
117 |
129 |
118 Add handler to the end of handler list. |
130 """ |
119 |
131 if event_name in self._event_handlers: |
120 """ |
132 if last: |
121 if event_name in self._event_handlers: |
133 self._event_handlers[event_name].append(handler) |
122 self._event_handlers[event_name].append(handler) |
|
123 else: |
|
124 raise KeyError('Unknown event: %s', event_name) |
|
125 |
|
126 def disconnect(self, event_name, handler=None): |
|
127 """Remove event handler from the list. |
|
128 |
|
129 If no handler is given, remove all handlers. |
|
130 |
|
131 """ |
|
132 if event_name in self._event_handlers: |
|
133 if handler: |
|
134 self._event_handlers[event_name].remove(handler) |
|
135 else: |
134 else: |
136 self._event_handlers[event_name][:] = [] |
135 self._event_handlers[event_name].insert(0, handler) |
137 else: |
136 else: |
138 raise KeyError('Unknown event: %s', event_name) |
137 raise KeyError('Unknown event: %s', event_name) |
139 |
138 |
140 def is_connected(self, event_name): |
139 def remove_handler(self, event_name, handler): |
141 """Test if any handlers are connected to event name. |
140 """Remove event handler from the list.""" |
|
141 if event_name in self._event_handlers: |
|
142 self._event_handlers[event_name].remove(handler) |
|
143 else: |
|
144 raise KeyError('Unknown event: %s', event_name) |
|
145 |
|
146 def remove_all_handlers(self, event_name): |
|
147 """Remove all handlers for event.""" |
|
148 if event_name in self._event_handlers: |
|
149 self._event_handlers[event_name][:] = [] |
|
150 else: |
|
151 raise KeyError('Unknown event: %s', event_name) |
|
152 |
|
153 def has_handlers(self, event_name): |
|
154 """Test if any handlers are attached to event name. |
142 |
155 |
143 Return True if event handler list is not empty, |
156 Return True if event handler list is not empty, |
144 False otherwise. |
157 False otherwise. |
145 |
158 |
146 """ |
159 """ |
151 |
164 |
152 def emit(self, event_name, *args, **kwargs): |
165 def emit(self, event_name, *args, **kwargs): |
153 """Emit the event. |
166 """Emit the event. |
154 |
167 |
155 Call all handlers from event's handler list, |
168 Call all handlers from event's handler list, |
156 starting from first added handler. |
169 starting from last added handler, going to those added |
|
170 before, ending with those added with last=True. |
|
171 |
|
172 Stop if any handler returns True. |
157 |
173 |
158 Return True when one of the handlers returns True, |
174 Return True when one of the handlers returns True, |
159 False otherwise. |
175 False otherwise. |
160 |
176 |
161 This creates new instance of event_class given to |
177 Creates new instance of event_class given to |
162 add_events() and passes all arguments after event_name |
178 add_events() and passes all arguments after event_name |
163 to its __init__ method. |
179 to its __init__ method. |
164 |
180 |
165 Unless first of these arguments is Event instance |
181 Unless first of these arguments is Event instance |
166 in which case no object is created and the instance |
182 in which case no object is created and the Event instance |
167 is passed to handlers. |
183 is passed to handlers. |
168 |
184 |
169 """ |
185 """ |
170 logging.getLogger('tuikit').debug('Emit "%s" on %s %s', |
186 logging.getLogger('tuikit').debug('Emit "%s" on %s %s', |
171 event_name, |
187 event_name, |
172 self.__class__.__name__, |
188 self.__class__.__name__, |
173 getattr(self, 'name', None) or id(self)) |
189 getattr(self, 'name', None) or id(self)) |
174 # create event from specified event class, or use first argument |
190 # create event from specified event class, or use first argument |
175 if len(args) and isinstance(args[0], Event): |
191 if len(args) == 1 and isinstance(args[0], Event): |
176 event = args[0] |
192 event = args[0] |
177 else: |
193 else: |
178 event = self._event_class[event_name](*args, **kwargs) |
194 event = self._event_class[event_name](*args, **kwargs) |
|
195 # set originator to instance on which emit() was called |
179 event.originator = self |
196 event.originator = self |
180 # try default handler, stop if satisfied |
197 # call handlers from first to last, stop if satisfied |
181 handled = getattr(self, '_handle_' + event_name)(event) |
|
182 if handled: |
|
183 return True |
|
184 # try custom handlers, stop if satisfied |
|
185 for handler in self._event_handlers[event_name]: |
198 for handler in self._event_handlers[event_name]: |
186 handled = handler(event) |
199 handled = handler(event) |
187 if handled: |
200 if handled: |
188 return True |
201 return True |
189 |
202 return False |
|
203 |
|
204 def _add_default_handlers(self, event_name): |
|
205 """Add default handlers from the instance and its base classes. |
|
206 |
|
207 Handlers are looked up in methods by their names. |
|
208 Method name is composited from prefix and event_name. |
|
209 Prefix is one of "on_" or "after_". |
|
210 Depending on prefix the handler is added to top or bottom of handler stack. |
|
211 |
|
212 See _add_default_handler method for more information. |
|
213 |
|
214 """ |
|
215 for cls in reversed(inspect.getmro(self.__class__)): |
|
216 self._add_default_handler(cls, 'on_', event_name, last=False) |
|
217 self._add_default_handler(cls, 'after_', event_name, last=True) |
|
218 |
|
219 def _add_default_handler(self, cls, prefix, event_name, last): |
|
220 """Add handler from one of base classes to handler list. |
|
221 |
|
222 cls -- one of self's base classes |
|
223 prefix -- method name prefix |
|
224 event_name -- event name |
|
225 last -- if True, add handler to end of handler list (will be called last) |
|
226 |
|
227 Look for method of name prefix + event_name in class 'cls', |
|
228 if exists, add it to handler list. |
|
229 |
|
230 """ |
|
231 method_name = prefix + event_name |
|
232 if method_name in cls.__dict__: |
|
233 unbound_handler = cls.__dict__[method_name] |
|
234 if callable(unbound_handler): |
|
235 handler = unbound_handler.__get__(self, cls) |
|
236 self.add_handler(event_name, handler, last) |
|
237 |