diff options
Diffstat (limited to 'tutorius/TProbe.py')
-rwxr-xr-x | tutorius/TProbe.py | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/tutorius/TProbe.py b/tutorius/TProbe.py new file mode 100755 index 0000000..5280097 --- /dev/null +++ b/tutorius/TProbe.py @@ -0,0 +1,259 @@ +import gobject + +import dbus +import dbus.service +import dbus.mainloop.glib +import cPickle as pickle + +import sugar.tutorius.addon as addon + +from sugar.tutorius.overlayer import Overlayer +from sugar.tutorius.services import ObjectStore +from sugar.tutorius.events import ClickedEvent + +import copy + + +class TProbe(dbus.service.Object): + + def __init__(self, activity_name, activity): + + # Moving the ObjectStore assignment here, in the meantime + # the reference to the activity shouldn't be share as a + # global variable but passed by the Probe to the objects + # that requires it + self._activity = activity + + ObjectStore().activity = activity + + self._activity_name = activity_name + self._session_bus = dbus.SessionBus() + + # Giving a new name because _name is already used by dbus + self._name2 = dbus.service.BusName(activity_name, self._session_bus) + dbus.service.Object.__init__(self, self._session_bus, "/tutorius/Probe") + + # Add the dictionary we will use to store which actions and events + # are known + self._installedActions = {} + self._subscribedEvents = {} + + # optional method to call if the probe is not inserted into an existing + # activity + def start(self): + mainloop = gobject.MainLoop() + print "Starting Probe for " + self._activity_name + mainloop.run() + + @dbus.service.method("com.tutorius.ProbeInterface", + in_signature='s', out_signature='') + def registered(self, service): + print ("Registered with: " + str(service)) + + @dbus.service.method("com.tutorius.ProbeInterface", + in_signature='', out_signature='s') + def ping(self): + return "alive" + + # ------------------ Action handling -------------------------------------- + @dbus.service.method("com.tutorius.ProbeInterface", + in_signature='s', out_signature='s') + def install(self, pickled_action): + loaded_action = pickle.loads(str(pickled_action)) + action = addon.create(loaded_action.__class__.__name__) + + address = self._generate_action_reference(action) + + self._installedActions[address] = action + + if action._props: + action._props.update(loaded_action._props) + + action.do() + + return address + + @dbus.service.method("com.tutorius.ProbeInterface", + in_signature='ss', out_signature='') + def update(self, address, action_props): + action = self._installedActions[address] + + if action._props: + props = pickle.loads(str(action_props)) + action._props.update(props) + action.undo() + action.do() + + @dbus.service.method("com.tutorius.ProbeInterface", + in_signature='s', out_signature='') + def uninstall(self, address): + if self._installedActions.has_key(address): + action = self._installedActions[address] + action.undo() + self._installedActions.pop(address) + + + # ------------------ Event handling --------------------------------------- + @dbus.service.method("com.tutorius.ProbeInterface", + in_signature='s', out_signature='s') + def subscribe(self, pickled_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 + + # 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) + + eventfilter.install_handlers(callback, activity=self._activity) + + name = self._generate_event_reference(event) + self._subscribedEvents[name] = eventfilter + + return name + + @dbus.service.method("com.tutorius.ProbeInterface", + in_signature='s', out_signature='') + def unsubscribe(self, address): + if self._subscribedEvents.has_key(address): + eventfilter = self._subscribedEvents[address] + eventfilter.remove_handlers() + self._subscribedEvents.pop(address) + + @dbus.service.signal("com.tutorius.ProbeInterface") + def eventOccured(self, event): + # We need no processing now, the signal will be sent + # when the method exit + pass + + # The actual method we will call on the probe to send events + def notify(self, event): + self.eventOccured(pickle.dumps(event)) + + # Return a unique name for this action + def _generate_action_reference(self, action): + # TODO elavoie 2009-07-25 Should return a universal address + name = action.__class__.__name__ + suffix = 1 + + while self._installedActions.has_key(name+str(suffix)): + suffix += 1 + + return name + str(suffix) + + + # 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 + suffix = 1 + + while self._subscribedEvents.has_key(name+str(suffix)): + suffix += 1 + + return name + str(suffix) + + + +class ProbeProxy: + def __init__(self, activityName): + bus = dbus.SessionBus() + self._object = bus.get_object(activityName, "/tutorius/Probe") + self._probe = dbus.Interface(self._object, "com.tutorius.ProbeInterface") + + # We keep those two data structures to be able to have multiple callbacks + # for the same event and be able to remove them independently + 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="com.tutorius.ProbeInterface") + + def install(self, action): + address = str(self._probe.install(pickle.dumps(action))) + return address + + def update(self, address, action): + self._probe.update(address, pickle.dumps(action._props)) + + def uninstall(self, address): + self._probe.uninstall(address) + + def subscribe(self, event, callback): + # 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 + + + # 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))) + + # We use the event object as a key + if not self._registeredCallbacks.has_key(event): + self._registeredCallbacks[event] = {} + + # TODO elavoie 2009-07-25 decide on a proper exception + # taxonomy + if self._registeredCallbacks[event].has_key(address): + # Oups, how come we have two similar addresses? + # send the bad news! + raise Exception("Probe subscribe exception, the following address already exists: " + str(address)) + + self._registeredCallbacks[event][address] = callback + + # We will keep another dictionary to remember the + # event that was associated to this unique address + # Let's copy to make sure that even if the event + # passed in is modified later it won't screw up + # our dictionary (python pass arguments by reference) + self._subscribedEvents[address] = copy.copy(event) + + return address + + def unsubscribe(self, address): + self._probe.unsubscribe(address) + + # Cleanup everything + if self._subscribedEvents.has_key(address): + event = self._subscribedEvents[address] + + if self._registeredCallbacks.has_key(event)\ + and self._registeredCallbacks[event].has_key(address): + self._registeredCallbacks[event].pop(address) + + if self._registeredCallbacks[event] == {}: + self._registeredCallbacks.pop(event) + + self._subscribedEvents.pop(address) + +if __name__ == '__main__': + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + probe = TProbe("com.tutorius.TBus") + + |