def __init__(self, source=None, auto_connect=True, **emitters): EventEmitter.__init__(self, source) self.auto_connect = auto_connect self.auto_connect_format = "on_%s" self._emitters = OrderedDict() self._emitters_connected = False # whether the sub-emitters have # been connected to the group self.add(**emitters)
def test_ordered_dict(): """Test ordered dictionary""" d1 = OrderedDict() d2 = OrderedDict() for d in [d1, d2]: d['a'] = 1 d['b'] = 2 d['c'] = 3 assert_equal(d1, d2) assert_true(all(dd1 == dd2 for dd1, dd2 in zip(reversed(d1), reversed(d2)))) assert_raises(TypeError, OrderedDict, 1, 2) del d1['c'] assert_true(d1 != d2) d2.popitem() assert_equal(d1, d2) # pickling (__reduce__) fname = op.join(temp_dir, 'pickle') with open(fname, 'wb') as fid: pickle.dump(d1, fid) with open(fname, 'rb') as fid: d1p = pickle.load(fid) assert_equal(d1, d1p)
class EmitterGroup(EventEmitter): """EmitterGroup instances manage a set of related :class:`EventEmitters <vispy.event.EventEmitter>`. Its primary purpose is to provide organization for objects that make use of multiple emitters and to reduce the boilerplate code needed to initialize those emitters with default connections. EmitterGroup instances are usually stored as an 'events' attribute on objects that use multiple emitters. For example:: EmitterGroup EventEmitter | | Canvas.events.mouse_press Canvas.events.resized Canvas.events.key_press EmitterGroup is also a subclass of :class:`EventEmitters <vispy.event.EventEmitter>`, allowing it to emit its own events. Any callback that connects directly to the EmitterGroup will receive _all_ of the events generated by the group's emitters. Input arguments --------------- source : object The object that the generated events apply to. auto_connect : bool If *auto_connect* is True (default), then one connection will be made for each emitter that looks like :func:`emitter.connect((source, 'on_'+event_name)) <vispy.event.EventEmitter.connect>`. This provides a simple mechanism for automatically connecting a large group of emitters to default callbacks. emitters : keyword arguments See the :func:`add <vispy.event.EmitterGroup.add>` method. """ def __init__(self, source=None, auto_connect=True, **emitters): EventEmitter.__init__(self, source) self.auto_connect = auto_connect self.auto_connect_format = "on_%s" self._emitters = OrderedDict() self._emitters_connected = False # whether the sub-emitters have # been connected to the group self.add(**emitters) def __getitem__(self, name): """ Return the emitter assigned to the specified name. Note that emitters may also be retrieved as an attribute of the EmitterGroup. """ return self._emitters[name] def __setitem__(self, name, emitter): """ Alias for EmitterGroup.add(name=emitter) """ self.add(**{name: emitter}) def add(self, auto_connect=None, **kwds): """ Add one or more EventEmitter instances to this emitter group. Each keyword argument may be specified as either an EventEmitter instance or an Event subclass, in which case an EventEmitter will be generated automatically. Thus:: # This statement: group.add(mouse_press=MouseEvent, mouse_release=MouseEvent) # ..is equivalent to this statement: group.add(mouse_press=EventEmitter(group.source, 'mouse_press', MouseEvent), mouse_release=EventEmitter(group.source, 'mouse_press', MouseEvent)) """ if auto_connect is None: auto_connect = self.auto_connect # check all names before adding anything for name in kwds: if name in self._emitters: raise ValueError("EmitterGroup already has an emitter named '%s'" % name) elif hasattr(self, name): raise ValueError("The name '%s' cannot be used as an emitter; it is already an attribute of EmitterGroup" % name) # add each emitter specified in the keyword arguments for name, emitter in kwds.items(): if emitter is None: emitter = Event if inspect.isclass(emitter) and issubclass(emitter, Event): emitter = EventEmitter(source=self.source, type=name, event_class=emitter) elif not isinstance(emitter, EventEmitter): raise Exception('Emitter must be specified as either an EventEmitter instance or Event subclass') emitter.source = self.source # give this emitter the same source as the group. setattr(self, name, emitter) self._emitters[name] = emitter if auto_connect and self.source is not None: emitter.connect((self.source, self.auto_connect_format % name)) # If emitters are connected to the group already, then this one should # be connected as well. if self._emitters_connected: emitter.connect(self) @property def emitters(self): """ List of current emitters in this group. """ return self._emitters def __iter__(self): """ Iterates over the names of emitters in this group. """ for k in self._emitters: yield k def block_all(self): """ Block all emitters in this group. """ self.block() for em in self._emitters.values(): em.block() def unblock_all(self): """ Unblock all emitters in this group. """ self.unblock() for em in self._emitters.values(): em.unblock() def connect(self, callback): """ Connect the callback to the event group. The callback will receive events from _all_ of the emitters in the group. See :func:`EventEmitter.connect() <vispy.event.EventEmitter.connect>` for arguments. """ self._connect_emitters(True) return EventEmitter.connect(self, callback) def disconnect(self, callback=None): """ Disconnect the callback from this group. See :func:`connect() <vispy.event.EmitterGroup.connect>` and :func:`EventEmitter.connect() <vispy.event.EventEmitter.connect>` for more information. """ ret = EventEmitter.disconnect(self, callback) if len(self.callbacks) == 0: self._connect_emitters(False) return ret def _connect_emitters(self, connect): # Connect/disconnect all sub-emitters from the group. This allows the # group to emit an event whenever _any_ of the sub-emitters emit, # while simultaneously eliminating the overhead if nobody is listening. if connect: for emitter in self: self[emitter].connect(self) else: for emitter in self: self[emitter].disconnect(self) self._emitters_connected = connect