From 7019bbb21cf345a25afe87c148e5651aef3dd5c2 Mon Sep 17 00:00:00 2001 From: Vincent Vinet Date: Sat, 28 Feb 2009 01:54:39 +0000 Subject: -Replace the current event classes to use EVentFilters and callbacks onto the Tutorial claa -Wow this is an awesome commit, seriously! --- (limited to 'src/sugar/tutorius/core.py') diff --git a/src/sugar/tutorius/core.py b/src/sugar/tutorius/core.py index 62caee0..45eee3a 100644 --- a/src/sugar/tutorius/core.py +++ b/src/sugar/tutorius/core.py @@ -25,63 +25,14 @@ import gtk import logging from sugar.tutorius.dialog import TutoriusDialog - +from sugar.tutorius.gtkutils import find_widget logger = logging.getLogger("tutorius") -class Event: - """Event descriptor class. - This class is used to describe events that are expected to happen. - """ - - def __init__(self, object_name, event_name ): - """Constructor for Event - @param object_name str name of the object that will send the event - @param event_name str name of the event - - Example: - evt=Event("0.0.1.1.2", "clicked") - """ - self.object_name = object_name - self.event_name = event_name - - def test(self, sig, name): - """Utility method for testing the equality between a signal and object - names and their expected values. - @param sig str signal name - @param name str object name - @return True if both match the expected values, False otherwise - """ - if self.object_name == name and self.event_name == sig: - return True - return False - - - - class Tutorial (object): """ Tutorial Class, used to run through the FSM. """ - EVENTS = [ - "focus", - "button-press-event", - "enter-notify-event", - "leave-notify-event", - "key-press-event", - "text-selected", - "clicked", - ] - - IGNORED_WIDGETS = [ - "GtkVBox", - "GtkHBox", - "GtkAlignment", - "GtkNotebook", - "GtkButton", - "GtkToolItem", - "GtkToolbar", - ] def __init__(self, name, fsm): """Create an unattached tutorial @@ -107,34 +58,17 @@ class Tutorial (object): def detach(self): """Detach from the current activity""" - self.disconnect_handlers() - self.activity = None - - def handle_event(self, *args): - """Default event handler for the Tutorial. - Tests the received object and signal names onto each defined - transition and changes to the next state if successful. - - The last parameter should be a two-tuple containing the - (signal_name, object_name) - """ - sig, objname = args[-1] - logger.debug("EVENT %s ON %s" % (sig, objname) ) - for transition, next in self.state_machine[self.state]["Events"]: - if transition.test(sig, objname): - self.set_state(next) + #Remove handlers + for eventfilter in self.state_machine.get(self.state,{}).get("EventFilters",()): + eventfilter.remove_handlers() -# @staticmethod -# def logEvent(obj, *args): -# logger.debug("%s" % str(args[-1])) - - def disconnect_handlers(self): - """Disconnect all event handlers attached by self""" - #Loop through handlers - for obj, hid in self.handlers: - obj.handler_disconnect(hid) - self.handlers = [] + #Undo actions + for act in self.state_machine.get(self.state,{}).get("Actions",()): + act.undo() + #FIXME There should be some amount of resetting done here... + self.activity = None + def set_state(self, name): """Switch to a new state""" @@ -142,8 +76,10 @@ class Tutorial (object): return logger.debug("====NEW STATE: %s====" % name) - #Remove handlers (TODO replace by EventFilter unregister) - self.disconnect_handlers() + #Remove handlers + for eventfilter in self.state_machine.get(self.state,{}).get("EventFilters",()): + eventfilter.remove_handlers() + #Undo actions for act in self.state_machine.get(self.state,{}).get("Actions",()): act.undo() @@ -151,131 +87,38 @@ class Tutorial (object): #Switch to new state self.state = name newstate = self.state_machine.get(name) - #Add handlers (TODO replace by EventFilter register) - for event, unused in newstate["Events"]: - self.register_signal(self.handle_event, \ - event.object_name, event.event_name) - + + #Register handlers for eventfilters + for eventfilter in newstate["EventFilters"]: + eventfilter.install_handlers(self._eventfilter_state_done, + activity=self.activity) + #Do actions for act in newstate.get("Actions",()): act.do() - def register_signals(self, target, handler, prefix=None, max_depth=None): - """ - Recursive function to register event handlers on an target - and it's children. The event handler is called with an extra - argument which is a two-tuple containing the signal name and - the FQDN-style name of the target that triggered the event. - - This function registers all of the events listed in - Tutorial.EVENTS and omits widgets with a name matching - Tutorial.IGNORED_WIDGETS from the name hierarchy. + def _eventfilter_state_done(self, eventfilter): + """Callback handler for eventfilter to notify + when we must go to the next state.""" + #XXX Tests should be run here normally - Example arg tuple added: - ("focus", "Activity.Toolbox.Bold") - Side effects: - -Handlers connected on the various targets - -Handler ID's stored in self.handlers + #Swith to the next state pointed by the eventfilter + self.set_state(eventfilter.get_next_state()) - @param target the target to recurse on - @param handler the handler function to connect - @param prefix name prepended to the target name to form a chain - @param max_depth maximum recursion depth, None for infinity - """ - #Gtk Containers have a get_children() function - if hasattr(target, "get_children") and \ - hasattr(target.get_children, "__call__"): - for child in target.get_children(): - if max_depth is None or max_depth > 0: - #Recurse with a prefix on all children - pre = ".".join( \ - [p for p in (prefix, target.get_name()) \ - if not (p is None or p in Tutorial.IGNORED_WIDGETS)] \ - ) - self.register_signals(child, handler, pre, max_depth-1) - name = ".".join( \ - [p for p in (prefix, target.get_name()) \ - if not (p is None or p in Tutorial.IGNORED_WIDGETS)] \ - ) - #register events on the target if a widget XXX necessary to check this? - if isinstance(target, gtk.Widget): - for sig in Tutorial.EVENTS: - try: - self.handlers.append( \ - (target, target.connect(sig, handler, (sig, name) )) \ - ) - except TypeError: - continue - - def register_signals_numbered(self, \ - target, handler, prefix="0", max_depth=None): - """ - Recursive function to register event handlers on an target - and it's children. The event handler is called with an extra - argument which is a two-tuple containing the signal name and - the FQDN-style name of the target that triggered the event. - - This function registers all of the events listed in - Tutorial.EVENTS - - Example arg tuple added: - ("focus", "1.1.2") - Side effects: - -Handlers connected on the various targets - -Handler ID's stored in self.handlers - - @param target the target to recurse on - @param handler the handler function to connect - @param prefix name prepended to the target name to form a chain - @param max_depth maximum recursion depth, None for infinity - """ - #Gtk Containers have a get_children() function - if hasattr(target, "get_children") and \ - hasattr(target.get_children, "__call__"): - children = target.get_children() - for i in range(len(children)): - child = children[i] - if max_depth is None or max_depth > 0: - #Recurse with a prefix on all children - pre = ".".join( \ - [p for p in (prefix, str(i)) if not p is None] - ) - if max_depth is None: - dep = None - else: - dep = max_depth - 1 - self.register_signals_numbered(child, handler, pre, dep) - #register events on the target if a widget XXX necessary to check this? - if isinstance(target, gtk.Widget): - for sig in Tutorial.EVENTS: - try: - self.handlers.append( \ - (target, target.connect(sig, handler, (sig, prefix) ))\ - ) - except TypeError: - continue - - def register_signal(self, handler, obj_fqdn, signal_name): - """Register a signal handler onto a specific widget - @param handler function to attach as a handler - @param obj_fqdn fqdn-style object name - @param signal_name signal name to connect to - - Side effects: - the object found and the handler id obtained by connect() are - appended in self.handlers - """ - path = obj_fqdn.split(".") - #We select the first object and pop the first zero - obj = self.activity - path.pop(0) - - while len(path) > 0: - obj = obj.get_children()[int(path.pop(0))] - - self.handlers.append( \ - (obj, obj.connect(signal_name, handler, (signal_name, obj_fqdn) ))\ - ) +# def register_signal(self, handler, obj_fqdn, signal_name): +# """Register a signal handler onto a specific widget +# @param handler function to attach as a handler +# @param obj_fqdn fqdn-style object name +# @param signal_name signal name to connect to +# +# Side effects: +# the object found and the handler id obtained by connect() are +# appended in self.handlers +# """ +# obj = find_widget(self.activity, obj_fqdn) +# self.handlers.append( \ +# (obj, obj.connect(signal_name, handler, (signal_name, obj_fqdn) ))\ +# ) class State: """This is a step in a tutorial. The state represents a collection of -- cgit v0.9.1