diff options
Diffstat (limited to 'tutorius/TProbe.py')
-rw-r--r-- | tutorius/TProbe.py | 165 |
1 files changed, 88 insertions, 77 deletions
diff --git a/tutorius/TProbe.py b/tutorius/TProbe.py index ec0f9a3..6c0883a 100644 --- a/tutorius/TProbe.py +++ b/tutorius/TProbe.py @@ -10,7 +10,9 @@ import cPickle as pickle import sugar.tutorius.addon as addon from sugar.tutorius.services import ObjectStore +from sugar.tutorius.properties import TPropContainer +from sugar.tutorius.dbustools import remote_call, save_args import copy """ @@ -157,38 +159,21 @@ class TProbe(dbus.service.Object): in_signature='s', out_signature='s') def subscribe(self, pickled_event): """ - Subscribe to a Gtk Widget Event - @param pickled_event string pickled Event + Subscribe to an Event + @param pickled_event string pickled EventFilter @return string unique name of registered event """ - event = pickle.loads(str(pickled_event)) - - # TODO elavoie 2009-07-25 Move to a reference counting implementation - # to avoid duplicating eventfilters when the event signature is the - # same - - # For now we will assume every probe is inserted in a GTK activity, - # however, in the future this should be moved in a subclass - eventfilter = addon.create("GtkWidgetEventFilter") - - # There might be a validation of the Address in source in the future - # and a partial resolution to extract the object_id from the address - eventfilter.object_id = event.source - - # TODO elavoie 2009-07-19 - # There should be a type translation from a tutorius type - # to a GTK type here - eventfilter.event_name = event.type + eventfilter = pickle.loads(str(pickled_event)) # The callback uses the event defined previously and each # successive call to subscribe will register a different # callback that references a different event def callback(*args): - self.notify(event) + self.notify(eventfilter) eventfilter.install_handlers(callback, activity=self._activity) - name = self._generate_event_reference(event) + name = self._generate_event_reference(eventfilter) self._subscribedEvents[name] = eventfilter return name @@ -215,7 +200,14 @@ class TProbe(dbus.service.Object): # The actual method we will call on the probe to send events def notify(self, event): - self.eventOccured(pickle.dumps(event)) + logging.debug("TProbe :: notify event %s", str(event)) + #HACK: reinstanciate the event with it's properties, to clear + # any internal state from getting pickled + if isinstance(event, TPropContainer): + newevent = type(event)(**event._props) + else: + newevent = event + self.eventOccured(pickle.dumps(newevent)) # Return a unique name for this action def _generate_action_reference(self, action): @@ -232,7 +224,7 @@ class TProbe(dbus.service.Object): # Return a unique name for this event def _generate_event_reference(self, event): # TODO elavoie 2009-07-25 Should return a universal address - name = event.type + name = event.__class__.__name__ suffix = 1 while self._subscribedEvents.has_key(name+str(suffix)): @@ -270,36 +262,51 @@ class ProbeProxy: self._probe = dbus.Interface(self._object, "org.tutorius.ProbeInterface") self._actions = {} - self._events = {} # We keep those two data structures to be able to have multiple callbacks # for the same event and be able to remove them independently + # _subscribedEvents holds a list of callback addresses's for each event + # _registeredCallbacks holds the functions to call for each address self._subscribedEvents = {} self._registeredCallbacks = {} - def _handle_signal(pickled_event): - event = pickle.loads(str(pickled_event)) - if self._registeredCallbacks.has_key(event): - for callback in self._registeredCallbacks[event].itervalues(): - callback(event) - - self._object.connect_to_signal("eventOccured", _handle_signal, dbus_interface="org.tutorius.ProbeInterface") - + self._object.connect_to_signal("eventOccured", self._handle_signal, dbus_interface="org.tutorius.ProbeInterface") + + def _handle_signal(self, pickled_event): + event = pickle.loads(str(pickled_event)) + logging.debug("ProbeProxy :: Received Event : %s %s", str(event), str(event._props.items())) + + logging.debug("ProbeProxy :: Currently %d events registered", len(self._registeredCallbacks)) + if self._registeredCallbacks.has_key(event): + for callback in self._registeredCallbacks[event].itervalues(): + callback(event) + else: + for event in self._registeredCallbacks.keys(): + logging.debug("==== %s", str(event._props.items())) + logging.debug("ProbeProxy :: Event does not appear to be registered") + def isAlive(self): try: return self._probe.ping() == "alive" except: return False - def install(self, action): + def __update_action(self, action, address): + self._actions[action] = str(address) + + def __clear_action(self, action): + self._actions.pop(action, None) + + def install(self, action, block=False): """ Install an action on the TProbe's activity @param action Action to install @return None """ - address = str(self._probe.install(pickle.dumps(action))) - self._actions[action] = address + remote_call(self._probe.install, (pickle.dumps(action),), + save_args(self.__update_action, action), + block=block) - def update(self, action): + def update(self, action, block=False): """ Update an already installed action's properties and run it again @param action Action to update @@ -308,46 +315,32 @@ class ProbeProxy: if not action in self._actions: raise RuntimeWarning("Action not installed") return - self._probe.update(self._actions[action], pickle.dumps(action._props)) + remote_call(self._probe.update, (self._actions[action], pickle.dumps(action._props)), block=block) - def uninstall(self, action): + def uninstall(self, action, block=False): """ Uninstall an installed action @param action Action to uninstall """ if action in self._actions: - self._probe.uninstall(self._actions.pop(action)) + remote_call(self._probe.uninstall,(self._actions.pop(action),), block=block) - def uninstall_all(self): + def uninstall_all(self, block=False): """ Uninstall all installed actions @return None """ for action in self._actions.keys(): - self.uninstall(action) - - def subscribe(self, event, callback): - """ - Register an event listener - @param event Event to listen for - @param callback callable that will be called when the event occurs - @return address identifier used for unsubscribing - """ - # TODO elavoie 2009-07-25 When we will allow for patterns both - # for event types and sources, we will need to revise the lookup - # mecanism for which callback function to call - if (event, callback) in self._events: - raise RuntimeError("event already registered for callback") - return + self.uninstall(action, block) + def __update_event(self, event, callback, address): + logging.debug("ProbeProxy :: Registered event %s with address %s", str(event), str(address)) # Since multiple callbacks could be associated to the same # event signature, we will store multiple callbacks # in a dictionary indexed by the unique address # given for this subscribtion and access this # dictionary from another one indexed by event - address = str(self._probe.subscribe(pickle.dumps(event))) - - self._events[(event, callback)] = address + address = str(address) # We use the event object as a key if not self._registeredCallbacks.has_key(event): @@ -371,19 +364,7 @@ class ProbeProxy: return address - def unsubscribe(self, event, callback): - """ - Unregister an event listener - @param address identifier given by subscribe() - @return None - """ - if not (event, callback) in self._events: - raise RuntimeWarning("callback/event not subscribed") - return - - address = self._events.pop((event, callback)) - self._probe.unsubscribe() - + def __clear_event(self, address): # Cleanup everything if self._subscribedEvents.has_key(address): event = self._subscribedEvents[address] @@ -397,13 +378,43 @@ class ProbeProxy: self._subscribedEvents.pop(address) - def unsubscribe_all(self): + def subscribe(self, event, callback, block=True): + """ + Register an event listener + @param event Event to listen for + @param callback callable that will be called when the event occurs + @return address identifier used for unsubscribing + """ + if not block: + raise RuntimeError("This function does not allow non-blocking mode yet") + + # TODO elavoie 2009-07-25 When we will allow for patterns both + # for event types and sources, we will need to revise the lookup + # mecanism for which callback function to call + return remote_call(self._probe.subscribe, (pickle.dumps(event),), + save_args(self.__update_event, event, callback), + block=block) + + def unsubscribe(self, address, block=False): + """ + Unregister an event listener + @param address identifier given by subscribe() + @return None + """ + if address in self._subscribedEvents.keys(): + remote_call(self._probe.unsubscribe, (address,), + return_cb=save_args(self.__clear_event, address), + block=block) + else: + logging.debug("ProbeProxy :: unsubsribe address %s failed : not registered", address) + + def unsubscribe_all(self, block=False): """ Unregister all event listeners @return None """ - for event, callback in self._events.keys(): - self.unsubscribe(event, callback) + for address in self._subscribedEvents.keys(): + self.unsubscribe(address, block) class ProbeManager(object): """ @@ -471,9 +482,9 @@ class ProbeManager(object): else: raise RuntimeWarning("No activity attached") - def unsubscribe(self, event, callback): + def unsubscribe(self, address): if self.currentActivity: - return self._probes[self.currentActivity].unsubscribe(event, callback) + return self._probes[self.currentActivity].unsubscribe(address) else: raise RuntimeWarning("No activity attached") |