Package advene :: Package model :: Module events
[hide private]
[frames] | no frames]

Source Code for Module advene.model.events

  1  """ 
  2  I provide the event/notification framework for Advene. 
  3   
  4  This framework is implemented on top of GTK (GObject) events. However, Advene 
  5  object do not inherit GObject, since they do not provide other functionalities 
  6  than events. 
  7  """ 
  8   
  9  from advene.util.synchronized import enter_cs, exit_cs 
 10   
 11  import gobject 
 12   
13 -class EventDelegate(gobject.GObject):
14 """ 15 Base class of event delegate. 16 """
17 - def __init__(self, boss):
18 gobject.GObject.__init__(self) 19 self._boss = boss
20
21 -class PackageEventDelegate(EventDelegate):
22 """ 23 Class for event-delegate for packages. 24 """ 25 pass
26
27 -class ElementEventDelegate(EventDelegate):
28 """ 29 Class for event-delegate for elements. 30 """ 31 pass
32
33 -def _handler_wrapper(gobj, *args):
34 original_handler = args[-1] 35 args = args[:-1] 36 return original_handler(gobj._boss, *args)
37
38 -class WithEventsMixin(object):
39 """ 40 This mixin class assumes that the mixed-in class will provide a 41 `_make_event_delegate` method returning an instance of the appropriate 42 subclass of EventDelegate. 43 """ 44 45 __event_delegate = None 46 __disabling_count = 0 47
48 - def connect(self, detailed_signal, handler, *args):
49 """ 50 Connect the given handler to the (optionally detailed) signal. 51 Additional arguments for the handler can be given. 52 53 Return the handler_id, which can be used to disconnect the handler. 54 """ 55 if self.__event_delegate is None: 56 self.__event_delegate = self._make_event_delegate() 57 wrapper_args = list(args) + [handler,] 58 return self.__event_delegate.connect(detailed_signal, _handler_wrapper, 59 *wrapper_args)
60
61 - def disconnect(self, handler_id):
62 """Disconnect the handler associated to the given handler_id.""" 63 assert self.has_handler(handler_id), "Handler-id %d is not connected" % handler_id 64 return self.__event_delegate.disconnect(handler_id)
65
66 - def has_handler(self, handler_id):
67 """ 68 Return True iff the given handler_id represents a connected handler. 69 70 NB: this has been renamed from GObject.handler_is_connected to comply 71 with Advene coding style (methode names should start with a verb). 72 """ 73 return self.__event_delegate is not None \ 74 and self.__event_delegate.handler_is_connected(handler_id)
75
76 - def block_handler(self, handler_id):
77 """ 78 Prevent the handler identified by handler_id to be invoked until it is 79 unblocked. 80 81 NB: this has been renamed from GObject.handler_block to comply 82 with Advene coding style (methode names should start with a verb). 83 """ 84 assert self.has_handler(handler_id), "Handler-id %d is not connected" % handler_id 85 return self.__event_delegate.handler_block(handler_id)
86
87 - def unblock_handler(self, handler_id):
88 """ 89 Unblock the blocked handler identified by handler_id so it can be 90 invoked again. 91 92 NB: this has been renamed from GObject.handler_unblock to comply 93 with Advene coding style (methode names should start with a verb). 94 """ 95 assert self.has_handler(handler_id), "Handler-id %d is not connected" % handler_id 96 return self.__event_delegate.handler_unblock(handler_id)
97
98 - def emit(self, detailed_signal, *args):
99 """ 100 Cause the object to emit the signal specified by detailed_signal. 101 The additional parameters must match the number and type of the 102 required signal handler parameters. 103 """ 104 if self.__event_delegate is not None and self.__disabling_count == 0: 105 return self.__event_delegate.emit(detailed_signal, *args)
106
107 - def emit_lazy(self, lazy_params):
108 """ 109 Like emit, but lazy_params is assumed to be a function returning an 110 iterable of the params to send to emit. 111 The rationale is that, since emit does nothing if we have no 112 EventDelegate, the parameters would not be evaluated. 113 """ 114 if self.__event_delegate is not None: 115 return self.__event_delegate.emit(*lazy_params())
116
117 - def stop_emission(self, detailed_signal):
118 """ 119 Stop the current emission of the signal specified by detailed_signal. 120 Any signal handlers in the list still to be run will not be 121 invoked. 122 """ 123 assert self.__event_delegate is not None 124 return self.__event_delegate.stop_emission(detailed_signal)
125 126 # synonyms for the sake of readability in GTK applications 127 128 handler_is_connected = has_handler 129 handler_block = block_handler 130 handler_unblock = unblock_handler 131 132 # advene specific methods 133
134 - def enter_no_event_section(self):
135 """ 136 Disable all event emission for this object, until 137 `exit_no_event_section` is called. 138 139 Not also that a "no event section is a critical section for the object 140 (in the sense of the `advene.util.synchronized` module). 141 """ 142 enter_cs(self) 143 self.__disabling_count += 1
144
145 - def exit_no_event_section(self):
146 """ 147 Re-enables all event emission for this object. 148 149 :see-also: `enter_no_event_section` 150 """ 151 self.__disabling_count -= 1 152 exit_cs(self)
153 154 155 # Common signals 156 # ============== 157 # 158 # NB: all signals involving a change have a "pre-" form, which is emitted 159 # *before* the actual change takes place. This can be useful for handlers 160 # requiring the state preceding the change (like EDL). 161 # 162 # signal:`modified` 163 # ---------------- 164 # 165 # Emitted everytime an attribute is modified in the object 166 # 167 # detail:: (depending on the object type) uri, url, frame_of_reference, media, 168 # begin, end 169 # params:: 170 # * the attribute name 171 # * the new value of the modified attribute 172 # 173 # This signal also has a "pre-" form. 174 175 gobject.signal_new("pre-modified", EventDelegate, 176 gobject.SIGNAL_RUN_FIRST|gobject.SIGNAL_DETAILED, 177 gobject.TYPE_NONE, (object,object,)) 178 179 gobject.signal_new("modified", EventDelegate, 180 gobject.SIGNAL_RUN_FIRST|gobject.SIGNAL_DETAILED, 181 gobject.TYPE_NONE, (object,object,)) 182 183 # signal:`modified-meta` 184 # ------------------- 185 # 186 # Emitted everytime a meta-data is created/modified in the object 187 # 188 # detail:: the URL key of the created/modified metadata 189 # params:: 190 # * the URL key of the metadata 191 # * the new value of the modified meta-data (None if deleted) 192 # 193 # This signal also has a "pre-" form. 194 195 gobject.signal_new("pre-modified-meta", EventDelegate, 196 gobject.SIGNAL_RUN_FIRST|gobject.SIGNAL_DETAILED, 197 gobject.TYPE_NONE, (object,object,)) 198 199 gobject.signal_new("modified-meta", EventDelegate, 200 gobject.SIGNAL_RUN_FIRST|gobject.SIGNAL_DETAILED, 201 gobject.TYPE_NONE, (object,object,)) 202 203 # Package signals 204 # =============== 205 # 206 # signal:`created` 207 # ---------------- 208 # 209 # Emitted everytime an element is created in the package 210 # 211 # detail:: media, annotation, relation, tag, list, query, view, resource, 212 # import, content_url, content_mimetype, content_schema 213 # params:: 214 # * the object just created 215 # 216 217 gobject.signal_new("created", PackageEventDelegate, 218 gobject.SIGNAL_RUN_FIRST|gobject.SIGNAL_DETAILED, 219 gobject.TYPE_NONE, (object,)) 220 221 # signal:`package-closed` 222 # ---------------- 223 # 224 # Emitted when the package is closed. 225 # NB: the package is already closed, so it is an error to use it when receiving 226 # this signal. However, the URL and URI of the package are given as parameters. 227 # 228 # params:: 229 # * the package URL 230 # * the package URI 231 # 232 233 gobject.signal_new("package-closed", PackageEventDelegate, 234 gobject.SIGNAL_RUN_FIRST, 235 gobject.TYPE_NONE, (object, object,)) 236 237 # signals:<element_type> 238 # ---------------------- 239 # 240 # Instead of subscribint to each individual element, one can choose to 241 # subscribe globally to all elements of a given type in a package, using 242 # the signal named after that element type. 243 # 244 # The detail for this signal is the name of the corresponding element signal, 245 # without its detail. E.g. media::modified, list::pre-modified-items... 246 # 247 # signal:: media, annotation, relation, tag, list, query, view, resource, 248 # import 249 # 250 # detail:: any element related signal 251 # 252 # params:: 253 # * the element to which the event is related 254 # * a list of ojects corresponding to the parameters of the element 255 # signal 256 257 gobject.signal_new("media", PackageEventDelegate, 258 gobject.SIGNAL_RUN_FIRST|gobject.SIGNAL_DETAILED, 259 gobject.TYPE_NONE, (object,str,object,)) 260 261 gobject.signal_new("annotation", PackageEventDelegate, 262 gobject.SIGNAL_RUN_FIRST|gobject.SIGNAL_DETAILED, 263 gobject.TYPE_NONE, (object,str,object,)) 264 265 gobject.signal_new("relation", PackageEventDelegate, 266 gobject.SIGNAL_RUN_FIRST|gobject.SIGNAL_DETAILED, 267 gobject.TYPE_NONE, (object,str,object,)) 268 269 gobject.signal_new("tag", PackageEventDelegate, 270 gobject.SIGNAL_RUN_FIRST|gobject.SIGNAL_DETAILED, 271 gobject.TYPE_NONE, (object,str,object,)) 272 273 gobject.signal_new("list", PackageEventDelegate, 274 gobject.SIGNAL_RUN_FIRST|gobject.SIGNAL_DETAILED, 275 gobject.TYPE_NONE, (object,str,object,)) 276 277 gobject.signal_new("query", PackageEventDelegate, 278 gobject.SIGNAL_RUN_FIRST|gobject.SIGNAL_DETAILED, 279 gobject.TYPE_NONE, (object,str,object,)) 280 281 gobject.signal_new("view", PackageEventDelegate, 282 gobject.SIGNAL_RUN_FIRST|gobject.SIGNAL_DETAILED, 283 gobject.TYPE_NONE, (object,str,object,)) 284 285 gobject.signal_new("resource", PackageEventDelegate, 286 gobject.SIGNAL_RUN_FIRST|gobject.SIGNAL_DETAILED, 287 gobject.TYPE_NONE, (object,str,object,)) 288 289 gobject.signal_new("import", PackageEventDelegate, 290 gobject.SIGNAL_RUN_FIRST|gobject.SIGNAL_DETAILED, 291 gobject.TYPE_NONE, (object,str,object,)) 292 293 294 # Element signals 295 # =============== 296 # 297 # signal:`modified-items` 298 # ---------------------- 299 # 300 # Emitted for lists and relations when the structure of their items changes. 301 # 302 # params:: 303 # * a slice with only positive indices, relative to the old structure, 304 # embeding all the modified indices 305 # * a python list representing the new structure of the slice 306 # 307 # NB: because of the current implementation, some operations (set a slice, 308 # delete a slice, extend) are actually implemented using more atomic operations 309 # (__setitem__, __delitem__, append) and will hence emit no event by themselves 310 # but let the underlying operations send several "atomic" events. 311 # 312 # This signal also has a "pre-" form. 313 314 gobject.signal_new("pre-modified-items", ElementEventDelegate, 315 gobject.SIGNAL_RUN_FIRST, 316 gobject.TYPE_NONE, (object, object,)) 317 318 gobject.signal_new("modified-items", ElementEventDelegate, 319 gobject.SIGNAL_RUN_FIRST, 320 gobject.TYPE_NONE, (object, object,)) 321 322 # signal:`modified-content-data` 323 # ----------------------------- 324 # 325 # Emitted for elements with content when their content data is modified. 326 # 327 # params:: 328 # * TODO: find a way to represent a versatile (i.e. text or binary) diff. 329 # Can be None if such a diff mechanism is not implemented. 330 331 gobject.signal_new("modified-content-data", ElementEventDelegate, 332 gobject.SIGNAL_RUN_FIRST, 333 gobject.TYPE_NONE, (object,)) 334 335 # signal:`renamed` 336 # ---------------- 337 # 338 # Emitted when the ID of this element changes. 339 # 340 # This signal also has a "pre-" form. 341 342 gobject.signal_new("pre-renamed", ElementEventDelegate, 343 gobject.SIGNAL_RUN_FIRST, 344 gobject.TYPE_NONE, ()) 345 346 gobject.signal_new("renamed", ElementEventDelegate, 347 gobject.SIGNAL_RUN_FIRST, 348 gobject.TYPE_NONE, ()) 349 350 # signal:`deleted` 351 # ---------------- 352 # 353 # Emitted when the ID of this element is deleted. 354 # 355 # This signal also has a "pre-" form. 356 357 gobject.signal_new("pre-deleted", ElementEventDelegate, 358 gobject.SIGNAL_RUN_FIRST, 359 gobject.TYPE_NONE, ()) 360 361 gobject.signal_new("deleted", ElementEventDelegate, 362 gobject.SIGNAL_RUN_FIRST, 363 gobject.TYPE_NONE, ()) 364 365 # signal:`added-tag` 366 # ------------------ 367 # 368 # Emitted when the element has a tag added to it. 369 # 370 # params:: 371 # * the tag that has been added 372 373 gobject.signal_new("added-tag", ElementEventDelegate, 374 gobject.SIGNAL_RUN_FIRST, 375 gobject.TYPE_NONE, (object,)) 376 377 # signal:`removed-tag` 378 # -------------------- 379 # 380 # Emitted when the element has a tag removed from it. 381 # 382 # params:: 383 # * the tag that has been removed 384 385 gobject.signal_new("removed-tag", ElementEventDelegate, 386 gobject.SIGNAL_RUN_FIRST, 387 gobject.TYPE_NONE, (object,)) 388 389 # signal:`added` 390 # -------------- 391 # 392 # Emitted by tags when added to an element. 393 # 394 # params:: 395 # * the element this tag has been added to. 396 397 gobject.signal_new("added", ElementEventDelegate, 398 gobject.SIGNAL_RUN_FIRST, 399 gobject.TYPE_NONE, (object,)) 400 401 # signal:`removed` 402 # ---------------- 403 # 404 # Emitted by tags when removed from an element. 405 # 406 # params:: 407 # * the element this tag has been removed from. 408 409 gobject.signal_new("removed", ElementEventDelegate, 410 gobject.SIGNAL_RUN_FIRST, 411 gobject.TYPE_NONE, (object,)) 412 413 # TODO : 414 # * signal de suppression pour les elements 415