From 09e693a8061d0b96c3aac8aae6ae12d6272b2c3e Mon Sep 17 00:00:00 2001 From: René 'Necoro' Neumann Date: Mon, 30 Jun 2008 21:06:17 +0200 Subject: Now the new design is able to do the same as the old one --- portato/gui/windows/plugin.py | 84 ++++++++++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 33 deletions(-) (limited to 'portato/gui/windows') diff --git a/portato/gui/windows/plugin.py b/portato/gui/windows/plugin.py index fb9446e..6e8fdab 100644 --- a/portato/gui/windows/plugin.py +++ b/portato/gui/windows/plugin.py @@ -34,48 +34,40 @@ class PluginWindow (AbstractDialog): self.plugins = plugins self.changedPlugins = {} - view = self.tree.get_widget("pluginList") - self.store = gtk.ListStore(str,str,str) + self.buttons = map(self.tree.get_widget, ("disabledRB", "tempEnabledRB", "enabledRB", "tempDisabledRB")) + map(lambda b: b.set_mode(False), self.buttons) + + self.descrLabel = self.tree.get_widget("descrLabel") + self.authorLabel = self.tree.get_widget("authorLabel") + + self.depExpander = self.tree.get_widget("depExpander") + self.installBtn = self.tree.get_widget("installBtn") - view.set_model(self.store) + self.view = self.tree.get_widget("pluginList") + self.store = gtk.ListStore(str) - cell = gtk.CellRendererText() - col = gtk.TreeViewColumn(_("Plugin"), cell, markup = 0) - view.append_column(col) + self.view.set_model(self.store) - col = gtk.TreeViewColumn(_("Authors"), cell, text = 1) - view.append_column(col) - - ccell = gtk.CellRendererCombo() - ccell.set_property("model", self.statsStore) - ccell.set_property("text-column", 0) - ccell.set_property("has-entry", False) - ccell.set_property("editable", True) - ccell.connect("edited", self.cb_status_changed) - col = gtk.TreeViewColumn(_("Status"), ccell, markup = 2) - view.append_column(col) + cell = gtk.CellRendererText() + col = gtk.TreeViewColumn("Plugin", cell, markup = 0) + self.view.append_column(col) - for p in ((""+p.name+"", p.author, _(self.statsStore[p.status][0])) for p in plugins): - self.store.append(p) + for p in plugins: + self.store.append(["%s" % p.name]) - self.window.show_all() + self.view.get_selection().connect("changed", self.cb_list_selection) - def cb_status_changed (self, cell, path, new_text): - path = int(path) - - self.store[path][2] = "%s" % new_text + self.window.show_all() - # convert string to constant - const = None - for num, val in enumerate(self.statsStore): - if val[0] == new_text: - const = num - break + def cb_state_toggled (self, rb): + + plugin = self.get_actual() - assert (const is not None) + if plugin: + state = self.buttons.index(rb) - self.changedPlugins.update({self.plugins[path] : const}) - debug("new changed plugins: %s => %d", self.plugins[path].name, const) + self.changedPlugins[plugin] = state + debug("new changed plugins: %s => %d", plugin.name, state) def cb_ok_clicked (self, btn): for plugin, val in self.changedPlugins.iteritems(): @@ -83,3 +75,29 @@ class PluginWindow (AbstractDialog): self.close() return True + + def cb_list_selection (self, selection): + plugin = self.get_actual() + + if plugin: + if not plugin.description: + self.descrLabel.hide() + else: + self.descrLabel.set_label(plugin.description) + self.descrLabel.show() + + self.authorLabel.set_label(plugin.author) + + status = self.changedPlugins.get(plugin, plugin.status) + self.buttons[status].set_active(True) + + self.installBtn.hide() + self.depExpander.hide() + + def get_actual (self): + store, it = self.view.get_selection().get_selected() + + if it: + return self.plugins[int(store.get_path(it)[0])] + else: + return None -- cgit v1.2.3-70-g09d2 From a32293a8bbb0a90512d4f8e0fbc385257b29e72a Mon Sep 17 00:00:00 2001 From: René 'Necoro' Neumann Date: Thu, 3 Jul 2008 17:59:53 +0200 Subject: Should show dependencies now --- portato/gui/templates/PluginWindow.glade | 160 +++++++++++++++---------------- portato/gui/windows/plugin.py | 50 +++++++++- 2 files changed, 128 insertions(+), 82 deletions(-) (limited to 'portato/gui/windows') diff --git a/portato/gui/templates/PluginWindow.glade b/portato/gui/templates/PluginWindow.glade index 01db971..8ba7aa9 100644 --- a/portato/gui/templates/PluginWindow.glade +++ b/portato/gui/templates/PluginWindow.glade @@ -1,6 +1,6 @@ - + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK @@ -41,81 +41,55 @@ 2 5 - + True - 5 - True - GTK_BUTTONBOX_EDGE - - - True - True - Enabled - 0 - True - - - - - - True - True - Temporarily enabled - 0 - enabledRB - - - 1 - - + True + True + _Install dependencies + True + 0 + + + 1 + 2 + 2 + 3 + + + + + True + True - + True True - Temporarily disabled - 0 - enabledRB - + True - - 2 - - + True - True - Disabled - 0 - enabledRB - + Needed dependencies + True - False - False - 3 + label_item - 2 - 3 - 4 - GTK_FILL + 2 + 3 - + True - 0 label - True - 1 2 - 1 - 2 GTK_FILL @@ -133,56 +107,82 @@ - + True + 0 label + True + 1 2 + 1 + 2 GTK_FILL - + True - True + 5 + True + GTK_BUTTONBOX_EDGE - + True True - True + Enabled + 0 + True + - + True - Needed dependencies - True + True + Temporarily enabled + 0 + enabledRB - label_item + 1 + + + + + True + True + Temporarily disabled + 0 + enabledRB + + + + 2 + + + + + True + True + Disabled + 0 + enabledRB + + + + False + False + 3 - 2 - 3 - - - - - True - True - True - _Install dependencies - True - 0 - - - 1 2 - 2 - 3 + 3 + 4 + GTK_FILL diff --git a/portato/gui/windows/plugin.py b/portato/gui/windows/plugin.py index 6e8fdab..17c5326 100644 --- a/portato/gui/windows/plugin.py +++ b/portato/gui/windows/plugin.py @@ -42,6 +42,10 @@ class PluginWindow (AbstractDialog): self.depExpander = self.tree.get_widget("depExpander") self.installBtn = self.tree.get_widget("installBtn") + self.depList = self.tree.get_widget("depList") + self.build_dep_list() + + self.instIcon = self.window.render_icon(gtk.STOCK_YES, gtk.ICON_SIZE_MENU) self.view = self.tree.get_widget("pluginList") self.store = gtk.ListStore(str) @@ -59,6 +63,32 @@ class PluginWindow (AbstractDialog): self.window.show_all() + def build_dep_list (self): + store = gtk.TreeStore(gtk.gdk.Pixbuf, str) + + self.depList.set_model(store) + + col = gtk.TreeViewColumn() + + cell = gtk.CellRendererPixbuf() + col.pack_start(cell, False) + col.add_attribute(cell, "pixbuf", 0) + + cell = gtk.CellRendererText() + col.pack_start(cell, True) + col.add_attribute(cell, "text", 1) + + self.depList.append_column(col) + + def fill_dep_list (self, inst = [], ninst = []): + store = self.depList.get_model() + store.clear() + + for dep in inst: + store.append([self.instIcon, dep]) + for dep in ninst: + store.append([None, dep]) + def cb_state_toggled (self, rb): plugin = self.get_actual() @@ -91,8 +121,24 @@ class PluginWindow (AbstractDialog): status = self.changedPlugins.get(plugin, plugin.status) self.buttons[status].set_active(True) - self.installBtn.hide() - self.depExpander.hide() + if plugin.deps: + inst = [] + ninst = [] + + for dep in plugin.deps: + if system.find_packages(dep, pkgSet = "installed"): + inst.append(dep) + else: + ninst.append(dep) + + self.fill_dep_list(inst, ninst) + self.depExpander.show() + + self.installBtn.show() + self.installBtn.set_sensitive(bool(ninst)) + else: + self.installBtn.hide() + self.depExpander.hide() def get_actual (self): store, it = self.view.get_selection().get_selected() -- cgit v1.2.3-70-g09d2 From b33a8067895a127a7c2b4e0627df55550503b6f1 Mon Sep 17 00:00:00 2001 From: René 'Necoro' Neumann Date: Thu, 3 Jul 2008 23:17:23 +0200 Subject: Ported completely --- portato/gui/windows/main.py | 19 +- portato/plugin.py | 53 ++++- portato/plugin_old.py | 530 -------------------------------------------- 3 files changed, 54 insertions(+), 548 deletions(-) delete mode 100644 portato/plugin_old.py (limited to 'portato/gui/windows') diff --git a/portato/gui/windows/main.py b/portato/gui/windows/main.py index 364810d..b4e6353 100644 --- a/portato/gui/windows/main.py +++ b/portato/gui/windows/main.py @@ -607,13 +607,13 @@ class MainWindow (Window): # set plugins and plugin-menu splash(_("Loading Plugins")) - plugin.load_plugins("gtk") - menus = plugin.get_plugin_queue().get_plugin_menus() + plugin.load_plugins() + menus = [p.menus for p in plugin.get_plugin_queue().get_plugins()] if menus: self.tree.get_widget("pluginMenuItem").set_no_show_all(False) pluginMenu = self.tree.get_widget("pluginMenu") - for m in menus: + for m in itt.chain(*menus): item = gtk.MenuItem(m.label) item.connect("activate", m.call) pluginMenu.append(item) @@ -1082,13 +1082,8 @@ class MainWindow (Window): def save_plugin (p): def _save (): - stat_on = p.status >= p.STAT_ENABLED - hard_on = not p.get_option("disabled") - - if stat_on != hard_on: - return int(stat_on) - else: - return "" + return int(p.status >= p.STAT_ENABLED) + return _save # SESSION VERSION @@ -1553,8 +1548,8 @@ class MainWindow (Window): if queue is None: plugins = [] else: - plugins = queue.get_plugins() - + plugins = list(queue.get_plugins()) + PluginWindow(self.window, plugins) return True diff --git a/portato/plugin.py b/portato/plugin.py index 6160d5e..88e5c55 100644 --- a/portato/plugin.py +++ b/portato/plugin.py @@ -12,10 +12,17 @@ from __future__ import absolute_import +import os +import os.path as osp from collections import defaultdict from functools import wraps -from .helper import debug, warning, info +from .helper import debug, warning, info, error +from .constants import PLUGIN_DIR +from . import plugins as plugin_module + +class PluginLoadException (Exception): + pass class Menu (object): __slots__ = ("label", "call") @@ -49,7 +56,7 @@ class Plugin (object): def __init__ (self): self.__menus = [] self.__calls = [] - self.state = STAT_ENABLED + self.status = self.STAT_ENABLED @property def author (self): @@ -93,13 +100,37 @@ class PluginQueue (object): self.plugins = [] self.hooks = defaultdict(Hook) - self._load() def get_plugins (self, list_disabled = True): return (x for x in self.plugins if (x.is_enabled() or list_disabled)) - def _load (self): + def load (self): """Load the plugins.""" + + plugins = [] + for f in os.listdir(PLUGIN_DIR): + path = osp.join(PLUGIN_DIR, f) + if osp.isdir(path): + if osp.isfile(osp.join(path, "__init__.py")): + plugins.append(f) + else: + debug("'%s' is not a plugin: __init__.py missing", path) + else: + if f.endswith(".py"): + plugins.append(f[:-3]) + else: + debug("'%s' is not a plugin: not a .py file", path) + + plugin_module.__path__.insert(0, PLUGIN_DIR.rstrip("/")) + plugin_module.__builtins__["Plugin"] = Plugin + plugin_module.__builtins__["register"] = register + + for p in plugins: + try: + exec "from portato.plugins import %s" % p in {} + except PluginLoadException, e: + error(_("Loading plugin '%(plugin)s' failed: %(error)s"), {"plugin" : p, "error" : e.message}) + self._organize() def add (self, plugin): @@ -108,7 +139,7 @@ class PluginQueue (object): def hook (self, hook, *hargs, **hkwargs): def hook_decorator (func): - h = self.hooks.hook + h = self.hooks[hook] active = Hook() @@ -242,6 +273,8 @@ def load_plugins(): global __plugins if __plugins is None: __plugins = PluginQueue() + __plugins.load() + def get_plugin_queue(): """Returns the actual L{PluginQueue}. If it is C{None}, they are not being loaded yet. @@ -260,4 +293,12 @@ def hook(hook, *args, **kwargs): def register (plugin): if __plugins is not None: - __plugins.add(plugin) + if callable(plugin) and Plugin in plugin.__bases__: + p = plugin() # need an instance and not the class + elif isinstance(plugin, Plugin): + p = plugin + else: + raise PluginLoadException, "Is neither a subclass nor an instance of Plugin." + + info(_("Plugin '%s' loaded."), p.name) + __plugins.add(p) diff --git a/portato/plugin_old.py b/portato/plugin_old.py deleted file mode 100644 index 5926922..0000000 --- a/portato/plugin_old.py +++ /dev/null @@ -1,530 +0,0 @@ -# -*- coding: utf-8 -*- -# -# File: portato/plugin.py -# This file is part of the Portato-Project, a graphical portage-frontend. -# -# Copyright (C) 2007 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 - -"""A module containing the management of the plugin system.""" - -from __future__ import absolute_import - -import os, os.path -from xml.dom.minidom import parse -from lxml import etree - -from .constants import PLUGIN_DIR, XSD_LOCATION -from .helper import debug, info, warning, error, flatten - -class PluginImportException (ImportError): - pass - -class Options (object): - """The -element.""" - - __options = ("disabled", "blocking") - - def __init__ (self, options = None): - - self.disabled = False - self.blocking = False - - if options: - self.parse(options) - - def parse (self, options): - for opt in options: - nodes = opt.childNodes - type = str(nodes[0].nodeValue.strip()) - if type in self.__options: - self.set(type, True) - - def get (self, name): - return self.__getattribute__(name) - - def set (self, name, value): - return self.__setattr__(name, value) - -class Menu: - """A single -element.""" - def __init__ (self, plugin, label, call): - """Constructor. - - @param plugin: the plugin this menu belongs to - @type plugin: Plugin - @param label: the label to show - @type label: string - @param call: the function to call relative to the import statement - @type call: string - - @raises PluginImportException: if the plugin's import could not be imported""" - - self.label = label - self.plugin = plugin - - if self.plugin.needs_import(): # get import - imp = self.plugin.get_import() - try: - mod = __import__(imp, globals(), locals(), [call]) - except ImportError: - raise PluginImportException, imp - - try: - self.call = eval("mod."+call) # build function - except AttributeError: - raise PluginImportException, imp - else: - try: - self.call = eval(call) - except AttributeError: - raise PluginImportException, imp - -class Connect: - """A single -element.""" - - def __init__ (self, hook, type, depend_plugin): - """Constructor. - - @param hook: the parent Hook - @type hook: Hook - @param type: the type of the connect ("before", "after", "override") - @type type: string - @param depend_plugin: a plugin we are dependant on - @type depend_plugin: string or None""" - - self.type = type - self.hook = hook - self.depend_plugin = depend_plugin - - def is_before_type (self): - return self.type == "before" - - def is_after_type (self): - return self.type == "after" - - def is_override_type (self): - return self.type == "override" - -class Hook: - """A single -element.""" - - def __init__ (self, plugin, hook, call): - """Constructor. - - @param plugin: the parent Plugin - @type plugin: Plugin - @param hook: the hook to add to - @type hook: string - @param call: the call to make - @type call: string""" - - self.plugin = plugin - self.hook = hook - self.call = call - self.connects = [] - - def parse_connects (self, connects): - """This gets a list of -elements and parses them. - - @param connects: the list of 's - @type connects: NodeList""" - - if not connects: # no connects - assume "before" connect - self.connects.append(Connect(self, "before", None)) - - for c in connects: - type = c.getAttribute("type") - if type == '': - type = "before" - - # get dep_plugin if available - dep_plugin = None - if c.hasChildNodes(): - nodes = c.childNodes - dep_plugin = nodes[0].nodeValue.strip() - - connect = Connect(self, type, dep_plugin) - self.connects.append(connect) - -class Plugin: - """A complete plugin.""" - - (STAT_DISABLED, STAT_TEMP_ENABLED, STAT_ENABLED, STAT_TEMP_DISABLED) = range(4) - - def __init__ (self, file, name, author): - """Constructor. - - @param file: the file name of the plugin.xml - @type file: string - @param name: the name of the plugin - @type name: Node - @param author: the author of the plugin - @type author: Node""" - - self.file = file - self.name = name.firstChild.nodeValue.strip() - self.author = author.firstChild.nodeValue.strip() - self._import = None - self.hooks = [] - self.menus = [] - self.options = Options() - - self.status = self.STAT_ENABLED - - def parse_hooks (self, hooks): - """Gets an -elements and parses it. - - @param hooks: the hooks node - @type hooks: NodeList""" - - if hooks: - for h in hooks[0].getElementsByTagName("hook"): - hook = Hook(self, str(h.getAttribute("type")), str(h.getAttribute("call"))) - hook.parse_connects(h.getElementsByTagName("connect")) - self.hooks.append(hook) - - def parse_menus (self, menus): - """Get a list of -elements and parses them. - - @param menus: the menu nodelist - @type menus: NodeList""" - - if menus: - for item in menus[0].getElementsByTagName("item"): - menu = Menu(self, item.firstChild.nodeValue.strip(), str(item.getAttribute("call"))) - self.menus.append(menu) - - def parse_options (self, options): - if options: - for o in options: - self.options.parse(o.getElementsByTagName("option")) - - self.status = self.STAT_DISABLED if self.options.get("disabled") else self.STAT_ENABLED - - def set_import (self, imports): - """This gets a list of imports and parses them - setting the import needed to call the plugin. - - @param imports: list of imports - @type imports: NodeList - - @raises PluginImportException: if the plugin's import could not be imported""" - - if imports: - self._import = str(imports[0].firstChild.nodeValue.strip()) - - try: # try loading - mod = __import__(self._import) - del mod - except ImportError: - raise PluginImportException, self._import - - def needs_import (self): - """Returns True if an import is required prior to calling the plugin. - @rtype: bool""" - return self._import is not None - - def get_import (self): - """Returns the module to import. - @rtype: string""" - return self._import - - def get_option(self, name): - return self.options.get(name) - - def set_option (self, name, value): - return self.options.set(name, value) - - def is_enabled (self): - return (self.status in (self.STAT_ENABLED, self.STAT_TEMP_ENABLED)) - -class PluginQueue: - """Class managing and loading the plugins.""" - - def __init__ (self, frontend, load = True): - """Constructor. - - @param frontend: the frontend used - @type frontend: string - @param load: if False nothing is loaded - @type load: bool""" - - self.frontend = frontend - self.list = [] - self.hooks = {} - if load: - self._load() - - def get_plugins (self, list_disabled = True): - return [x for x in self.list if (x.is_enabled() or list_disabled)] - - def get_plugin_data (self, list_disabled = False): - return [(x.name, x.author) for x in self.list if (x.is_enabled() or list_disabled)] - - def get_plugin_menus (self, list_disabled = False): - return flatten([x.menus for x in self.list if (x.is_enabled() or list_disabled)]) - - def hook (self, hook, *hargs, **hkwargs): - """This is a method taking care of calling the plugins. - - B{Example}:: - - @pluginQueue.hook("some_hook", data) - def function (a, b, c): - orig_call(b,c,data) - - def function (a, b, c): - hook = pluginQueue.hook("some_hook", data) - hook(orig_call)(b,c,data) - - @param hook: the name of the hook - @type hook: string""" - - def call (cmd): - """Convienience function for calling a connect. - @param cmd: the actual Connect - @type cmd: Connect""" - - imp = "" - if cmd.hook.plugin.needs_import(): # get import - imp = cmd.hook.plugin.get_import() - try: - mod = __import__(imp, globals(), locals(), [cmd.hook.call]) - except ImportError: - error(_("%s cannot be imported."), imp) - return - - try: - f = eval("mod."+cmd.hook.call) # build function - except AttributeError: - error(_("%s cannot be imported."), cmd.hook.call) - else: - try: - f = eval(cmd.hook.call) - except AttributeError: - error(_("%s cannot be imported."), cmd.hook.call) - - return f(*hargs, **hkwargs) # call function - - def hook_decorator (func): - """This is the real decorator.""" - - if hook in self.hooks: - list = self.hooks[hook] - else: - list = ([],[],[]) - - # remove disabled - _list = ([],[],[]) - for i in range(len(list)): - for cmd in list[i]: - if cmd.hook.plugin.is_enabled(): - _list[i].append(cmd) - - list = _list - - def wrapper (*args, **kwargs): - - ret = None - - # before - for cmd in list[0]: - debug(_("Accessing hook '%(hook)s' of plugin '%(plugin)s' (before)."), {"hook" : hook, "plugin": cmd.hook.plugin.name}) - call(cmd) - - if list[1]: # override - info(_("Overriding hook '%(hook)s' with plugin '%(plugin)s'."), {"hook": hook, "plugin": list[1][0].hook.plugin.name}) - ret = call(list[1][0]) - else: # normal - ret = func(*args, **kwargs) - - # after - for cmd in list[2]: - debug(_("Accessing hook '%(hook)s' of plugin '%(plugin)s' (after)."), {"hook":hook, "plugin": cmd.hook.plugin.name}) - call(cmd) - - return ret - - return wrapper - - return hook_decorator - - def _load (self): - """Load the plugins.""" - plugins = filter(lambda x: x.endswith(".xml"), os.listdir(PLUGIN_DIR)) - plugins = map(lambda x: os.path.join(PLUGIN_DIR, x), plugins) - schema = etree.XMLSchema(file = XSD_LOCATION) - - for p in plugins: - - try: - schema.assertValid(etree.parse(p)) - except etree.XMLSyntaxError: - error(_("Loading plugin '%s' failed. Invalid XML syntax."), p) - continue - except etree.DocumentInvalid: - error(_("Loading plugin '%s' failed. Plugin does not comply with schema."), p) - continue - - doc = parse(p) - - try: - list = doc.getElementsByTagName("plugin") - elem = list[0] - - frontendOK = None - frontends = elem.getElementsByTagName("frontends") - if frontends: - nodes = frontends[0].childNodes - for f in nodes[0].nodeValue.strip().split(): - if f == self.frontend: - frontendOK = True # one positive is enough - break - elif frontendOK is None: # do not make negative if we already have a positive - frontendOK = False - - if frontendOK is None or frontendOK == True: - plugin = Plugin(p, elem.getElementsByTagName("name")[0], elem.getElementsByTagName("author")[0]) - plugin.parse_hooks(elem.getElementsByTagName("hooks")) - plugin.set_import(elem.getElementsByTagName("import")) - plugin.parse_menus(elem.getElementsByTagName("menu")) - plugin.parse_options(elem.getElementsByTagName("options")) - - self.list.append(plugin) - info(_("Plugin '%s' loaded."), p) - - except PluginImportException, e: - error(_("Loading plugin '%(plugin)s' failed: Could not import %(import)s"), {"plugin": p, "import": e[0]}) - finally: - doc.unlink() - - self._organize() - - def _organize (self): - """Creates the lists of connects in a way, that all dependencies are fullfilled.""" - unresolved_before = {} - unresolved_after = {} - star_before = {} # should be _before_ all other - star_after = {} # should be _after_ all other - - for plugin in self.list: # plugins - for hook in plugin.hooks: # hooks in plugin - if not hook.hook in self.hooks: - self.hooks[hook.hook] = ([], [], []) - - for connect in hook.connects: # connects in hook - - # type="before" - if connect.is_before_type(): - if connect.depend_plugin is None: # no dependency -> straight add - self.hooks[hook.hook][0].append(connect) - elif connect.depend_plugin == "*": - self.hooks[hook.hook][0][0:0] = [connect] - elif connect.depend_plugin == "-*": - if not hook.hook in star_before: - star_before[hook.hook] = [] - - star_before[hook.hook].append(connect) - else: - named = [x.plugin.name for x in self.hooks[hook.hook][0]] - if connect.depend_plugin in named: - self.hooks[hook.hook][0].insert(named.index(connect.depend_plugin), connect) - else: - if not hook.hook in unresolved_before: - unresolved_before[hook.hook] = [] - - unresolved_before[hook.hook].append(connect) - - # type = "after" - elif connect.is_after_type(): - if connect.depend_plugin is None: # no dependency -> straight add - self.hooks[hook.hook][2].append(connect) - elif connect.depend_plugin == "*": - if not hook.hook in star_after: - star_after[hook.hook] = [] - - star_after[hook.hook].append(connect) - elif connect.depend_plugin == "-*": - self.hooks[hook.hook][2][0:0] = [connect] - else: - named = [x.hook.plugin.name for x in self.hooks[hook.hook][2]] - if connect.depend_plugin in named: - self.hooks[hook.hook][2].insert(named.index(connect.depend_plugin)+1, connect) - else: - if not hook.hook in unresolved_after: - unresolved_after[hook.hook] = [] - - unresolved_after[hook.hook].append(connect) - - # type = "override" - elif connect.is_override_type(): - if self.hooks[hook.hook][1]: - warning(_("For hook '%(hook)s' an override is already defined by plugin '%(plugin)s'!"), {"hook": hook.hook, "plugin": self.hooks[hook.hook][1][0]}) - - self.hooks[hook.hook][1][:1] = [connect] - continue - - self._resolve_unresolved(unresolved_before, unresolved_after) - - for hook in star_before: - self.hooks[hook][0].extend(star_before[hook]) # append the list - - for hook in star_after: - self.hooks[hook][2].extend(star_after[hook]) # append the list - - - def _resolve_unresolved (self, before, after): - def resolve(hook, list, idx, add): - if not list: - return - - changed = False - for connect in list[:]: - named = [x.plugin.name for x in self.hooks[hook][idx]] - if connect.depend_plugin in named: - changed = True - self.hooks[hook][idx].insert(named.index(connect.depend_plugin)+add, connect) - list.remove(connect) - - if changed: - resolve(hook, list, idx, add) - - for l in list: - warning("Command for hook '%(hook)s' in plugin '%(plugin)s' could not be added due to missing dependant: '%(dep)s'!", {"hook": hook, "plugin": l.hook.plugin.name, "dep": l.depend_plugin}) - - for hook in before: - resolve(hook, before[hook], 0, 0) - - for hook in after: - resolve(hook, after[hook], 2, 1) - - -__plugins = None - -def load_plugins(frontend): - """Loads the plugins for a given frontend. - @param frontend: The frontend. See L{constants.FRONTENDS} for the correct list of values. - @type frontend: string""" - - global __plugins - if __plugins is None or __plugins.frontend != frontend: - __plugins = PluginQueue(frontend) - -def get_plugin_queue(): - """Returns the actual L{PluginQueue}. If it is C{None}, they are not being loaded yet. - - @returns: the actual L{PluginQueue} or C{None} - @rtype: PluginQueue""" - return __plugins - -def hook(hook, *args, **kwargs): - if __plugins is None: - def pseudo_decorator(f): - return f - return pseudo_decorator - else: - return __plugins.hook(hook, *args, **kwargs) -- cgit v1.2.3-70-g09d2 From 8e07fd436cfbf02fbf43c9e221badb55acdb546a Mon Sep 17 00:00:00 2001 From: René 'Necoro' Neumann Date: Fri, 4 Jul 2008 14:15:55 +0200 Subject: Some more dependency awareness --- plugins/etc_proposals.py | 4 +-- plugins/gpytage.py | 3 +-- plugins/notify.py | 12 ++++++--- portato/gui/windows/main.py | 3 +++ portato/plugin.py | 60 ++++++++++++++++++++++++++++++++++----------- 5 files changed, 59 insertions(+), 23 deletions(-) (limited to 'portato/gui/windows') diff --git a/plugins/etc_proposals.py b/plugins/etc_proposals.py index 07f9a80..c32c8f3 100644 --- a/plugins/etc_proposals.py +++ b/plugins/etc_proposals.py @@ -20,9 +20,7 @@ class EtcProposals (Plugin): __description__ = "Adds support for etc-proposals, a graphical etc-update replacement." __dependency__ = ["app-portage/etc-proposals"] - def __init__ (self): - Plugin.__init__(self) - + def init (self): self.prog = ["/usr/sbin/etc-proposals"] self.add_call("after_emerge", self.hook, type = "after") self.add_menu("Et_c-Proposals", self.menu) diff --git a/plugins/gpytage.py b/plugins/gpytage.py index 33509e1..d8c2831 100644 --- a/plugins/gpytage.py +++ b/plugins/gpytage.py @@ -17,8 +17,7 @@ class GPytage (Plugin): __description__ = "Adds a menu entry to directly start gpytage, a config editor." __dependency__ = ["app-portage/gpytage"] - def __init__ (self): - Plugin.__init__(self) + def init (self): self.add_menu("Config _Editor", self.menu) def menu (self, *args): diff --git a/plugins/notify.py b/plugins/notify.py index bc1b2ea..6446812 100644 --- a/plugins/notify.py +++ b/plugins/notify.py @@ -10,7 +10,12 @@ # # Written by René 'Necoro' Neumann -import pynotify +disable = False + +try: + import pynotify +except ImportError: + disable = True from portato import get_listener @@ -22,8 +27,7 @@ class Notify (Plugin): __description__ = "Show notifications when an emerge process finishes." __dependency__ = ["dev-python/notify-python"] - def __init__ (self): - Plugin.__init__(self) + def init (self): self.add_call("after_emerge", self.notify) def notify (self, retcode, **kwargs): @@ -42,4 +46,4 @@ class Notify (Plugin): get_listener().send_notify(base = text, descr = descr, icon = icon, urgency = urgency) -register(Notify) +register(Notify, disable) diff --git a/portato/gui/windows/main.py b/portato/gui/windows/main.py index b4e6353..479274d 100644 --- a/portato/gui/windows/main.py +++ b/portato/gui/windows/main.py @@ -1082,6 +1082,9 @@ class MainWindow (Window): def save_plugin (p): def _save (): + if p.status == p.STAT_HARD_DISABLED: + return "" + return int(p.status >= p.STAT_ENABLED) return _save diff --git a/portato/plugin.py b/portato/plugin.py index 7b8a493..ada7a0f 100644 --- a/portato/plugin.py +++ b/portato/plugin.py @@ -19,6 +19,7 @@ from functools import wraps from .helper import debug, warning, info, error from .constants import PLUGIN_DIR +from .backend import system from . import plugins as plugin_module class PluginLoadException (Exception): @@ -52,11 +53,30 @@ class Hook (object): class Plugin (object): (STAT_DISABLED, STAT_TEMP_ENABLED, STAT_ENABLED, STAT_TEMP_DISABLED) = range(4) + STAT_HARD_DISABLED = -1 - def __init__ (self): + def __init__ (self, disable = False): self.__menus = [] self.__calls = [] - self.status = self.STAT_ENABLED + self._unresolved_deps = False + + if not disable: + self.status = self.STAT_ENABLED + else: + self.status = self.STAT_HARD_DISABLED + + def _init (self): + + for d in self.deps: + if not system.find_packages(d, pkgSet="installed", with_version = False): + self._unresolved_deps = True + break + + if self.status != self.STAT_HARD_DISABLED and not self._unresolved_deps: + self.init() + + def init (self): + pass @property def author (self): @@ -140,8 +160,28 @@ class PluginQueue (object): self._organize() - def add (self, plugin): - self.plugins.append(plugin) + def add (self, plugin, disable = False): + if callable(plugin) and Plugin in plugin.__bases__: + p = plugin(disable = disable) # need an instance and not the class + elif isinstance(plugin, Plugin): + p = plugin + if disable: + p.status = p.STAT_HARD_DISABLED + else: + raise PluginLoadException, "Is neither a subclass nor an instance of Plugin." + + p._init() + + self.plugins.append(p) + + if p.status == p.STAT_HARD_DISABLED: + msg = _("Plugin is disabled!") + elif p._unresolved_deps: + msg = _("Plugin has unresolved dependencies - disabled!") + else: + msg = "" + + info("%s %s", _("Plugin '%s' loaded.") % p.name, msg) def hook (self, hook, *hargs, **hkwargs): @@ -298,14 +338,6 @@ def hook(hook, *args, **kwargs): else: return __plugins.hook(hook, *args, **kwargs) -def register (plugin): +def register (plugin, disable = False): if __plugins is not None: - if callable(plugin) and Plugin in plugin.__bases__: - p = plugin() # need an instance and not the class - elif isinstance(plugin, Plugin): - p = plugin - else: - raise PluginLoadException, "Is neither a subclass nor an instance of Plugin." - - info(_("Plugin '%s' loaded."), p.name) - __plugins.add(p) + __plugins.add(plugin, disable) -- cgit v1.2.3-70-g09d2 From cf70f253a11871ba6db372eb4735335ec97129cd Mon Sep 17 00:00:00 2001 From: René 'Necoro' Neumann Date: Fri, 4 Jul 2008 15:57:28 +0200 Subject: Added ability to install missing plugin deps --- portato/gui/templates/PluginWindow.glade | 172 +++++++++++++++---------------- portato/gui/windows/main.py | 2 +- portato/gui/windows/plugin.py | 47 +++++++-- 3 files changed, 126 insertions(+), 95 deletions(-) (limited to 'portato/gui/windows') diff --git a/portato/gui/templates/PluginWindow.glade b/portato/gui/templates/PluginWindow.glade index 9e1fc43..f76193e 100644 --- a/portato/gui/templates/PluginWindow.glade +++ b/portato/gui/templates/PluginWindow.glade @@ -1,6 +1,6 @@ - + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK @@ -38,7 +38,6 @@ True 0 - GTK_SHADOW_OUT True @@ -46,82 +45,62 @@ 2 10 - + True - 5 - True - GTK_BUTTONBOX_EDGE - - - True - True - Enabled - 0 - True - - - - - - True - True - Temporarily enabled - 0 - enabledRB - - - 1 - - + True + True + _Install dependencies + True + 0 + + + + 1 + 2 + 2 + 3 + + 10 + + + + + True + True - + True True - Temporarily disabled - 0 - enabledRB - + False + True - - 2 - - + True - True - Disabled - 0 - enabledRB - + Needed dependencies + True - False - False - 3 + label_item - 2 - 3 - 4 - GTK_FILL + 2 + 3 + 10 - + True - 0 label - True - 1 2 - 1 - 2 GTK_FILL + 10 @@ -138,61 +117,82 @@ - + True + 0 label + True + 1 2 + 1 + 2 GTK_FILL - 10 - + True - True + 5 + True + GTK_BUTTONBOX_EDGE - + True True - False - True + Enabled + 0 + True + - + True - Needed dependencies - True + True + Temporarily enabled + 0 + enabledRB - label_item + 1 + + + + + True + True + Temporarily disabled + 0 + enabledRB + + + + 2 + + + + + True + True + Disabled + 0 + enabledRB + + + + False + False + 3 - 2 - 3 - 10 - - - - - True - True - True - _Install dependencies - True - 0 - - - 1 2 - 2 - 3 - - 10 + 3 + 4 + GTK_FILL diff --git a/portato/gui/windows/main.py b/portato/gui/windows/main.py index 479274d..265d4dd 100644 --- a/portato/gui/windows/main.py +++ b/portato/gui/windows/main.py @@ -1553,7 +1553,7 @@ class MainWindow (Window): else: plugins = list(queue.get_plugins()) - PluginWindow(self.window, plugins) + PluginWindow(self.window, plugins, self.queue) return True def cb_show_updates_clicked (self, *args): diff --git a/portato/gui/windows/plugin.py b/portato/gui/windows/plugin.py index 9760658..392654e 100644 --- a/portato/gui/windows/plugin.py +++ b/portato/gui/windows/plugin.py @@ -15,7 +15,9 @@ from __future__ import absolute_import import gtk from .basic import AbstractDialog +from ..dialogs import blocked_dialog, unmask_dialog from ...backend import system +from ...backend.exceptions import PackageNotFoundException, BlockedException from ...helper import debug class PluginWindow (AbstractDialog): @@ -25,7 +27,7 @@ class PluginWindow (AbstractDialog): for s in (_("Disabled"), _("Temporarily enabled"), _("Enabled"), _("Temporarily disabled")): statsStore.append([s]) - def __init__ (self, parent, plugins): + def __init__ (self, parent, plugins, queue = None): """Constructor. @param parent: the parent window @@ -33,7 +35,10 @@ class PluginWindow (AbstractDialog): AbstractDialog.__init__(self, parent) self.plugins = plugins + self.queue = queue self.changedPlugins = {} + self.inst = [] + self.ninst = [] self.buttons = map(self.tree.get_widget, ("disabledRB", "tempEnabledRB", "enabledRB", "tempDisabledRB")) map(lambda b: b.set_mode(False), self.buttons) @@ -46,6 +51,8 @@ class PluginWindow (AbstractDialog): self.depList = self.tree.get_widget("depList") self.build_dep_list() + self.buttonBox = self.tree.get_widget("buttonBox") + self.instIcon = self.window.render_icon(gtk.STOCK_YES, gtk.ICON_SIZE_MENU) self.view = self.tree.get_widget("pluginList") @@ -109,6 +116,8 @@ class PluginWindow (AbstractDialog): def cb_list_selection (self, selection): plugin = self.get_actual() + self.inst = [] + self.ninst = [] if plugin: if not plugin.description: @@ -123,23 +132,45 @@ class PluginWindow (AbstractDialog): self.buttons[status].set_active(True) if plugin.deps: - inst = [] - ninst = [] for dep in plugin.deps: - if system.find_packages(dep, pkgSet = "installed"): - inst.append(dep) + if system.find_packages(dep, pkgSet = "installed", with_version = False): + self.inst.append(dep) else: - ninst.append(dep) + self.ninst.append(dep) - self.fill_dep_list(inst, ninst) + self.fill_dep_list(self.inst, self.ninst) self.depExpander.show() self.installBtn.show() - self.installBtn.set_sensitive(bool(ninst)) + self.installBtn.set_sensitive(bool(self.ninst)) + else: self.installBtn.hide() self.depExpander.hide() + + self.buttonBox.set_sensitive(not plugin._unresolved_deps and plugin.status != plugin.STAT_HARD_DISABLED) + + def cb_install_clicked (self, *args): + if not self.queue: + return False + + for cpv in self.ninst: + + pkg = system.find_best_match(cpv, masked = False, only_cpv = True) + if not pkg: + pkg = system.find_best_match(cpv, masked = True, only_cpv = True) + + try: + try: + self.queue.append(pkg, type = "install") + except PackageNotFoundException, e: + if unmask_dialog(e[0]) == gtk.RESPONSE_YES: + self.queue.append(pkg, type = "install", unmask = True) + except BlockedException, e: + blocked_dialog(e[0], e[1]) + + return True def get_actual (self): store, it = self.view.get_selection().get_selected() -- cgit v1.2.3-70-g09d2