From baf4d66fea871cd6c285c51fcbb995efe5dd6800 Mon Sep 17 00:00:00 2001 From: necoro <> Date: Mon, 9 Jul 2007 04:51:41 +0000 Subject: added resume_loop plugin --- portato/gui/gtk/windows.py | 2 +- portato/gui/gui_helper.py | 4 ++- portato/gui/qt/terminal.py | 35 +++++++++++++++++++++-- portato/gui/qt/tree.py | 2 +- portato/gui/qt/windows.py | 2 +- portato/plugin.py | 65 +++++++++++++++++++++++++++++++++++------- portato/plugins/resume_loop.py | 39 +++++++++++++++++++++++++ 7 files changed, 132 insertions(+), 17 deletions(-) create mode 100644 portato/plugins/resume_loop.py (limited to 'portato') diff --git a/portato/gui/gtk/windows.py b/portato/gui/gtk/windows.py index 3ce8ae2..8b433ca 100644 --- a/portato/gui/gtk/windows.py +++ b/portato/gui/gtk/windows.py @@ -154,7 +154,7 @@ class PluginWindow (AbstractDialog): def cb_ok_clicked (self, btn): for plugin, val in self.changedPlugins.iteritems(): - plugin.set_enabled(val) + plugin.set_option("disabled", not val) self.close() return True diff --git a/portato/gui/gui_helper.py b/portato/gui/gui_helper.py index e827a22..358f56e 100644 --- a/portato/gui/gui_helper.py +++ b/portato/gui/gui_helper.py @@ -459,9 +459,11 @@ class EmergeQueue: self.title_update(title) time.sleep(0.5) - if self.title_update: self.title_update(None) + if self.process is None: # someone resetted this + return + @plugin.hook("after_emerge", packages = packages, retcode = self.process.returncode) def update_packages(): for p in packages: diff --git a/portato/gui/qt/terminal.py b/portato/gui/qt/terminal.py index 91d2e94..23e090a 100644 --- a/portato/gui/qt/terminal.py +++ b/portato/gui/qt/terminal.py @@ -13,8 +13,9 @@ from PyQt4 import Qt from Queue import Queue -from threading import Thread -from os import read +from threading import Thread, currentThread +from os import read, close +import errno try: from curses.ascii import ctrl @@ -45,6 +46,13 @@ class DeleteEvent (Qt.QEvent): Qt.QEvent.__init__(self, self.TYPE) self.del_type = type +class SetPtyEvent (Qt.QEvent): + TYPE = Qt.QEvent.Type(1003) + + def __init__ (self, pty): + Qt.QEvent.__init__(self, self.TYPE) + self.pty = pty + class BoldFormat (Qt.QTextCharFormat): def __init__(self): @@ -112,6 +120,11 @@ class QtConsole (Console, Qt.QTextEdit): # set black bg self.palette().setColor(Qt.QPalette.Base, Qt.QColor("black")) + + # set highlighting colors ... XXX: for some reasons this does not work ... Qt sucks + self.palette().setColor(Qt.QPalette.Highlight, Qt.QColor("white")) + self.palette().setColor(Qt.QPalette.HighlightedText, Qt.QColor("black")) + self.setBackgroundRole(Qt.QPalette.Base) self.setAutoFillBackground(True) @@ -151,6 +164,11 @@ class QtConsole (Console, Qt.QTextEdit): self._deletePrev(event.del_type) event.accept() return True + + elif event.type() == SetPtyEvent.TYPE: + self.set_pty(event.pty) + event.accept() + return True event.ignore() return False @@ -207,6 +225,10 @@ class QtConsole (Console, Qt.QTextEdit): self.current.start() def set_pty (self, pty): + if currentThread().getName() != "MainThread": + Qt.QCoreApplication.postEvent(self, SetPtyEvent(pty)) + return + if not self.running: self.pty = pty self.start_new_thread() @@ -215,6 +237,7 @@ class QtConsole (Console, Qt.QTextEdit): else: # quit current thread self.run = False self.clear() + close(self.pty) self.pty = pty # set this after clearing to lose no chars :) self.start_new_thread() @@ -226,7 +249,13 @@ class QtConsole (Console, Qt.QTextEdit): got_cr = False while self.run: - s = read(self.pty, 1) + try: + s = read(self.pty, 1) + except OSError, e: # bug in Python with the subprocess module + if e.errno == errno.EINTR: + continue + raise + if s == "": break # nothing read -> finish if self.isNotWrapping and s == "\n": diff --git a/portato/gui/qt/tree.py b/portato/gui/qt/tree.py index 6e9950f..7c0fa4c 100644 --- a/portato/gui/qt/tree.py +++ b/portato/gui/qt/tree.py @@ -72,7 +72,7 @@ class QtTree (Tree): iter += 1 # next iter ... newIt = iter.value() - if newIt.parent() != it.parent(): # stop if we left the current parent + if not newIt or newIt.parent() != it.parent(): # stop if we left the current parent return None else: return newIt diff --git a/portato/gui/qt/windows.py b/portato/gui/qt/windows.py index 3bd9296..5f1100f 100644 --- a/portato/gui/qt/windows.py +++ b/portato/gui/qt/windows.py @@ -116,7 +116,7 @@ class PluginDialog (Window): @Qt.pyqtSignature("") def on_buttonBox_accepted(self): for pluginKey, value in self.changedPlugins.iteritems(): - self.plugins[pluginKey].set_enabled(value) + self.plugins[pluginKey].set_option("disabled", not value) self.accept() diff --git a/portato/plugin.py b/portato/plugin.py index abdf86c..ebc1e2f 100644 --- a/portato/plugin.py +++ b/portato/plugin.py @@ -25,6 +25,43 @@ def error (reason, p): reason = "("+reason+")" debug("Malformed plugin:", p, reason, minus=1, error = 1) +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: + if opt.hasChildNodes(): + nodes = opt.childNodes + + if len(nodes) > 1: + raise ParseException, "Malformed option" + + if nodes[0].nodeType != nodes[0].TEXT_NODE: + raise ParseException, "Malformed option" + + type = str(nodes[0].nodeValue.strip()) + + if type in self.__options: + self.set(type, True) + else: + raise ParseException, "Malformed option" + + 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): @@ -172,7 +209,7 @@ class Plugin: self._import = None self.hooks = [] self.menus = [] - self.enabled = True + self.options = Options() def parse_hooks (self, hooks): """Gets a list of -elements and parses them. @@ -198,6 +235,11 @@ class Plugin: for m in menus: menu = Menu(self, str(m.getAttribute("label")), str(m.getAttribute("call"))) self.menus.append(menu) + + def parse_options (self, options): + if options: + for o in options: + self.options.parse(o.getElementsByTagName("option")) def set_import (self, imports): """This gets a list of imports and parses them - setting the import needed to call the plugin. @@ -239,15 +281,14 @@ class Plugin: @rtype: string""" return self._import - def set_disabled (self, nodes): - if nodes: - self.set_enabled(False) + def get_option(self, name): + return self.options.get(name) - def is_enabled (self): - return self.enabled + def set_option (self, name, value): + return self.options.set(name, value) - def set_enabled (self, e): - self.enabled = e + def is_enabled (self): + return not self.get_option("disabled") class PluginQueue: """Class managing and loading the plugins.""" @@ -398,7 +439,7 @@ class PluginQueue: plugin.parse_hooks(elem.getElementsByTagName("hook")) plugin.set_import(elem.getElementsByTagName("import")) plugin.parse_menus(elem.getElementsByTagName("menu")) - plugin.set_disabled(elem.getElementsByTagName("disabled")) + plugin.parse_options(elem.getElementsByTagName("options")) self.list.append(plugin) @@ -428,6 +469,8 @@ class PluginQueue: 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] = [] @@ -451,6 +494,8 @@ class PluginQueue: 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.plugin.name for x in self.hooks[hook.hook][2]] if connect.depend_plugin in named: @@ -472,7 +517,7 @@ class PluginQueue: self._resolve_unresolved(unresolved_before, unresolved_after) for hook in star_before: - self.hooks[hook][0][0:0] = star_before[hook] # prepend the list + 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 diff --git a/portato/plugins/resume_loop.py b/portato/plugins/resume_loop.py new file mode 100644 index 0000000..9891fee --- /dev/null +++ b/portato/plugins/resume_loop.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# +# File: portato/plugins/resume_loop.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 + +import pty +from subprocess import call, STDOUT +from portato.backend import system +from portato.helper import debug + +console = None +command = "until emerge --resume --skipfirst; do : ; done" + +def set_console (*args, **kwargs): + global console + console = kwargs["console"] + +def resume_loop (retcode, *args, **kwargs): + if retcode is None: + debug("Resume-loop called while process is still running!", warn = True) + elif retcode == 0: + # everything ok - ignore + #pass + debug("Everything is ok") + else: + if console is None: + debug("No console for the resume loop...") + else: + # open tty + (master, slave) = pty.openpty() + console.set_pty(master) + call(command, stdout = slave, stderr = STDOUT, shell = True, env = system.get_environment()) -- cgit v1.2.3