summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Sivak <msivak@redhat.com>2010-08-23 15:14:06 +0200
committerMartin Sivak <msivak@redhat.com>2010-08-23 16:49:35 +0200
commitb6f503fc174302bdc3fc05dcc33e737432037218 (patch)
treef12c8b094c2b9573fdb48b6a263bfe517435b859
parent8079f17a282c83825e07b76cd6bec972ba2ba112 (diff)
downloadfirstaidkit-b6f503fc174302bdc3fc05dcc33e737432037218.tar.gz
firstaidkit-b6f503fc174302bdc3fc05dcc33e737432037218.tar.xz
firstaidkit-b6f503fc174302bdc3fc05dcc33e737432037218.zip
Add List/Config question (something similar to about:config) and Gtk GUI for it
-rw-r--r--frontend/frontend_gtk.py85
-rw-r--r--frontend/gtk-list.xml105
-rw-r--r--plugins/plugin_examples/dialogue.py18
-rw-r--r--pyfirstaidkit/reporting.py23
4 files changed, 229 insertions, 2 deletions
diff --git a/frontend/frontend_gtk.py b/frontend/frontend_gtk.py
index e66799d..86b6ed0 100644
--- a/frontend/frontend_gtk.py
+++ b/frontend/frontend_gtk.py
@@ -311,6 +311,54 @@ class CallbacksFlagList(object):
self._dialog.destroy()
return True
+class ListDialog(object):
+ def __init__(self, title, description, items, dir=""):
+ gtkb = gtk.Builder()
+ gtkb.add_from_file(os.path.join(dir, "gtk-list.xml"))
+ self._dialog = gtkb.get_object("listdialog")
+ self._dialog.set_title(title)
+ self._store = gtkb.get_object("store")
+ self._label = gtkb.get_object("label")
+ self._label.set_text(description)
+ self._view = gtkb.get_object("view")
+
+ rend_text = gtk.CellRendererText()
+ rend_text_edit = gtk.CellRendererText()
+ rend_text_edit.set_property("editable", True)
+ rend_text_edit.connect('edited', self.edited_cb, self._store)
+
+ col_0 = gtk.TreeViewColumn('Key', rend_text, text = 1)
+ col_1 = gtk.TreeViewColumn('Value', rend_text_edit, text = 2)
+ self._view.append_column(col_0)
+ self._view.append_column(col_1)
+
+ map(self._store.append, items)
+
+ gtkb.connect_signals(self)
+
+ def items(self):
+ F = lambda row: (row[0], row[2])
+ return map(F, self._store)
+
+ def run(self):
+ self._dialog.show_all()
+ return self._dialog.run()
+
+ def edited_cb(self, cell, path, new_data, store):
+ store[path][2] = new_data
+
+ def destroy(self):
+ self._dialog.destroy()
+
+ def cb_ok(self, data):
+ self._dialog.response(gtk.RESPONSE_ACCEPT)
+
+ def cb_cancel(self, data):
+ self._dialog.response(gtk.RESPONSE_CANCEL)
+
+ def cb_close(self, data):
+ self._dialog.response(gtk.RESPONSE_CANCEL)
+
class MainWindow(object):
_cancel_answer = object()
_no_answer = object()
@@ -538,6 +586,9 @@ class MainWindow(object):
elif message["action"]==reporting.CHOICE_QUESTION:
gobject.idle_add(_o, self.choice_question, message)
+ elif message["action"]==reporting.CONFIG_QUESTION:
+ gobject.idle_add(_o, self.config_question, message)
+
elif message["action"]==reporting.FILENAME_QUESTION:
gobject.idle_add(_o, self.filename_question, message)
@@ -709,6 +760,40 @@ class MainWindow(object):
gtk.gdk.flush()
gtk.gdk.threads_leave()
+ def config_question(self, message):
+ """Return the user's answers.
+
+ Return self._no_answer on invalid answer,
+ self._cancel_answer if the user wants to cancel.
+
+ """
+
+ try:
+ gtk.gdk.threads_enter()
+
+ question = message["message"]
+ dlg = ListDialog(title = question.title,
+ description = question.description,
+ items = question.items,
+ dir = os.path.dirname(self._glade.relative_file("."))
+ )
+
+ res = dlg.run()
+
+ print "Dlg: ", res
+
+ if res==gtk.RESPONSE_ACCEPT:
+ print dlg.items()
+ question.send_answer(message, dlg.items(), origin = self)
+ elif res==gtk.RESPONSE_CANCEL:
+ question.send_answer(message, [], origin = self)
+ else:
+ message["reply"].end(level = reporting.FIRSTAIDKIT)
+ finally:
+ # schedule dialog destroy
+ dlg.destroy()
+ gtk.gdk.flush()
+ gtk.gdk.threads_leave()
def text_question(self, message):
"""Return the user's answer.
diff --git a/frontend/gtk-list.xml b/frontend/gtk-list.xml
new file mode 100644
index 0000000..bde40e6
--- /dev/null
+++ b/frontend/gtk-list.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkListStore" id="store">
+ <columns>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ <!-- column-name description -->
+ <column type="gchararray"/>
+ <!-- column-name value -->
+ <column type="gchararray"/>
+ <!-- column-name tooltip -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkDialog" id="listdialog">
+ <property name="border_width">5</property>
+ <property name="type_hint">normal</property>
+ <property name="has_separator">False</property>
+ <signal name="close" handler="cb_close"/>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkTreeView" id="view">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">store</property>
+ <property name="tooltip_column">3</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">label</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="ok_button">
+ <property name="label" translatable="yes">OK</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <signal name="clicked" handler="cb_ok"/>
+ <signal name="activate" handler="cb_ok"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel_button">
+ <property name="label" translatable="yes">Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <signal name="clicked" handler="cb_cancel"/>
+ <signal name="activate" handler="cb_cancel"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="0">ok_button</action-widget>
+ <action-widget response="0">cancel_button</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/plugins/plugin_examples/dialogue.py b/plugins/plugin_examples/dialogue.py
index 1ff007a..bcc5a00 100644
--- a/plugins/plugin_examples/dialogue.py
+++ b/plugins/plugin_examples/dialogue.py
@@ -26,8 +26,8 @@ from pyfirstaidkit.issue import SimpleIssue
class DialoguePlugin(Plugin):
"""This plugin demonstrates asking the user for information."""
name = "DialoguePlugin"
- version = "0.0.1"
- author = "Miloslav Trmač"
+ version = "0.0.2"
+ author = "Miloslav Trmač & Martin Sivák"
def __init__(self, *args, **kwargs):
Plugin.__init__(self, *args, **kwargs)
@@ -76,6 +76,20 @@ class DialoguePlugin(Plugin):
level = PLUGIN)
self._reporting.info("File name: %s" % repr(s), origin = self,
level = PLUGIN)
+
+ config_options = [
+ ("id:1", "PL", "5", "Password length"),
+ ("id:2", "PS", "C", "Password strength"),
+ ("id:3", "PL", "aA0.", "Password chars"),
+ ]
+
+ s = self._reporting.config_question_wait("Setup choices",
+ "Set preferred values",
+ config_options, origin = self,
+ level = PLUGIN)
+ self._reporting.info("Options: %s" % repr(s), origin = self,
+ level = PLUGIN)
+
self._issue.set(checked = True, happened = False,
reporting = self._reporting, origin = self,
level = PLUGIN)
diff --git a/pyfirstaidkit/reporting.py b/pyfirstaidkit/reporting.py
index 5c859ee..9151db9 100644
--- a/pyfirstaidkit/reporting.py
+++ b/pyfirstaidkit/reporting.py
@@ -47,6 +47,7 @@ CHOICE_QUESTION = 990 #a Question object, "reply" specifies a Reports object
TEXT_QUESTION = 991
FILENAME_QUESTION = 992
PASSWORD_QUESTION = 993
+CONFIG_QUESTION = 994
ANSWER = 999 #Data sent in reply to a *_QUESTION
END = 1000 #End of operations, final message
@@ -73,6 +74,18 @@ class Question(object):
importance = question_message["importance"], inreplyto = self)
question_message["reply"].end(level = FIRSTAIDKIT)
+class ConfigQuestion(Question):
+ """A question that allows list of configurable variables
+
+ Each item is a tuple (id, title, value, tooltip)"""
+
+ def __init__(self, title, description, items):
+ super(ConfigQuestion, self).__init__(title)
+ assert len(items) > 0
+ self.title = title
+ self.description = description
+ self.items = items
+
class ChoiceQuestion(Question):
"""A question that offers multiple options.
@@ -326,3 +339,13 @@ class Reports(object):
def filename_question_wait(self, *args, **kwargs):
return self.__blocking_question(self.filename_question, args, kwargs)
+
+ def config_question(self, reply_mb, title, description, items, origin, level = PLUGIN,
+ importance = logging.ERROR):
+ q = ConfigQuestion(title, description, items)
+ self.put(q, origin, level, CONFIG_QUESTION, importance = importance,
+ reply = reply_mb)
+ return q
+
+ def config_question_wait(self, *args, **kwargs):
+ return self.__blocking_question(self.config_question, args, kwargs)