Autowire menus from applet paths
authorEnrico Zini <enrico@enricozini.org>
Sun, 14 Jun 2009 14:17:35 +0000 (22:17 +0800)
committerEnrico Zini <enrico@enricozini.org>
Sun, 14 Jun 2009 14:17:35 +0000 (22:17 +0800)
plugins/50_sat_monitor.py
plugins/99_debug.py
src/zavai
zavai/__init__.py
zavai/app.py
zavai/menu.py
zavai/registry.py

index 47458dcf92fa862d3bdc0c8cf42918b7d0a55d49..a3ea63d49d26dcffffbc2467b593762f12a7941d 100644 (file)
@@ -18,8 +18,7 @@
 
 import gtk
 import sys
-import gettext
-_ = gettext.gettext
+from gettext import gettext as _
 import zavai
 
 SAT_QI_NAMES = {
@@ -33,9 +32,10 @@ SAT_QI_NAMES = {
     7: _("receiving data")
 }
 
-class SatelliteMonitor(gtk.VBox, zavai.Resource):
+class SatelliteMonitor(gtk.VBox, zavai.Applet):
     def __init__(self, registry, name, **kw):
-        super(SatelliteMonitor, self).__init__()
+        zavai.Applet.__init__(self, registry, name)
+        gtk.VBox.__init__(self)
 
         self.gps = registry.resource("gps")
 
@@ -51,14 +51,8 @@ class SatelliteMonitor(gtk.VBox, zavai.Resource):
             col.add_attribute(renderer, "text", idx)
             self.view.append_column(col)
 
-        self.back = registry.menu_link("gps", _("Back"))
-        self.back.connect("clicked", self.stop)
-
         self.pack_start(self.view, True, True)
-        self.pack_start(self.back, False, False)
-
-    def shutdown(self):
-        self.stop()
+        self.pack_start(self.make_parent_link, False, False)
 
     def start(self, *args):
         self.gps.monitor.connect(self.on_ubxdebug_packet)
@@ -104,19 +98,5 @@ class SatelliteMonitor(gtk.VBox, zavai.Resource):
                 bad and "bad" or "",
                 qi])
 
-def start_monitor(registry):
-    monitor = registry.resource("app.satellite_monitor")
-    registry.resource("app").show_widget("app.satellite_monitor")
-    monitor.start()
-
 def init(conf = None, registry = None, **kw):
-    registry.register("app.satellite_monitor", SatelliteMonitor)
-    menu_gps = registry.menu("main.gps")
-
-    monitor = zavai.MenuButton(_("Monitor"))
-    monitor.connect("clicked", lambda *args: start_monitor(registry))
-    menu_gps.add_child(monitor)
-
-    # TODO: automate this in registry
-    registry.menu("main").add_child(registry.menu_link("main.gps", _("GPS")))
-
+    registry.register_factory("menu.main.gps.satellite_monitor", SatelliteMonitor, _("Satellite monitor"))
index b7801e0e0d698a778ed09cb1a5335dea3a7cdb5a..6ddc17621f8359db2999dbafc1307cd287d369fd 100644 (file)
 
 import gtk
 import sys
-import gettext
-_ = gettext.gettext
+from gettext import gettext as _
 import zavai
 
-def init(conf = None, registry = None, **kw):
-    debug = registry.menu("main.debug")
-
-    quit = zavai.MenuButton(_("Quit"))
-    quit.connect("clicked", gtk.main_quit)
-    debug.add_child(quit)
+class Quitter(gtk.Label, zavai.Applet):
+    def __init__(self, registry, name, **kw):
+        zavai.Applet.__init__(self, registry, name)
+        gtk.Label.__init__(self, _("Shutting down..."))
 
-    registry.menu("main").add_child(registry.menu_link("main.debug", _("Debug")))
+    def start(self, *args):
+        gtk.main_quit()
 
+def init(conf = None, registry = None, **kw):
+    registry.register_factory("menu.main.debug.quit", Quitter, _("Quit"))
index 3adaa562c57118f3e2fe529bf7f82b2f5958daf0..897dd7f15f2707dd43a9f85bf02ac9e6d6e2993e 100755 (executable)
--- a/src/zavai
+++ b/src/zavai
@@ -61,11 +61,10 @@ dbus_system_bus = dbus.SystemBus()
 registry = zavai.Registry()
 
 # Register main factories
-registry.register("dbus.system_bus", lambda *args, **kw: dbus_system_bus)
-registry.register("conf", lambda *args, **kw: conf)
-registry.register("app", zavai.Zavai)
-registry.register("menu", zavai.Menu)
-registry.register("gps", zavai.GPS)
+registry.register("dbus.system_bus", dbus_system_bus)
+registry.register("conf", conf)
+registry.register_factory("app", zavai.Zavai)
+registry.register_factory("gps", zavai.GPS)
 
 # Load plugins
 zavai.info("Loading plugins")
index b349c43c3d8fc861c7d1969c87c53ec9a4960fb0..0acc4774cbf2f7700f1861fff029f0ed4e25870e 100644 (file)
@@ -18,9 +18,9 @@
 
 from conf import read_config
 from plugins import load_plugins
-from registry import Registry, Resource
-from menu import Menu, MenuLink, MenuButton
-from app import Zavai
+from registry import Registry, Resource, get_parent
+from menu import Menu, MenuButton, LinkButton
+from app import Zavai, Applet
 from gps import GPS, GPX
 
 def warn(*args):
index 1d902ce4a229753121bd4cbf9288b9dd33b9a82a..3b700854dc66669c59d255cbe2a3bcc7a2be0e06 100644 (file)
@@ -24,14 +24,27 @@ class Zavai(gtk.Window, zavai.Resource):
     def __init__(self, registry, name):
         super(Zavai, self).__init__()
         self.registry = registry
-        self.add(registry.menu("main"))
+        self.current = None
+        self.show_widget("menu.main")
 
     def show_widget(self, name):
-        widget = self.registry.get_existing(name)
+        # Remove the current widget.
+        # If it is an Applet, stop it
+        if self.current is not None:
+            cur = self.registry.resource(self.current)
+            if isinstance(cur, zavai.Applet):
+                cur.stop()
+            self.remove(self.get_child())
+            self.current = None
+
+        # Add the new widget. If it is an applet, start it
+        widget = self.registry.resource(name)
         if widget is None:
-            widget = self.registry.menu("main")
-        self.remove(self.get_child())
+            widget = self.registry.resource("menu.main")
         self.add(widget)
+        self.current = name
+        if isinstance(widget, zavai.Applet):
+            widget.start()
         widget.show_all()
 
     def run(self):
@@ -39,3 +52,20 @@ class Zavai(gtk.Window, zavai.Resource):
         self.show_all()
         gtk.main()
 
+class Applet(zavai.Resource):
+    def __init__(self, registry, name):
+        super(Applet, self).__init__()
+        self.zavai_registry = registry
+        self.zavai_name = name
+
+    def make_parent_link(self):
+        return zavai.LinkButton(self.zavai_registry, zavai.get_parent(self.zavai_name), _("Back"))
+
+    def shutdown(self):
+        self.stop()
+
+    def start(self, *args):
+        pass
+
+    def stop(self, *args):
+        pass
index b1ff0e1221c762d1ea7c423126682f9b0ac2181e..424ee07bfcdd32b7d36a23714219a4f42be63c1a 100644 (file)
@@ -17,6 +17,7 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 import sys
+from gettext import gettext as _
 import zavai
 import gtk
 
@@ -27,30 +28,23 @@ class MenuButton(gtk.Button):
         self.set_size_request(0, 80)
 
 class Menu(gtk.VBox, zavai.Resource):
-    def __init__(self, registry, name, *args, **kw):
+    def __init__(self, registry, name, parent = None, *args, **kw):
         super(Menu, self).__init__()
         self.vbox = self
 
-        parent = None
-        pos = name.rfind(".")
-        if pos != -1:
-            parent = name[:pos]
-            if parent == "menu":
-                parent = None
-
         if parent is not None:
             self.vbox = gtk.VBox()
             self.pack_start(self.vbox, False, False)
             self.pack_start(gtk.Label(""), True, True)
-            self.pack_start(registry.menu_link(parent, "Back"), False, False)
+            self.pack_start(LinkButton(registry, parent, _("Back")), False, False)
 
     def add_child(self, widget):
         self.vbox.pack_start(widget, False, False)
 
-class MenuLink(MenuButton, zavai.Resource):
+class LinkButton(MenuButton, zavai.Resource):
     def __init__(self, registry, targetName, label):
-        super(MenuLink, self).__init__(label)
-        self.target = "menu." + targetName
+        super(LinkButton, self).__init__(label)
+        self.target = targetName
         self.registry = registry
         self.connect("clicked", self.on_click)
 
index 304ec8e2a35a9b3a5de28946fc7e64e4087d9c0a..ac46759b885a7b73e1cf28143b05c334edb5a474 100644 (file)
 
 import zavai
 
+def get_parent(s):
+    "Get the parent name for s"
+    pos = s.rfind(".")
+    if pos == -1: return None
+    res = s[:pos]
+    if res == "menu": return None
+    return res
+
+def default_label(s):
+    "Compute a default label given the last element of a path"
+    pos = s.rfind(".")
+    if pos == -1: return s.capitalize()
+    return s[pos+1:].capitalize()
+
+
 class Registry(object):
     """Collection of resources.
 
@@ -29,64 +44,85 @@ class Registry(object):
     def __init__(self):
         self.factories = dict()
         self.objects = dict()
+        self.labels = dict()
+
+    def register(self, name, obj, label = None):
+        """Register an object at the given path.
+
+        Name the path to this object, like "menu.gps.monitor".
+        """
+        if name in self.objects:
+            return KeyError("%s is already registered", name)
+        zavai.info("Registering", name)
+        self.objects[name] = obj
+        if label is not None: self.labels[name] = label
+
+        if name.startswith("menu."):
+            self.add_to_menu(name)
+
+    def register_factory(self, name, fac, label = None):
+        """Register an object factory at the given path.
+
+        Name the path to this object, like "menu.gps.monitor".
+        """
+        if name in self.factories:
+            return KeyError("Factory %s is already registered", name)
+        zavai.info("Registering factory", name)
+        self.factories[name] = fac
+        if label is not None: self.labels[name] = label
+
+        if name.startswith("menu."):
+            self.add_to_menu(name)
+
+    def add_to_menu(self, name):
+        "Add the applet with the given name to the menu structure"
+        parent = get_parent(name)
+        if parent is not None:
+            zavai.info("Add to menu", name, parent)
+            menu = self.menu(parent)
+            menu.add_child(zavai.LinkButton(self, name, self.label(name)))
+
+    def label(self, name):
+        "Return the label for the object with the given name"
+        res = self.labels.get(name)
+        if res is not None:
+            return res
+        return default_label(name)
 
     def resource(self, name):
-        """Get a resource, instantiating it if it has not been done yet.
+        """Get a resource from the registry.
 
-        First it tries to use the factory registered with `name` itself. If it
-        fails, it tries to use the factory registered one level above (using
-        the dot '.' as a separator).
+        If no resource exists at `name` but there is a factory, instantiate the
+        object using the factory.
 
-        If no suitable factory has been found, returns None.
+        If not even a factory exists at `name`, returns None.
         """
         res = self.objects.get(name, None)
         if res is None:
             fac = self.factories.get(name, None)
-            root = name
-            if fac is None:
-                while True:
-                    pos = root.rfind(".")
-                    if pos == -1: break
-                    root = root[:pos]
-                    fac = self.factories.get(root, None)
-                if fac is None:
-                    return None
-            zavai.info("Instantiating", name, "with factory", root)
-            res = fac(self, name)
-            if res is not None:
-                self.objects[name] = res
+            if fac is not None:
+                res = self.objects[name] = fac(self, name)
         return res
 
     def menu(self, name):
-        """Get a menu resource, instantiating it if it has not been done yet.
-
-        All menu resources share the same factory, which builds zavai.Menu
-        objects.
-        """
-        return self.resource("menu." + name)
-
-    def menu_link(self, name, label):
-        """Return a MenuLink to the menu with the given name
-        """
-        return zavai.MenuLink(self, name, label)
+        """Get a menu resource, automatically creating it if it is missing.
 
-    def get_existing(self, name):
-        """Get a menu resource, but only if it has been already instantiated.
-
-        If the resource has not been instantiated yet, returns None.
+        Menus are created automatically linked to a parent menu, according to
+        the hierarchy in `name`.
         """
-        return self.objects.get(name, None)
-
-    def register(self, name, factory):
-        """Register a factory for this registry.
-
-        Name is a name for this factory, like "menu.gps.monitor".
-
-        Factory is a function that creates a Resource object. The function will
-        be passed the Registry itself as the only parameter"""
-        if name in self.factories:
-            return KeyError("% is already registered as a factory", name)
-        self.factories[name] = factory
+        res = self.resource(name)
+        if res is None:
+            # Check if it is a toplevel menu
+            if name.startswith("menu."):
+                parent = get_parent(name[5:])
+                if parent is not None:
+                    parent = "menu." + parent
+            else:
+                parent = get_parent(name)
+
+            res = zavai.Menu(self, name, parent)
+            self.register(name, res)
+        return res
 
     def shutdown(self):
         """Shut down all objects in this Registry.
@@ -98,12 +134,6 @@ class Registry(object):
         self.objects.clear()
 
 class Resource(object):
-    def __init__(self, registry, name, *args, **kw):
-        """The default constructor does nothing, but is there to eat the
-        parameters passed by Registry in case subclassers do not need a
-        constructor"""
-        pass
-
     def shutdown(self):
         """Shut down this resource.