diff options
author | necoro <> | 2007-03-31 19:29:26 +0000 |
---|---|---|
committer | necoro <> | 2007-03-31 19:29:26 +0000 |
commit | 2d2f6823f5360a5287b4b19d035cad4a5611fa3a (patch) | |
tree | c108aee3dd9a34c1c7dbc68ed87ff4cc65078593 /portato | |
parent | 5138b1e23d34e0a72e0c2f4ae52256e14d825320 (diff) | |
download | portato-2d2f6823f5360a5287b4b19d035cad4a5611fa3a.tar.gz portato-2d2f6823f5360a5287b4b19d035cad4a5611fa3a.tar.bz2 portato-2d2f6823f5360a5287b4b19d035cad4a5611fa3a.zip |
Allowed Plugins to have a menu
Diffstat (limited to '')
-rwxr-xr-x | portato.py | 9 | ||||
-rw-r--r-- | portato/gui/gtk/glade/portato.glade | 394 | ||||
-rw-r--r-- | portato/gui/gtk/windows.py | 13 | ||||
-rw-r--r-- | portato/helper.py | 12 | ||||
-rw-r--r-- | portato/plugin.py | 69 | ||||
-rw-r--r-- | portato/plugins/etc_proposals.py | 14 |
6 files changed, 310 insertions, 201 deletions
@@ -15,7 +15,7 @@ from portato.constants import VERSION, FRONTENDS, STD_FRONTEND import sys -if __name__ == "__main__": +def main (): uimod = STD_FRONTEND for arg in sys.argv[1:]: @@ -41,8 +41,8 @@ Written by René 'Necoro' Neumann <necoro@necoro.net>""" % VERSION if uimod in FRONTENDS: try: exec ("from portato.gui.%s import run" % uimod) - except ImportError: - print "'%s' should be installed, but cannot be imported. This is definitly a bug." % uimod + except ImportError, e: + print "'%s' should be installed, but cannot be imported. This is definitly a bug. (%s)" % (uimod, e[0]) sys.exit(1) else: print ("Unknown interface '%s'. Correct interfaces are:" % uimod) , @@ -52,3 +52,6 @@ Written by René 'Necoro' Neumann <necoro@necoro.net>""" % VERSION sys.exit(1) run() + +if __name__ == "__main__": + main() diff --git a/portato/gui/gtk/glade/portato.glade b/portato/gui/gtk/glade/portato.glade index 89f52d2..7735975 100644 --- a/portato/gui/gtk/glade/portato.glade +++ b/portato/gui/gtk/glade/portato.glade @@ -256,6 +256,18 @@ </widget> </child> <child> + <widget class="GtkMenuItem" id="pluginMenuItem"> + <property name="no_show_all">True</property> + <property name="label" translatable="yes">_Plugins</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="pluginMenu"> + <property name="visible">True</property> + </widget> + </child> + </widget> + </child> + <child> <widget class="GtkMenuItem" id="helpMenu"> <property name="visible">True</property> <property name="label" translatable="yes">_?</property> @@ -389,72 +401,48 @@ <property name="n_rows">4</property> <property name="n_columns">2</property> <child> - <widget class="GtkScrolledWindow" id="useListScroll"> + <widget class="GtkHBox" id="checkHB"> <property name="visible">True</property> - <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> - <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="spacing">1</property> + <property name="homogeneous">True</property> <child> - <widget class="GtkTreeView" id="useList"> + <widget class="GtkCheckButton" id="installedCheck"> <property name="visible">True</property> + <property name="no_show_all">True</property> + <property name="label" translatable="yes">Installed</property> + <property name="draw_indicator">True</property> + <signal name="button_press_event" handler="cb_button_pressed"/> </widget> + <packing> + <property name="fill">False</property> + </packing> </child> - </widget> - <packing> - <property name="right_attach">2</property> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="x_padding">5</property> - <property name="y_padding">5</property> - </packing> - </child> - <child> - <widget class="GtkVBox" id="comboVB"> - <property name="visible">True</property> <child> - <placeholder/> + <widget class="GtkCheckButton" id="maskedCheck"> + <property name="visible">True</property> + <property name="no_show_all">True</property> + <property name="label" translatable="yes">Masked</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="cb_masked_toggled"/> + </widget> + <packing> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkCheckButton" id="testingCheck"> + <property name="visible">True</property> + <property name="no_show_all">True</property> + <property name="label" translatable="yes">Testing</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="cb_testing_toggled"/> + </widget> + <packing> + <property name="fill">False</property> + <property name="position">2</property> + </packing> </child> - </widget> - <packing> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="y_options">GTK_FILL</property> - <property name="x_padding">5</property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="descLabel"> - <property name="visible">True</property> - <property name="justify">GTK_JUSTIFY_CENTER</property> - <property name="wrap">True</property> - </widget> - <packing> - <property name="right_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - <property name="y_padding">10</property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="missingLabel"> - <property name="visible">True</property> - <property name="no_show_all">True</property> - <property name="label" translatable="yes"><span foreground='red'><b>MISSING KEYWORD</b></span></property> - <property name="use_markup">True</property> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="y_options">GTK_FILL</property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="notInSysLabel"> - <property name="visible">True</property> - <property name="no_show_all">True</property> - <property name="label" translatable="yes"><b>Installed, but not in portage anymore</b></property> - <property name="use_markup">True</property> </widget> <packing> <property name="left_attach">1</property> @@ -519,55 +507,79 @@ </packing> </child> <child> - <widget class="GtkHBox" id="checkHB"> + <widget class="GtkLabel" id="notInSysLabel"> + <property name="visible">True</property> + <property name="no_show_all">True</property> + <property name="label" translatable="yes"><b>Installed, but not in portage anymore</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options">GTK_FILL</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="missingLabel"> + <property name="visible">True</property> + <property name="no_show_all">True</property> + <property name="label" translatable="yes"><span foreground='red'><b>MISSING KEYWORD</b></span></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options">GTK_FILL</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="descLabel"> + <property name="visible">True</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">True</property> + </widget> + <packing> + <property name="right_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + <property name="y_padding">10</property> + </packing> + </child> + <child> + <widget class="GtkVBox" id="comboVB"> <property name="visible">True</property> - <property name="spacing">1</property> - <property name="homogeneous">True</property> <child> - <widget class="GtkCheckButton" id="installedCheck"> - <property name="visible">True</property> - <property name="no_show_all">True</property> - <property name="label" translatable="yes">Installed</property> - <property name="draw_indicator">True</property> - <signal name="button_press_event" handler="cb_button_pressed"/> - </widget> - <packing> - <property name="fill">False</property> - </packing> + <placeholder/> </child> + </widget> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options">GTK_FILL</property> + <property name="x_padding">5</property> + </packing> + </child> + <child> + <widget class="GtkScrolledWindow" id="useListScroll"> + <property name="visible">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> <child> - <widget class="GtkCheckButton" id="maskedCheck"> + <widget class="GtkTreeView" id="useList"> <property name="visible">True</property> - <property name="no_show_all">True</property> - <property name="label" translatable="yes">Masked</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="cb_masked_toggled"/> </widget> - <packing> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - <child> - <widget class="GtkCheckButton" id="testingCheck"> - <property name="visible">True</property> - <property name="no_show_all">True</property> - <property name="label" translatable="yes">Testing</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="cb_testing_toggled"/> - </widget> - <packing> - <property name="fill">False</property> - <property name="position">2</property> - </packing> </child> </widget> <packing> - <property name="left_attach">1</property> <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="y_options">GTK_FILL</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_padding">5</property> + <property name="y_padding">5</property> </packing> </child> </widget> @@ -1077,186 +1089,186 @@ <placeholder/> </child> <child> - <widget class="GtkLabel" id="maskLabel"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="xpad">5</property> - <property name="label" translatable="yes"><u><i>Masking Keywords</i></u></property> - <property name="use_markup">True</property> - <property name="single_line_mode">True</property> - </widget> - <packing> - <property name="top_attach">7</property> - <property name="bottom_attach">8</property> - <property name="y_padding">5</property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="testLabel"> + <widget class="GtkEntry" id="useFileEdit"> <property name="visible">True</property> - <property name="xalign">0</property> - <property name="xpad">5</property> - <property name="label" translatable="yes"><u><i>Testing Keywords</i></u></property> - <property name="use_markup">True</property> - <property name="single_line_mode">True</property> </widget> <packing> - <property name="top_attach">4</property> - <property name="bottom_attach">5</property> - <property name="y_padding">5</property> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> </packing> </child> <child> - <widget class="GtkLabel" id="useLabel"> + <widget class="GtkLabel" id="useEditLabel"> <property name="visible">True</property> <property name="xalign">0</property> - <property name="xpad">5</property> - <property name="label" translatable="yes"><u><i>Use-Flags</i></u></property> - <property name="use_markup">True</property> + <property name="label" translatable="yes">File name to use, if package.use is a directory: </property> <property name="single_line_mode">True</property> </widget> <packing> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="y_padding">6</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> </packing> </child> <child> - <widget class="GtkEventBox" id="hintEB"> + <widget class="GtkCheckButton" id="usePerVersionCheck"> <property name="visible">True</property> - <child> - <widget class="GtkFrame" id="hintFrame"> - <property name="visible">True</property> - <property name="label_xalign">0</property> - <property name="shadow_type">GTK_SHADOW_OUT</property> - <child> - <widget class="GtkLabel" id="hintLabel"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="label" translatable="yes"><u>You may use the following placeholders:</u> - - <i>$(cat)</i>: category - <i>$(pkg)</i>: package name - <i>$(cat-1)/$(cat-2)</i>: first/second part of the category</property> - <property name="use_markup">True</property> - </widget> - </child> - <child> - <placeholder/> - <packing> - <property name="type">label_item</property> - </packing> - </child> - </widget> - </child> + <property name="label" translatable="yes">Add only exact version to package.use</property> + <property name="draw_indicator">True</property> </widget> <packing> <property name="right_attach">2</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> </packing> </child> <child> - <widget class="GtkCheckButton" id="maskPerVersionCheck"> + <widget class="GtkCheckButton" id="testPerVersionCheck"> <property name="visible">True</property> - <property name="label" translatable="yes">Add only exact version to package.mask/package.unmask</property> + <property name="label" translatable="yes">Add only exact version to package.keywords</property> <property name="draw_indicator">True</property> </widget> <packing> <property name="right_attach">2</property> - <property name="top_attach">8</property> - <property name="bottom_attach">9</property> + <property name="top_attach">5</property> + <property name="bottom_attach">6</property> </packing> </child> <child> - <widget class="GtkLabel" id="maskEditLabel"> + <widget class="GtkLabel" id="testEditLabel"> <property name="visible">True</property> <property name="xalign">0</property> - <property name="label" translatable="yes">File name to use, if package.mask/package.unmask is a directory: </property> + <property name="label" translatable="yes">File name to use, if package.keywords is a directory: </property> <property name="single_line_mode">True</property> </widget> <packing> - <property name="top_attach">9</property> - <property name="bottom_attach">10</property> + <property name="top_attach">6</property> + <property name="bottom_attach">7</property> </packing> </child> <child> - <widget class="GtkEntry" id="maskFileEdit"> + <widget class="GtkEntry" id="testFileEdit"> <property name="visible">True</property> </widget> <packing> <property name="left_attach">1</property> <property name="right_attach">2</property> - <property name="top_attach">9</property> - <property name="bottom_attach">10</property> + <property name="top_attach">6</property> + <property name="bottom_attach">7</property> </packing> </child> <child> - <widget class="GtkEntry" id="testFileEdit"> + <widget class="GtkEntry" id="maskFileEdit"> <property name="visible">True</property> </widget> <packing> <property name="left_attach">1</property> <property name="right_attach">2</property> - <property name="top_attach">6</property> - <property name="bottom_attach">7</property> + <property name="top_attach">9</property> + <property name="bottom_attach">10</property> </packing> </child> <child> - <widget class="GtkLabel" id="testEditLabel"> + <widget class="GtkLabel" id="maskEditLabel"> <property name="visible">True</property> <property name="xalign">0</property> - <property name="label" translatable="yes">File name to use, if package.keywords is a directory: </property> + <property name="label" translatable="yes">File name to use, if package.mask/package.unmask is a directory: </property> <property name="single_line_mode">True</property> </widget> <packing> - <property name="top_attach">6</property> - <property name="bottom_attach">7</property> + <property name="top_attach">9</property> + <property name="bottom_attach">10</property> </packing> </child> <child> - <widget class="GtkCheckButton" id="testPerVersionCheck"> + <widget class="GtkCheckButton" id="maskPerVersionCheck"> <property name="visible">True</property> - <property name="label" translatable="yes">Add only exact version to package.keywords</property> + <property name="label" translatable="yes">Add only exact version to package.mask/package.unmask</property> <property name="draw_indicator">True</property> </widget> <packing> <property name="right_attach">2</property> - <property name="top_attach">5</property> - <property name="bottom_attach">6</property> + <property name="top_attach">8</property> + <property name="bottom_attach">9</property> </packing> </child> <child> - <widget class="GtkCheckButton" id="usePerVersionCheck"> + <widget class="GtkEventBox" id="hintEB"> <property name="visible">True</property> - <property name="label" translatable="yes">Add only exact version to package.use</property> - <property name="draw_indicator">True</property> + <child> + <widget class="GtkFrame" id="hintFrame"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">GTK_SHADOW_OUT</property> + <child> + <widget class="GtkLabel" id="hintLabel"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><u>You may use the following placeholders:</u> + + <i>$(cat)</i>: category + <i>$(pkg)</i>: package name + <i>$(cat-1)/$(cat-2)</i>: first/second part of the category</property> + <property name="use_markup">True</property> + </widget> + </child> + <child> + <placeholder/> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + </child> </widget> <packing> <property name="right_attach">2</property> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> </packing> </child> <child> - <widget class="GtkLabel" id="useEditLabel"> + <widget class="GtkLabel" id="useLabel"> <property name="visible">True</property> <property name="xalign">0</property> - <property name="label" translatable="yes">File name to use, if package.use is a directory: </property> + <property name="xpad">5</property> + <property name="label" translatable="yes"><u><i>Use-Flags</i></u></property> + <property name="use_markup">True</property> <property name="single_line_mode">True</property> </widget> <packing> - <property name="top_attach">3</property> - <property name="bottom_attach">4</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_padding">6</property> </packing> </child> <child> - <widget class="GtkEntry" id="useFileEdit"> + <widget class="GtkLabel" id="testLabel"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">5</property> + <property name="label" translatable="yes"><u><i>Testing Keywords</i></u></property> + <property name="use_markup">True</property> + <property name="single_line_mode">True</property> + </widget> + <packing> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + <property name="y_padding">5</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="maskLabel"> <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">5</property> + <property name="label" translatable="yes"><u><i>Masking Keywords</i></u></property> + <property name="use_markup">True</property> + <property name="single_line_mode">True</property> </widget> <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">3</property> - <property name="bottom_attach">4</property> + <property name="top_attach">7</property> + <property name="bottom_attach">8</property> + <property name="y_padding">5</property> </packing> </child> </widget> diff --git a/portato/gui/gtk/windows.py b/portato/gui/gtk/windows.py index a27d156..2de772f 100644 --- a/portato/gui/gtk/windows.py +++ b/portato/gui/gtk/windows.py @@ -530,7 +530,8 @@ class PackageTable: try: self.queue.append(self.actual_package().get_cpv(), unmerge = True) except PackageNotFoundException, e: - masked_dialog(e[0]) + debug("Package could not be found",e[0], error = 1) + #masked_dialog(e[0]) def cb_combo_changed (self, combo): """Callback for the changed ComboBox. @@ -720,7 +721,17 @@ class MainWindow (Window): self.cfg.modify_external_configs() + # set plugins and plugin-menu plugin.load_plugins() + menus = plugin.get_plugins().get_plugin_menus() + if menus: + self.tree.get_widget("pluginMenuItem").set_no_show_all(False) + pluginMenu = self.tree.get_widget("pluginMenu") + + for m in menus: + item = gtk.MenuItem(m.label) + item.connect("activate", m.call) + pluginMenu.append(item) # set vpaned position vpaned = self.tree.get_widget("vpaned") diff --git a/portato/helper.py b/portato/helper.py index b41dbbe..604f2c1 100644 --- a/portato/helper.py +++ b/portato/helper.py @@ -11,6 +11,8 @@ # Written by René 'Necoro' Neumann <necoro@necoro.net> et.al. import traceback, os.path, sys +from itertools import chain + DEBUG = True @@ -77,6 +79,16 @@ def am_i_root (): else: return False +def flatten (listOfLists): + """Flattens the given list of lists. + + @param listOfLists: the list of lists to flatten + @type listOfLists: list of lists + @returns: flattend list + @rtyoe: list""" + + return list(chain(*listOfLists)) + def unique_array(s): """Stolen from portage_utils: lifted from python cookbook, credit: Tim Peters diff --git a/portato/plugin.py b/portato/plugin.py index 4ce4099..c6e3288 100644 --- a/portato/plugin.py +++ b/portato/plugin.py @@ -14,7 +14,7 @@ import os, os.path from xml.dom.minidom import parse from constants import PLUGIN_DIR -from helper import debug +from helper import debug, flatten class ParseException (Exception): pass @@ -23,6 +23,46 @@ def error (reason, p): reason = "("+reason+")" debug("Malformed plugin:", p, reason, minus=1, error = 1) +class Menu: + """A single <menu>-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 ParseException: on parsing errors""" + + if not label: + raise ParseException, "label attribute missing" + + if not call: + raise ParseException, "call attribute missing" + + 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 ParseException, imp+" cannot be imported" + + try: + self.call = eval("mod."+call) # build function + except AttributeError: + raise ParseException, call+" cannot be imported" + else: + try: + self.call = eval(call) + except AttributeError: + raise ParseException, call+" cannot be imported" + class Connect: """A single <connect>-element.""" @@ -129,6 +169,7 @@ class Plugin: self.author = author self._import = None self.hooks = [] + self.menus = [] def parse_hooks (self, hooks): """Gets a list of <hook>-elements and parses them. @@ -143,6 +184,18 @@ class Plugin: hook.parse_connects(h.getElementsByTagName("connect")) self.hooks.append(hook) + def parse_menus (self, menus): + """Gets a list of <menu>-elements and parses them. + + @param menus: the list of elements + @type menus: NodeList + + @raises ParseException: on parsing errors""" + + for m in menus: + menu = Menu(self, m.getAttribute("label"), m.getAttribute("call")) + self.menus.append(menu) + def set_import (self, imports): """This gets a list of imports and parses them - setting the import needed to call the plugin. @@ -200,6 +253,9 @@ class PluginQueue: def get_plugin_data (self): return [(x.name, x.author) for x in self.list] + def get_plugin_menus (self): + return flatten([x.menus for x in self.list]) + def hook (self, hook, *hargs, **hkwargs): """This is a method taking care of calling the plugins. @@ -230,9 +286,15 @@ class PluginQueue: debug(imp,"cannot be imported", error = 1) return - f = eval("mod."+cmd.hook.call) # build function + try: + f = eval("mod."+cmd.hook.call) # build function + except AttributeError: + debug(cmd.hook.call,"cannot be imported", error = 1) else: - f = eval(cmd.hook.call) + try: + f = eval(cmd.hook.call) + except AttributeError: + debug(cmd.hook.call,"cannot be imported", error = 1) f(*hargs, **hkwargs) # call function @@ -284,6 +346,7 @@ class PluginQueue: plugin = Plugin(p, elem.getAttribute("name"), elem.getAttribute("author")) plugin.parse_hooks(elem.getElementsByTagName("hook")) plugin.set_import(elem.getElementsByTagName("import")) + plugin.parse_menus(elem.getElementsByTagName("menu")) self.list.append(plugin) diff --git a/portato/plugins/etc_proposals.py b/portato/plugins/etc_proposals.py index bf58c06..e830115 100644 --- a/portato/plugins/etc_proposals.py +++ b/portato/plugins/etc_proposals.py @@ -10,10 +10,12 @@ # # Written by René 'Necoro' Neumann <necoro@necoro.net> -from portato.helper import debug +from portato.helper import debug, am_i_root from portato.backend import system -import os +from portato.gui.gtk.dialogs import not_root_dialog + +from subprocess import Popen from etcproposals.etcproposals_lib import EtcProposals class PortatoEtcProposals(EtcProposals): @@ -32,4 +34,10 @@ def etc_prop (*args, **kwargs): debug(l,"files to update") if l > 0: - os.system("etc-proposals") + Popen("etc-proposals") + +def etc_prop_menu (*args, **kwargs): + if not am_i_root(): + not_root_dialog() + else: + Popen("etc-proposals") |