From 1aa26594ad7f55cf91fbd140b006c062c247fb13 Mon Sep 17 00:00:00 2001 From: Vincent Vinet Date: Thu, 12 Feb 2009 05:04:41 +0000 Subject: Add the tutorius module to sugar-toolkit --- diff --git a/src/sugar/Makefile.am b/src/sugar/Makefile.am index fb87bf6..c3eeeaf 100644 --- a/src/sugar/Makefile.am +++ b/src/sugar/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = activity bundle graphics presence datastore +SUBDIRS = activity bundle graphics presence datastore tutorius sugardir = $(pythondir)/sugar sugar_PYTHON = \ diff --git a/src/sugar/tutorius/Makefile.am b/src/sugar/tutorius/Makefile.am new file mode 100644 index 0000000..0c85c33 --- /dev/null +++ b/src/sugar/tutorius/Makefile.am @@ -0,0 +1,5 @@ +sugardir = $(pythondir)/sugar/tutorius +sugar_PYTHON = \ + __init__.py \ + tutorial.py \ + dialog.py diff --git a/src/sugar/tutorius/__init__.py b/src/sugar/tutorius/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/sugar/tutorius/__init__.py diff --git a/src/sugar/tutorius/dialog.py b/src/sugar/tutorius/dialog.py new file mode 100644 index 0000000..d15cc52 --- /dev/null +++ b/src/sugar/tutorius/dialog.py @@ -0,0 +1,39 @@ +# Copyright (C) 2009, Tutorius.org +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +class TutoriusDialog(gtk.Dialog): + def __init__(self, label="Hint", button_clicked_cb=None, response_cb=None): + gtk.Dialog.__init__(self) + + self._button = gtk.Button(label) + + self.add_action_widget(self._button, 1) + + if not button_clicked_cb == None: + self._button.connect("clicked", button_clicked_cb) + + self._button.show() + + if not response_cb == None: + self.connect("response", response_cb) + + self.set_decorated(False) + + def setButtonClickedCallback(self, funct): + self._button.connect("clicked", funct) + + def closeSelf(self, Arg=None): + self.destroy() diff --git a/src/sugar/tutorius/tutorial.py b/src/sugar/tutorius/tutorial.py new file mode 100644 index 0000000..5236127 --- /dev/null +++ b/src/sugar/tutorius/tutorial.py @@ -0,0 +1,162 @@ +# Copyright (C) 2009, Tutorius.org +# Copyright (C) 2009, Vincent Vinet +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +import gtk +import logging + +from sugar.tutorius.dialog import TutoriusDialog + + +logger = logging.getLogger("tutorius") + +class Event: + def __init__(self, object_name, event_name ): + self.object_name = object_name + self.event_name = event_name + + def test(self, sig, name): + if self.object_name == name and self.event_name == sig: + return True + return False + + +class Tutorial (object): + 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): + object.__init__(self) + self.name = name + self.state_machine = fsm + + self.handlers = [] + self.activity = None + #self.setState("INIT") + #self.state="INIT" + #self.register_signals(self.activity, self.handleEvent, max_depth=10) + + def attach(self, activity): + #For now, absolutely detach if a previous one! + if self.activity: + self.detach() + self.activity = activity + self.state="INIT" + self.register_signals(self.activity,self.handleEvent, max_depth=10) + + def detach(self): + self.disconnectHandlers() + self.activity = None + + def handleEvent(self, *args): + 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): + logger.debug("====NEW STATE: %s====" % next) + self.state = next + dlg = TutoriusDialog(self.state_machine[self.state]["Message"]) + dlg.setButtonClickedCallback(dlg.closeSelf) + dlg.run() + +# @staticmethod +# def logEvent(obj, *args): +# logger.debug("%s" % str(args[-1])) + + def disconnectHandlers(self): + for t, id in self.handlers: + t.disconnect_handler(id) + +# def setState(self,name): +# self.disconnectHandlers() +# self.state = name +# newstate = ABIWORD_MEF.get(name,()) +# for event, n in newstate: +# target = self.activity +# try: +# for obj in event.object_name.split("."): +# target = getattr(target,obj) +# id = target.connect(self.handler,(event.object_name, event.event_name)) +# self.handlers.append(target, id) +# id = target.connect(Tutorial.logEvent,"EVENT %s ON %s" % (event.object_name, event.event_name)) +# self.handlers.append(target, id) +# except Exception, e: +# logger.debug(str(e)) + + def register_signals(self,object,handler,prefix=None,max_depth=None): + """ + Recursive function to register event handlers on an object + 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 object 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. + + Example arg tuple added: + ("focus", "Activity.Toolbox.Bold") + Side effects: + -Handlers connected on the various objects + -Handler ID's stored in self.handlers + + @param object the object to recurse on + @param handler the handler function to connect + @param prefix name prepended to the object name to form a chain + @param max_depth maximum recursion depth, None for infinity + """ + #Gtk Containers have a get_children() function + if hasattr(object,"get_children") and \ + hasattr(object.get_children,"__call__"): + for child in object.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, object.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, object.get_name()) \ + if not (p is None or p in Tutorial.IGNORED_WIDGETS)] \ + ) + #register events on the object if a widget XXX necessary to check this? + if isinstance(object,gtk.Widget): + for sig in Tutorial.EVENTS: + try: + self.handlers.append( (object,object.connect(sig,handler,(sig, name) )) ) + except TypeError: + continue + + -- cgit v0.9.1