From fb422aef7ee6832c85c8fa8a703e491838e74d62 Mon Sep 17 00:00:00 2001 From: Vincent Vinet Date: Fri, 04 Dec 2009 05:06:49 +0000 Subject: Add Event Sources: - Add source property in Action and EventFilter - Change TPropContainer contructor to accept keyword arguments and set properties that were given - Change every single TPropContainer subclass constructor to accept kwargs and pass them on to super init - Add a "null" option for TStringProperty Use Event Sources: - Make the probe require a source property to install or subscribe - Have ProbeProxy install and subscribe return a prefixed address - Make update, uninstall and unsubsribe extract the prefix from the address - Have the TutorialRunner set a source on actions/events before installing/subscribing instead of setting current activity on ProbeManager Test Event Sources: - Change the tests according to the new constructors and behaviors --- (limited to 'tutorius') diff --git a/tutorius/TProbe.py b/tutorius/TProbe.py index acba26f..2f99329 100644 --- a/tutorius/TProbe.py +++ b/tutorius/TProbe.py @@ -47,6 +47,10 @@ import copy -------------------- ---------- """ + +#Prefix separator for action/event addresses +PSEP=":" + #TODO Add stub error handling for remote calls in the classes so that it will # be clearer how errors can be handled in the future. @@ -429,6 +433,7 @@ class ProbeProxy: bus = dbus.SessionBus() self._object = bus.get_object(activityName, "/tutorius/Probe/"+str(unique_id)) self._probe = dbus.Interface(self._object, "org.tutorius.ProbeInterface") + self.prefix = str(unique_id)+PSEP self._actions = {} self._edition_callbacks = {} @@ -480,7 +485,7 @@ class ProbeProxy: if editing_cb: self._edition_callbacks[address] = editing_cb # Propagate the action installed callback upwards in the stack - callback(address) + callback(self.prefix + address) def __clear_action(self, address): # Remove the action installed at this address @@ -563,7 +568,7 @@ class ProbeProxy: # our dictionary (python pass arguments by reference) self._subscribedEvents[address] = copy.copy(event) - event_subscribed_cb(address) + event_subscribed_cb(self.prefix + address) return address def __clear_event(self, address): @@ -679,6 +684,12 @@ class ProbeManager(object): currentActivity = property(fget=getCurrentActivity, fset=setCurrentActivity) + def get_source_activity(self, propc): + if hasattr(propc, "source"): + return propc.source + else: + return None + def install(self, action, action_installed_cb, error_cb, is_editing=False, editing_cb=None): """ Install an action on the current activity @@ -691,8 +702,13 @@ class ProbeManager(object): this action (only used when is_editing is true) @return None """ - if self.currentActivity: - return self._first_proxy(self.currentActivity).install( + activity = self.get_source_activity(action) + #Allow the creator to look for the current activity + if activity is None and is_editing: + activity = self.currentActivity + + if activity: + return self._first_proxy(activity).install( action=action, is_editing=is_editing, action_installed_cb=action_installed_cb, @@ -710,8 +726,15 @@ class ProbeManager(object): @param is_editing whether this action comes from the editor @return None """ - if self.currentActivity: - return self._first_proxy(self.currentActivity).update(action_address, newaction, is_editing) + probe_id, sep, address = action_address.rpartition(PSEP) + if probe_id: + probe = self._get_proxy_by_unique_id(probe_id) + if probe is None: + #TODO What happens if the Probe is gone?? + raise RuntimeWarning("ProbeProxy containing action address is gone") + else: + return probe.update(address, newaction, is_editing) + else: raise RuntimeWarning("No activity attached") @@ -722,8 +745,16 @@ class ProbeManager(object): @param block Force a synchroneous dbus call if True @param is_editing whether this action comes from the editor """ - if self.currentActivity: - return self._first_proxy(self.currentActivity).uninstall(action_address, is_editing) + probe_id, sep, address = action_address.rpartition(PSEP) + if probe_id: + probe = self._get_proxy_by_unique_id(probe_id) + if probe is None: + logging.warning( + "ProbeProxy for address %s is gone, assuming uninstall not necessary" % \ + action_address) + else: + return probe.uninstall(address, is_editing) + else: raise RuntimeWarning("No activity attached") @@ -751,8 +782,10 @@ class ProbeManager(object): installation @return address identifier used for unsubscribing """ - if self.currentActivity: - return self._first_proxy(self.currentActivity).subscribe(event, notification_cb,\ + activity = self.get_source_activity(event) + + if activity: + return self._first_proxy(activity).subscribe(event, notification_cb,\ event_subscribed_cb, error_cb) else: raise RuntimeWarning("No activity attached") @@ -763,8 +796,15 @@ class ProbeManager(object): @param address identifier given by subscribe() @return None """ - if self.currentActivity: - return self._first_proxy(self.currentActivity).unsubscribe(address) + probe_id, sep, address = address.rpartition(PSEP) + if probe_id: + probe = self._get_proxy_by_unique_id(probe_id) + if probe is None: + logging.warning( + "ProbeProxy for address %s is gone, assuming unsubscribe not necessary" % \ + address) + else: + return probe.unsubscribe(address) else: raise RuntimeWarning("No activity attached") @@ -825,4 +865,14 @@ class ProbeManager(object): else: raise RuntimeWarning("No activity attached under '%s'", process_name) - + def _get_proxy_by_unique_id(self, unique_id): + """ + Get a probe proxy by it's unique id. + @param unique_id The unique id of the probe + @return the probe proxy or None if not found + """ + for probes in self._probes.values(): + for id, probe in probes: + if id == unique_id: + return probe + return None diff --git a/tutorius/actions.py b/tutorius/actions.py index cf586f2..6d1f58e 100644 --- a/tutorius/actions.py +++ b/tutorius/actions.py @@ -165,9 +165,9 @@ class DragWrapper(object): class Action(TPropContainer): """Base class for Actions""" - def __init__(self): - TPropContainer.__init__(self) - self.position = (0,0) + source = TStringProperty(None, null=True) + def __init__(self, **kwargs): + super(Action, self).__init__(**kwargs) self._drag = None # The callback that will be triggered when the action is requested # to notify all its changes diff --git a/tutorius/engine.py b/tutorius/engine.py index 198fa11..c0769b5 100644 --- a/tutorius/engine.py +++ b/tutorius/engine.py @@ -77,14 +77,9 @@ class TutorialRunner(object): #Temp FIX until event/actions have an activity id self._activity_id = None - #Temp FIX until event, actions have an activity id - def setCurrentActivity(self): - self._pM.currentActivity = self._activity_id - ########################################################################### # Incoming messages def start(self): - self.setCurrentActivity() #Temp Hack until activity in events/actions self.enterState(self._tutorial.INIT) def stop(self): @@ -141,6 +136,8 @@ class TutorialRunner(object): # Send all the event registration for (event_name, (event, next_state)) in transitions.items(): + if hasattr(event, "source") and not event.source: + event.source = self._activity_id self._pM.subscribe(event, save_args(self._handleEvent, next_state), save_args(self.event_subscribed, event_name), @@ -153,7 +150,6 @@ class TutorialRunner(object): ########################################################################### # Helper functions def _execute_stop(self): - self.setCurrentActivity() #Temp Hack until activity in events/actions self._teardownState() self._state = None self._runner_state = RUNNER_STATE_IDLE @@ -253,6 +249,8 @@ class TutorialRunner(object): for (action_name, action) in actions.items(): LOGGER.debug("TutorialRunner :: Installed action %s"%(action_name)) + if hasattr(action, "source") and not action.source: + action.source = self._activity_id self._pM.install(action, save_args(self.action_installed, action_name), save_args(self.install_error, action_name)) @@ -269,7 +267,6 @@ class TutorialRunner(object): @param state_name The name of the state to enter in """ - self.setCurrentActivity() #Temp Hack until activity in events/actions # Set the runner state to actions setup self._runner_state = RUNNER_STATE_SETUP_ACTIONS diff --git a/tutorius/filters.py b/tutorius/filters.py index 38cf86b..6ef8867 100644 --- a/tutorius/filters.py +++ b/tutorius/filters.py @@ -25,12 +25,12 @@ class EventFilter(properties.TPropContainer): """ Base class for an event filter """ - - def __init__(self): + source = properties.TStringProperty(None, null=True) + def __init__(self, **kwargs): """ Constructor. """ - super(EventFilter, self).__init__() + super(EventFilter, self).__init__(**kwargs) self._callback = None def install_handlers(self, callback, **kwargs): diff --git a/tutorius/properties.py b/tutorius/properties.py index bfdb32c..a0d63bb 100644 --- a/tutorius/properties.py +++ b/tutorius/properties.py @@ -49,12 +49,14 @@ class TPropContainer(object): at the cost of needing a mapping between container instances, and property values. This is what TPropContainer does. """ - def __init__(self): + def __init__(self, **kwargs): """ Prepares the instance for property value storage. This is done at object initialization, thus allowing initial mapping of properties declared on the class. Properties won't work correctly without this call. + + Keyword arguments will be evaluated as properties """ # create property value storage object.__setattr__(self, "_props", {}) @@ -74,6 +76,10 @@ class TPropContainer(object): # to the creator to update its action edition dialog. self._diff_dict = {} + #Set attribute values that were supplied + for key, value in kwargs.items(): + setattr(self, key, value) + def __getattribute__(self, name): """ Process the 'fake' read of properties in the appropriate instance @@ -254,11 +260,15 @@ class TStringProperty(TutoriusProperty): Represents a string. Can have a maximum size limit. """ widget_class = StringPropWidget - def __init__(self, value, size_limit=None): + def __init__(self, value, size_limit=None, null=False): TutoriusProperty.__init__(self) self.type = "string" - self.size_limit = MaxSizeConstraint(size_limit) - self.string_type = StringTypeConstraint() + if size_limit: + self.size_limit = MaxSizeConstraint(size_limit) + if null: + self.string_type = TypeConstraint((str, type(None))) + else: + self.string_type = StringTypeConstraint() self.default = self.validate(value) -- cgit v0.9.1