diff options
-rw-r--r-- | frontend/frontend_gtk.py | 85 | ||||
-rw-r--r-- | frontend/gtk-list.xml | 105 | ||||
-rw-r--r-- | plugins/plugin_examples/dialogue.py | 18 | ||||
-rw-r--r-- | pyfirstaidkit/reporting.py | 23 |
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) |