Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/tutorius/TProbe.py
diff options
context:
space:
mode:
Diffstat (limited to 'tutorius/TProbe.py')
-rwxr-xr-xtutorius/TProbe.py259
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")
+
+