summaryrefslogtreecommitdiff
path: root/portato
diff options
context:
space:
mode:
authorRené 'Necoro' Neumann <necoro@necoro.net>2009-05-15 17:43:51 +0200
committerRené 'Necoro' Neumann <necoro@necoro.net>2009-05-15 17:43:51 +0200
commit824e812a44851ec1963d3347f588821477e9937c (patch)
treee678245fcf06da5ac8c5fc1a4d1ec8163062bca1 /portato
parentdc790188d63aa32984db739d9e08ce877b51534f (diff)
parent226f0d6a40a01cbcc9c7baffde647159fb784e40 (diff)
downloadportato-824e812a44851ec1963d3347f588821477e9937c.tar.gz
portato-824e812a44851ec1963d3347f588821477e9937c.tar.bz2
portato-824e812a44851ec1963d3347f588821477e9937c.zip
Merge new (better) plugin system, now allowing to add widgets
Diffstat (limited to 'portato')
-rw-r--r--portato/gui/slots.py42
-rw-r--r--portato/gui/templates/MainWindow.ui2
-rw-r--r--portato/gui/windows/main.py40
-rw-r--r--portato/plugin.py149
4 files changed, 178 insertions, 55 deletions
diff --git a/portato/gui/slots.py b/portato/gui/slots.py
new file mode 100644
index 0000000..2ad4c01
--- /dev/null
+++ b/portato/gui/slots.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+#
+# File: portato/gui/slots.py
+# This file is part of the Portato-Project, a graphical portage-frontend.
+#
+# Copyright (C) 2006-2009 René 'Necoro' Neumann
+# This is free software. You may redistribute copies of it under the terms of
+# the GNU General Public License version 2.
+# There is NO WARRANTY, to the extent permitted by law.
+#
+# Written by René 'Necoro' Neumann <necoro@necoro.net>
+
+from __future__ import absolute_import, with_statement
+
+import gtk
+from ...plugin import WidgetSlot # other modules might import WidgetSlot from here
+
+class PluginMenuSlot (WidgetSlot):
+
+ def __init__ (self, tree):
+ WidgetSlot.__init__(self, self.create_action, "Plugin Menu")
+
+ self.ctr = 0 # counter for the plugin actions
+ self.uim = tree.get_widget("uimanager")
+ self.ag = tree.get_widget("pluginActionGroup")
+
+ def create_action (self, label):
+ aname = "plugin%d" % self.ctr
+ a = gtk.Action(aname, label, None, None)
+ self.ctr += 1
+
+ return a
+
+ def add (self, widget):
+ action = widget.widget
+ self.ag.add_action(action)
+
+ # add to UI
+ mid = self.uim.new_merge_id()
+ self.uim.add_ui(mid, "ui/menubar/pluginMenu", action.get_name(), action.get_name(), gtk.UI_MANAGER_MENUITEM, False)
+
+ self.uim.ensure_update()
diff --git a/portato/gui/templates/MainWindow.ui b/portato/gui/templates/MainWindow.ui
index 383011e..b0a9f18 100644
--- a/portato/gui/templates/MainWindow.ui
+++ b/portato/gui/templates/MainWindow.ui
@@ -963,7 +963,7 @@
</packing>
</child>
<child>
- <object class="GtkHBox" id="hbox1">
+ <object class="GtkHBox" id="optionsHB">
<property name="visible">True</property>
<property name="homogeneous">True</property>
<child>
diff --git a/portato/gui/windows/main.py b/portato/gui/windows/main.py
index fd327a4..9ca7277 100644
--- a/portato/gui/windows/main.py
+++ b/portato/gui/windows/main.py
@@ -23,13 +23,17 @@ from collections import defaultdict
# our backend stuff
from ...backend import flags, system # must be the first to avoid circular deps
-from ... import get_listener, plugin
+from ... import get_listener
from ...helper import debug, warning, error, info
from ...session import Session
from ...db import Database
from ...constants import CONFIG_LOCATION, VERSION, APP_ICON
from ...backend.exceptions import PackageNotFoundException, BlockedException, VersionsNotFoundException
+# plugin stuff
+from ... import plugin
+from .. import slots
+
# more GUI stuff
from ..utils import Config, GtkThread, get_color
from ..queue import EmergeQueue
@@ -469,31 +473,6 @@ class MainWindow (Window):
# package db
splash(_("Creating Database"))
self.db = Database(self.cfg.get("type", section = "DATABASE"))
-
- # set plugins and plugin-menu
- splash(_("Loading Plugins"))
-
- plugin.load_plugins()
- menus = [p.menus for p in plugin.get_plugin_queue().get_plugins()]
- if menus:
- uim = self.tree.get_widget("uimanager")
- ag = self.tree.get_widget("pluginActionGroup")
-
- ctr = 0
- for m in itt.chain(*menus):
-
- # create action
- aname = "plugin%d" % ctr
- a = gtk.Action(aname, m.label, None, None)
- a.connect("activate", m.call)
- ag.add_action(a)
-
- # add to UI
- mid = uim.new_merge_id()
- uim.add_ui(mid, "ui/menubar/pluginMenu", aname, aname, gtk.UI_MANAGER_MENUITEM, False)
-
- ctr += 1
-
splash(_("Building frontend"))
# set paned position
@@ -578,6 +557,15 @@ class MainWindow (Window):
self.queueTree = GtkTree(self.queueList.get_model())
self.queue = EmergeQueue(console = self.console, tree = self.queueTree, db = self.db, title_update = self.title_update, threadClass = GtkThread)
+ # set plugins and plugin-menu
+ splash(_("Loading Plugins"))
+
+ optionsHB = self.tree.get_widget("optionsHB")
+ slots.WidgetSlot(gtk.CheckButton, "Emerge Options", add = lambda w: optionsHB.pack_end(w.widget))
+
+ slots.PluginMenuSlot(self.tree)
+ plugin.load_plugins()
+
# session
splash(_("Restoring Session"))
try:
diff --git a/portato/plugin.py b/portato/plugin.py
index 94a0b4c..4914f25 100644
--- a/portato/plugin.py
+++ b/portato/plugin.py
@@ -34,24 +34,6 @@ class PluginLoadException (Exception):
"""
pass
-class Menu (object):
- """
- One single menu entry.
-
- :IVariables:
-
- label : string
- The label of the entry. Can have underscores to define the shortcut.
-
- call
- The function to call, if the entry is clicked.
- """
- __slots__ = ("label", "call")
-
- def __init__ (self, label, call):
- self.label = label
- self.call = call
-
class Call (object):
"""
This class represents an object, which is attached to a specified hook.
@@ -104,6 +86,79 @@ class Hook (object):
self.override = None
self.after = []
+class WidgetSlot (object):
+ """
+ A slot, where plugins can add widgets.
+
+ :IVariables:
+
+ widget : gobject.GObjectMeta
+ The widget class, which can be used in this slot.
+
+ name : string
+ The slot's name.
+
+ init : function
+ Function to call, iff there is at least one widget for that slot.
+
+ add : function(`Widget`)
+ Function to call, if a widget is added.
+
+ max : int
+ The maximum number of widgets which can be registered. -1 means unlimited.
+
+ """
+
+ slots = {}
+
+ def __init__ (self, widget, name, add = None, init = None, max = -1):
+ self.widget = widget
+ self.name = name
+ self.max = max
+
+ # we might be subclassed
+ # in this case do not overwrite
+
+ if not hasattr(self, "init"):
+ self.init = init
+
+ if not hasattr(self, "add"):
+ self.add = add
+
+ self._inited = False
+
+ debug("Registering new WidgetSlot '%s'.", name)
+ WidgetSlot.slots[name] = self
+
+ def add_widget (self, w):
+ if not self._inited:
+ if self.init is not None:
+ self.init()
+ self._inited = True
+
+ if self.add is not None:
+ self.add(w)
+
+class Widget (object):
+ """
+ Fills a WidgetSlot.
+
+ :IVariables:
+
+ slot : string
+ The name of the slot to fill.
+
+ widget : gtk.Widget
+ The widget which is added.
+
+ """
+
+ __slots__ = ("slot", "widget")
+
+ def __init__ (self, slot, widget):
+ self.slot = slot
+ self.widget = widget
+
class Plugin (object):
"""
This is the main plugin object. It is used where ever a plugin is wanted, and it is the one, which needs to be subclassed by plugin authors.
@@ -134,7 +189,7 @@ class Plugin (object):
:param disable: Forcefully disable the plugin
:type disable: bool
"""
- self.__menus = [] #: List of `Menu`
+ self.__widgets = [] #: List of `Widget`
self.__calls = [] #: List of `Call`
self._unresolved_deps = False #: Does this plugin has unresolved dependencies?
@@ -203,13 +258,13 @@ class Plugin (object):
return getattr(self, "__name__", self.__class__.__name__)
@property
- def menus (self):
+ def widgets (self):
"""
- Returns an iterator over the menus for this plugin.
+ Returns an iterator over the widgets for this plugin.
- :rtype: iter<`Menu`>
+ :rtype: iter<`Widget`>
"""
- return iter(self.__menus)
+ return iter(self.__widgets)
@property
def calls (self):
@@ -243,13 +298,48 @@ class Plugin (object):
"""
return (self.status in (self.STAT_ENABLED, self.STAT_TEMP_ENABLED))
- def add_menu (self, label, callable):
+ def add_widget (self, slot, widget):
+ """
+ Adds a new widget for this plugin.
+
+ :see: `Widget`
+ """
+
+ if not slot in WidgetSlot.slots:
+ raise PluginLoadException, "Could not find specified widget slot: %s" % slot
+
+ self.__widgets.append(Widget(slot, widget))
+
+ def create_widget (self, slot, args, **kwargs):
"""
- Adds a new menu item for this plugin.
+ Creates a new widget for the plugin.
+
+ :Parameters:
+
+ slot : string
+ The slot to create a widget for.
+
+ args : list of objects
+ The arguments to pass to the widget constructor.
- :see: `Menu`
+ kwargs : dict
+ Additional named parameters are for callback functions.
"""
- self.__menus.append(Menu(label, callable))
+
+ try:
+ widget = WidgetSlot.slots[slot].widget
+ except KeyError:
+ raise PluginLoadException, "Could not find specified widget slot: %s" % slot
+
+ if not hasattr(args, "__iter__"):
+ w = widget(args)
+ else:
+ w = widget(*args)
+
+ for k,v in kwargs.iteritems():
+ w.connect(k, v)
+
+ self.add_widget(slot, w)
def add_call (self, hook, callable, type = "before", dep = None):
"""
@@ -334,6 +424,10 @@ class PluginQueue (object):
self._organize()
+ for p in self.plugins:
+ for w in p.widgets:
+ WidgetSlot.slots[w.slot].add_widget(w)
+
def add (self, plugin, disable = False):
"""
Adds a plugin to the internal list.
@@ -481,7 +575,6 @@ class PluginQueue (object):
for hook, calls in star_after.iteritems():
self.hooks[hook].after.extend(calls) # append the list
-
def _resolve_unresolved (self, before, after):
def resolve(hook, list, type, add):
if not list: