[node-patches] Change in ovirt-node[master]: [DRAFT] ui: Introduce Builder pattern

fabiand at fedoraproject.org fabiand at fedoraproject.org
Fri Jan 11 18:41:50 UTC 2013


Fabian Deutsch has uploaded a new change for review.

Change subject: [DRAFT] ui: Introduce Builder pattern
......................................................................

[DRAFT] ui: Introduce Builder pattern

We introduce a builder pattern [0] to have a better way to orientate and
documentation by choosing a well known design pattern.

[0] http://en.wikipedia.org/wiki/Builder_pattern

Change-Id: Ia6f52e82631483b27ad50a6be64f6b72b4539bc6
Signed-off-by: Fabian Deutsch <fabiand at fedoraproject.org>
---
M scripts/tui/src/ovirt/node/app.py
M scripts/tui/src/ovirt/node/plugins.py
M scripts/tui/src/ovirt/node/setup/status_page.py
M scripts/tui/src/ovirt/node/ui/__init__.py
D scripts/tui/src/ovirt/node/ui/builder.py
D scripts/tui/src/ovirt/node/ui/tui.py
M scripts/tui/src/ovirt/node/utils/__init__.py
7 files changed, 401 insertions(+), 870 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-node refs/changes/12/10912/1

diff --git a/scripts/tui/src/ovirt/node/app.py b/scripts/tui/src/ovirt/node/app.py
index 993a56a..aed89da 100644
--- a/scripts/tui/src/ovirt/node/app.py
+++ b/scripts/tui/src/ovirt/node/app.py
@@ -25,13 +25,13 @@
 which communicate with each other.
 """
 
-from ovirt.node import base, utils, plugins
+from ovirt.node import base, utils, plugins, ui
 from ovirt.node.config import defaults
 from ovirt.node.utils import system
+from ovirt.node.ui import urwid_builder
 import argparse
 import logging
 import logging.config
-import ovirt.node.ui.tui
 
 LOGGING = {
     'version': 1,
@@ -79,22 +79,39 @@
 #                    format="%(asctime)s %(levelname)s %(name)s %(message)s")
 
 
+class Action(base.Base):
+    callback = None
+
+    def __call__(self, *args, **kwargs):
+        r = None
+        if self.callback:
+            r = self.callback(*args, **kwargs)
+        else:
+            self.logger.warning("No callback for %s" % self)
+        return r
+
+    def __str__(self):
+        return "<%s '%s'>" % (self.__class__.__name__, self.callback)
+
+
 class Application(base.Base):
+    """
+    _actions map an action type to a handler
+    """
     __plugins = {}
+
+    _action_handlers = {}
 
     ui = None
 
-    def __init__(self, plugin_base, ui_backend="urwid"):
+    def __init__(self, plugin_base, ui=urwid_builder.UrwidWindow):
         super(Application, self).__init__()
         self.logger.info(("Starting '%s' application " +
-                          "with '%s' UI") % (plugin_base, ui_backend))
+                          "with '%s' UI") % (plugin_base, ui))
 
         self.__parse_cmdline()
 
-        ui_backend_class = {
-            "urwid": ovirt.node.ui.tui.UrwidTUI
-        }[ui_backend]
-        self.ui = ui_backend_class(self)
+        self.ui = ui("screen", self)
         self.plugin_base = plugin_base
 
     def __parse_cmdline(self):
@@ -117,26 +134,12 @@
         self.logger.debug("Commandline arguments: %s" % self.args)
 
     def plugins(self):
+        """Retrieve all loaded plugins
+        """
         return self.__plugins
 
-    def __load_plugins(self):
-        self.__plugins = {}
-        for m in plugins.load(self.plugin_base):
-            if hasattr(m, "Plugin"):
-                self.logger.debug("Found plugin in module: %s" % m)
-                plugin = m.Plugin(self)
-                self.logger.debug("Registering plugin '%s': %s" %
-                                  (plugin.name(), plugin))
-                self.__plugins[plugin.name()] = plugin
-            else:
-                self.logger.debug("Found no plugin in module: %s" % m)
-
-        for plugin in self.__plugins.values():
-            self.logger.debug("Loading plugin %s" % plugin)
-            self.ui.register_plugin(plugin.ui_name(), plugin)
-
     def get_plugin(self, mixed):
-        """Find a plugin by name or type
+        """Find a plugin by instance, name, or type
         """
         mtype = type(mixed)
         self.logger.debug("Looking up plugin: %s (%s)" % (mixed, mtype))
@@ -154,15 +157,28 @@
         self.logger.debug("Found plugin for type: %s" % plugin)
         return plugin
 
-    def __drop_to_shell(self):
-        with self.ui.suspended():
-            utils.process.system("reset ; bash")
+    def current_plugin(self):
+        return self.navigation.current_plugin()
 
-    def __check_terminal_size(self):
-        cols, rows = self.ui.size()
-        if cols < 80 or rows < 24:
-            self.logger.warning("Window size is too small: %dx%d" % (cols,
-                                                                     rows))
+    def handle_action(self, action):
+        """Handle an app.Action issued by some component (e.g. UI)
+        """
+        assert type(action) is Action
+        self.logger.debug("Handling UI action: %s" % action)
+        if Action.callback:
+            action(self)
+        else:
+            action = action if type(action) is type else type(action)
+            app = self.application
+            window = app.ui
+            current_plugin = window.navigation.current_plugin()
+            handlers= {ui.SaveAction: current_plugin._on_ui_save,
+                       ui.CloseAction: window.close_topmost_dialog,
+                       ui.ResetAction: current_plugin._on_ui_reset,
+                       ui.RevalidateAction: current_plugin._on_ui_validate,
+                       ui.QuitAction: app.quit}
+            handlers.update(self.action_handlers) 
+            handlers[action](self)
 
     def model(self, plugin_name):
         model = None
@@ -189,3 +205,31 @@
     def quit(self):
         self.logger.info("Quitting")
         self.ui.quit()
+
+    def __load_plugins(self):
+        """Load all plugins which are found in the given plugin_base
+        """
+        self.__plugins = {}
+        for m in plugins.load(self.plugin_base):
+            if hasattr(m, "Plugin"):
+                self.logger.debug("Found plugin in module: %s" % m)
+                plugin = m.Plugin(self)
+                self.logger.debug("Registering plugin '%s': %s" %
+                                  (plugin.name(), plugin))
+                self.__plugins[plugin.name()] = plugin
+            else:
+                self.logger.debug("Found no plugin in module: %s" % m)
+
+        for plugin in self.__plugins.values():
+            self.logger.debug("Loading plugin %s" % plugin)
+            self.ui.register_plugin(plugin.ui_name(), plugin)
+
+    def __drop_to_shell(self):
+        with self.ui.suspended():
+            utils.process.system("reset ; bash")
+
+    def __check_terminal_size(self):
+        cols, rows = self.ui.size()
+        if cols < 80 or rows < 24:
+            self.logger.warning("Window size is too small: %dx%d" % (cols,
+                                                                     rows))
diff --git a/scripts/tui/src/ovirt/node/plugins.py b/scripts/tui/src/ovirt/node/plugins.py
index a43495b..6d9308c 100644
--- a/scripts/tui/src/ovirt/node/plugins.py
+++ b/scripts/tui/src/ovirt/node/plugins.py
@@ -22,7 +22,7 @@
 """
 This contains much stuff related to plugins
 """
-from ovirt.node import base, exceptions, valid
+from ovirt.node import base, exceptions, valid, ui
 import pkgutil
 
 
@@ -285,7 +285,25 @@
         else:
             self.logger.info("Changes were not merged.")
 
-        return successfull_merge
+        return self.__handle_merge_result(successfull_merge)
+
+    def __handle_merge_result(self, result):
+        """Checks if a page/dialog was returned and displays it accordingly
+        """
+        self.logger.debug("Parsing plugin merge result: %s" % result)
+        window = self.application.ui
+
+        if type(result) in [ui.Page]:
+            window.show_on_page(result)
+
+        elif type(result) in [ui.Dialog]:
+            dialog = window.show_on_dialog(result)
+
+            def on_item_close_changed_cb(i, v):
+                dialog.close()
+            result.connect_signal("close", on_item_close_changed_cb)
+
+        return result
 
     def _on_ui_reset(self):
         """Called when a ResetButton was clicked
@@ -295,6 +313,9 @@
         self.logger.debug("Request to discard model changes: %s" % changes)
         self.__changes = {}
 
+    def _on_ui_validate(self):
+        self.check_semantics()
+
     def pending_changes(self, only_effective_changes=True,
                         include_invalid=False):
         """Return all changes which happened since the last on_merge call
diff --git a/scripts/tui/src/ovirt/node/setup/status_page.py b/scripts/tui/src/ovirt/node/setup/status_page.py
index 7a2c03a..2803e05 100644
--- a/scripts/tui/src/ovirt/node/setup/status_page.py
+++ b/scripts/tui/src/ovirt/node/setup/status_page.py
@@ -73,42 +73,37 @@
         aligned = lambda l: l.ljust(14)
 
         # Network related widgets, appearing in one row
-        network_widgets = [
-                ("networking",
-                    ui.KeywordLabel(aligned("Networking: "))),
-                ("networking.bridge",
-                    ui.KeywordLabel("Bridge: ")),
-            ]
+        network_widgets = [ui.KeywordLabel("networking",
+                                           aligned("Networking: ")),
+                           ui.KeywordLabel("networking.bridge",
+                                           "Bridge: "),
+                           ]
 
-        action_widgets = [
-            ("action.lock", ui.Button("Lock")),
-            ("action.logoff", ui.Button("Log Off")),
-            ("action.restart", ui.Button("Restart")),
-            ("action.poweroff", ui.Button("Poweroff")),
-        ]
+        action_widgets = [ui.Button("action.lock", "Lock"),
+                          ui.Button("action.logoff", "Log Off"),
+                          ui.Button("action.restart", "Restart"),
+                          ui.Button("action.poweroff", "Poweroff")
+                          ]
 
-        widgets = [
-            ("status",
-                ui.KeywordLabel(aligned("Status: "))),
-            ("status._space", ui.Divider()),
+        widgets = [ui.KeywordLabel("status", aligned("Status: ")),
+                   ui.Divider("divider[0]"),
 
-            ("network._column", ui.Row(network_widgets)),
-            ("network._space", ui.Divider()),
+                   ui.Row("row[0]", network_widgets),
+                   ui.Divider("divider[1]"),
 
-            ("logs",
-                ui.KeywordLabel(aligned("Logs: "))),
-            ("logs._space", ui.Divider()),
+                   ui.KeywordLabel("logs", aligned("Logs: ")),
+                   ui.Divider("divider[2]"),
 
-            ("libvirt.num_guests",
-                ui.KeywordLabel(aligned("Running VMs: "))),
-            ("libvirt._space", ui.Divider()),
+                   ui.KeywordLabel("libvirt.num_guests",
+                                   aligned("Running VMs: ")),
+                   ui.Divider("divider[3]"),
 
-            ("support.hint", ui.Label("Press F8 for support menu")),
-            ("support._space", ui.Divider()),
+                   ui.Label("support.hint", "Press F8 for support menu"),
+                   ui.Divider("divider[4]"),
 
-            ("action.hostkey", ui.Button("View Host Key")),
+                   ui.Button("action.hostkey", "View Host Key"),
 
-            ("action._row", ui.Row(action_widgets)),
+                   ui.Row("row[1]", action_widgets),
         ]
         # Save it "locally" as a dict, for better accessability
         self._widgets = dict(widgets)
@@ -153,37 +148,33 @@
 
     def _build_dialog(self, path, txt, widgets):
         self._widgets.update(dict(widgets))
-        self._widgets[path] = ui.Dialog(txt, widgets)
+        self._widgets[path] = ui.Dialog(path, txt, widgets)
         return self._widgets[path]
 
     def _build_hostkey_dialog(self):
         ssh = security.Ssh()
         fp, hk = ssh.get_hostkey()
         dialog = self._build_dialog("dialog.hostkey", "Host Key", [
-            ("hostkey.fp._label",
-                ui.Label("RSA Host Key Fingerprint:")),
-            ("hostkey.fp",
-                ui.Label(fp)),
+                ui.Label("hostkey.label[0]", "RSA Host Key Fingerprint:"),
+                ui.Label("hostkey.fp", fp),
 
-            ("hostkey._divider", ui.Divider()),
+                ui.Divider("hostkey.divider[0]"),
 
-            ("hostkey._label",
-                ui.Label("RSA Host Key:")),
-            ("hostkey",
-                ui.Label("\n".join(textwrap.wrap(hk, 64)))),
-        ])
+                ui.Label("hostkey.label[1]", "RSA Host Key:"),
+                ui.Label("hostkey", "\n".join(textwrap.wrap(hk, 64))),
+                ])
         dialog.buttons = []
         return dialog
 
     def _build_lock_dialog(self):
-        widgets = [
-            ("label[0]", ui.Header("Enter the admin password to unlock")),
-            ("username", ui.KeywordLabel("Username: ", os.getlogin())),
-            ("password",
-                ui.PasswordEntry("Password:"))
-        ]
+        widgets = [ui.Header("lock.label[0]",
+                             "Enter the admin password to unlock"),
+                   ui.KeywordLabel("username", "Username: ", os.getlogin()),
+                   ui.PasswordEntry("password", "Password:")
+                   ]
+
         self._widgets = dict(widgets)
-        page = ui.Dialog("This screen is locked.", widgets)
-        page.buttons = [("action.unlock", ui.Button("Unlock"))]
+        page = ui.Dialog("lock.dialog", "This screen is locked.", widgets)
+        page.buttons = [ui.Button("action.unlock", "Unlock")]
         page.escape_key = None
         return page
diff --git a/scripts/tui/src/ovirt/node/ui/__init__.py b/scripts/tui/src/ovirt/node/ui/__init__.py
index 0af2e72..09cccb2 100644
--- a/scripts/tui/src/ovirt/node/ui/__init__.py
+++ b/scripts/tui/src/ovirt/node/ui/__init__.py
@@ -18,7 +18,8 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 # MA  02110-1301, USA.  A copy of the GNU General Public License is
 # also available at http://www.gnu.org/copyleft/gpl.html.
-from ovirt.node import base
+from ovirt.node import base, app
+from ovirt.node.utils import Timer
 
 """
 This contains abstract UI Elements
@@ -31,19 +32,25 @@
     This basically provides signals to communicate between real UI widget and
     the plugins
     """
+    name = None
     _signal_cbs = None
 
-    def __init__(self):
+    def __init__(self, name=None):
         """Registers all widget signals.
         All signals must be given in self.signals
         """
         super(Element, self).__init__()
-        self.logger.debug("Initializing new %s" % self)
+        self.name = name
+        self.logger.debug("Initializing %s" % self)
 
-    def set_text(self, value):
-        """A general way to set the "text" of a widget
+    def value(self, value):
+        """A general way to set the "value" of a widget
+        Can be a text or selection, ...
         """
         raise NotImplementedError
+
+    def __str__(self):
+        return "<%s '%s'>" % (self.__class__.__name__, self.name)
 
 
 class InputElement(Element):
@@ -51,8 +58,7 @@
     """
 
     def __init__(self, name, is_enabled):
-        super(InputElement, self).__init__()
-        self.name = name
+        super(InputElement, self).__init__(name)
         self.enabled(is_enabled)
         self.text("")
 
@@ -68,7 +74,7 @@
             self._text = text
         return self._text
 
-    def set_text(self, txt):
+    def value(self, txt):
         self.text(txt)
 
 
@@ -78,8 +84,8 @@
     children = []
     title = None
 
-    def __init__(self, children, title=None):
-        super(ContainerElement, self).__init__()
+    def __init__(self, name, children, title=None):
+        super(ContainerElement, self).__init__(name)
         self.children = children
         self.title = title
 
@@ -93,12 +99,12 @@
     """Abstract Window definition
     """
 
-    app = None
+    application = None
 
-    def __init__(self, app):
-        super(Window, self).__init__()
-        self.logger.info("Creating UI for application '%s'" % app)
-        self.app = app
+    def __init__(self, name, application):
+        super(Window, self).__init__(name=name)
+        self.logger.info("Creating UI for application '%s'" % application)
+        self.application = application
 
         self._plugins = {}
         self._hotkeys = {}
@@ -131,6 +137,37 @@
     def switch_to_plugin(self, plugin, check_for_changes=True):
         """Show the given plugin
         """
+        self.logger.debug("Switching to plugin " +
+                          "%s, with checks? %s" % (plugin, check_for_changes))
+        if check_for_changes and self._check_outstanding_changes():
+            return
+        self._current_plugin = plugin
+        with Timer() as t:
+            self.show_on_page(plugin.ui_content())
+        self.logger.debug("Build and displayed plugin_page in %s seconds" % t)
+
+    def show_on_page(self, ui_page):
+        """Show the ui.Page as a page in the window
+        """
+        raise NotImplementedError
+
+    def show_on_dialog(self, ui_dialog):
+        """Show the ui.Dialog as a dialog in the window
+        Retruns:
+            The ui.Dialog instance (so it can be closed)
+        """
+        raise NotImplementedError
+
+    def suspended(self):
+        """Supspends the screen to do something in the foreground
+        Returns:
+            ...
+        """
+        raise NotImplementedError
+
+    def force_redraw(self):
+        """Forces a complete redraw of the UI
+        """
         raise NotImplementedError
 
     class Navigation(base.Base):
@@ -152,6 +189,9 @@
                               if p.has_ui()]
             self.logger.debug("Available plugins with ui: %s" % sorted_plugins)
             return sorted_plugins
+
+        def current_plugin(self):
+            return self.__current_plugin
 
         def to_plugin(self, plugin_candidate):
             """Goes to the plugin (by instance or type)
@@ -204,8 +244,8 @@
     """
     buttons = []
 
-    def __init__(self, children, title=None):
-        super(Page, self).__init__(children, title)
+    def __init__(self, name, children, title=None):
+        super(Page, self).__init__(name, children, title)
         self.buttons = self.buttons or [
                         (None, SaveButton()),
                         (None, ResetButton())
@@ -218,19 +258,13 @@
 
     escape_key = "esc"
 
-    def __init__(self, title, children):
-        super(Dialog, self).__init__(children, title)
+    def __init__(self, name, title, children):
+        super(Dialog, self).__init__(name, children, title)
         self.close(False)
 
     @Element.signal_change
     def close(self, v=True):
         self._close = v
-
-
-class InfoDialog(Dialog):
-    def __init__(self, title, children):
-        super(InfoDialog, self).__init__(title, children)
-        self.buttons = [(None, CloseButton())]
 
 
 class Row(ContainerElement):
@@ -243,8 +277,8 @@
     """Represents a r/o label
     """
 
-    def __init__(self, text):
-        super(Label, self).__init__()
+    def __init__(self, name, text):
+        super(Label, self).__init__(name)
         self.text(text)
 
     @Element.signal_change
@@ -253,15 +287,15 @@
             self._text = text
         return self._text
 
-    def set_text(self, txt):
-        self.text(txt)
+    def value(self, value):
+        self.text(value)
 
 
 class Header(Label):
     template = "\n  %s\n"
 
-    def __init__(self, text, template=template):
-        super(Header, self).__init__(text)
+    def __init__(self, name, text, template=template):
+        super(Header, self).__init__(name, text)
         self.template = template
 
 
@@ -270,8 +304,8 @@
     E.g.: <b>Networking:</b> Enabled
     """
 
-    def __init__(self, keyword, text=""):
-        super(KeywordLabel, self).__init__(text)
+    def __init__(self, name, keyword, text=""):
+        super(KeywordLabel, self).__init__(name, text)
         self.keyword = keyword
 
 
@@ -280,8 +314,8 @@
     TODO multiline
     """
 
-    def __init__(self, label, enabled=True, align_vertical=False):
-        super(Entry, self).__init__(label, enabled)
+    def __init__(self, name, label, enabled=True, align_vertical=False):
+        super(Entry, self).__init__(name, label, enabled)
         self.label = label
         self.align_vertical = align_vertical
         self.valid(True)
@@ -300,8 +334,8 @@
 class Button(InputElement):
     action = None
 
-    def __init__(self, label, enabled=True):
-        super(Button, self).__init__(label, enabled)
+    def __init__(self, name, label, enabled=True):
+        super(Button, self).__init__(name, label, enabled)
         self.text(label)
 
     @Element.signal_change
@@ -310,28 +344,61 @@
             self._label = label
         return self._label
 
-    def set_text(self, txt):
-        self.text(txt)
+    def value(self, value):
+        self.text(value)
+
+
+class SaveAction(app.Action):
+    """Action to save the current page/dialog
+    """
+    pass
+
+
+class CloseAction(app.Action):
+    """Action to close the current dialog
+    """
+    pass
+
+
+class ResetAction(app.Action):
+    """Action to reset all InputElements on the current page/dialog
+    """
+    pass
+
+
+class RevalidateAction(app.Action):
+    """Action to revalidate all InputElements on the current page/dialog
+    """
+    pass
+
+
+class QuitAction(app.Action):
+    """Action to close the whole app.Application
+    """
+    pass
 
 
 class SaveButton(Button):
-    def __init__(self, enabled=True):
-        super(SaveButton, self).__init__("Save", enabled)
+    def __init__(self, name, enabled=True):
+        super(SaveButton, self).__init__(name, "Save", enabled)
+        self.action = SaveAction
 
 
 class ResetButton(Button):
-    def __init__(self, enabled=True):
-        super(ResetButton, self).__init__("Reset", enabled)
+    def __init__(self, name, enabled=True):
+        super(ResetButton, self).__init__(name, "Reset", enabled)
+        self.action = ResetAction
 
 
 class CloseButton(Button):
-    def __init__(self, enabled=True):
-        super(CloseButton, self).__init__("Close", enabled)
+    def __init__(self, name, enabled=True):
+        super(CloseButton, self).__init__(name, "Close", enabled)
+        self.action = CloseAction
 
 
 class Divider(Element):
-    def __init__(self, char=u" "):
-        super(Divider, self).__init__()
+    def __init__(self, name, char=u" "):
+        super(Divider, self).__init__(name)
         self.char = char
 
 
@@ -342,8 +409,8 @@
         label: The caption of the options
         options:
     """
-    def __init__(self, label, options):
-        super(Options, self).__init__(label, True)
+    def __init__(self, name, label, options):
+        super(Options, self).__init__(name, label, True)
         self.label = label
         self.options = options
         self.option(options[0])
@@ -354,8 +421,8 @@
             self._option = option
         return self._option
 
-    def set_text(self, txt):
-        self.option(txt)
+    def value(self, value):
+        self.option(value)
 
 
 class Checkbox(InputElement):
@@ -365,8 +432,8 @@
         label: Caption of this checkbox
         state: The initial change
     """
-    def __init__(self, label, state=False, is_enabled=True):
-        super(Checkbox, self).__init__(label, is_enabled)
+    def __init__(self, name, label, state=False, is_enabled=True):
+        super(Checkbox, self).__init__(name, label, is_enabled)
         self.label = label
         self.state(state)
 
@@ -384,8 +451,8 @@
         current: The initial value
         done: The maximum value
     """
-    def __init__(self, current=0, done=100):
-        super(ProgressBar, self).__init__()
+    def __init__(self, name, current=0, done=100):
+        super(ProgressBar, self).__init__(name)
         self.current(current)
         self.done = done
 
@@ -413,8 +480,8 @@
         height: The height of the Table
     """
 
-    def __init__(self, label, header, items, height=5, enabled=True):
-        super(Table, self).__init__(label, enabled)
+    def __init__(self, name, label, header, items, height=5, enabled=True):
+        super(Table, self).__init__(name, label, enabled)
         self.label = label
         self.header = header
         self.items = items
@@ -428,7 +495,7 @@
 
 
 class TransactionProgressDialog(Dialog):
-    def __init__(self, transaction, plugin, initial_text=""):
+    def __init__(self, name, transaction, plugin, initial_text=""):
         self.transaction = transaction
         self.texts = [initial_text, ""]
         self.plugin = plugin
@@ -437,7 +504,8 @@
         self.buttons = [(None, self._close_button)]
         self._progress_label = Label(initial_text)
         widgets = [("dialog.progress", self._progress_label)]
-        super(TransactionProgressDialog, self).__init__(self.transaction.title,
+        super(TransactionProgressDialog, self).__init__(name,
+                                                        self.transaction.title,
                                                         widgets)
 
     def add_update(self, txt):
@@ -465,3 +533,116 @@
         except Exception as e:
             self.add_update("\nAn error occurred while applying the changes:")
             self.add_update("%s" % e.message)
+
+
+class AbstractUIBuilder(base.Base):
+    """An abstract class
+    """
+    application = app.Application
+
+    def __init__(self, application):
+        self.application
+
+    def build(self, ui_element):
+        assert type(ui_element) is Element
+
+        builder_for_element = {
+            Window: self.build_window,
+            Page: self.build_page,
+            Dialog: self.build_dialog,
+
+            Label: self.build_label,
+            KeywordLabel: self.build_keywordlabel,
+
+            Entry: self.build_entry,
+            PasswordEntry: self.build_passwordentry,
+
+            Header: self.build_header,
+
+            Button: self.build_button,
+
+            Options: self.build_options,
+            ProgressBar: self.build_progressbar,
+            Table: self.build_table,
+            Checkbox: self.build_checkbox,
+
+            Divider: self.build_divider,
+            Row: self.build_row,
+        }
+
+        ui_element_type = type(ui_element)
+        builder_func = None
+
+        # Check if builder is available for UI Element
+        if ui_element_type in builder_for_element:
+            builder_func = builder_for_element[ui_element_type]
+        else:
+            # It could be a derived type, therefor find it's base:
+            for sub_type in type(ui_element).mro():
+                if sub_type in builder_for_element:
+                    builder_func = builder_for_element[sub_type]
+
+        if not builder_func:
+            raise Exception("No builder for UI element '%s'" % ui_element)
+
+        # Build widget from UI Element
+        widget = builder_func(ui_element)
+
+        """# Populate with values
+        if ui_element_type in [Entry,
+                               PasswordEntry,
+                               Label,
+                               KeywordLabel,
+                               Options,
+                               Checkbox]:
+            model = plugin.model()
+            if path in model:
+                text = model[path]
+                widget.set_text(text)"""
+
+        return widget
+
+    def build_window(self, ui_window):
+        raise NotImplementedError
+
+    def build_page(self, ui_page):
+        raise NotImplementedError
+
+    def build_dialog(self, ui_dialog):
+        raise NotImplementedError
+
+    def build_label(self, ui_label):
+        raise NotImplementedError
+
+    def build_keywordlabel(self, ui_keywordlabel):
+        raise NotImplementedError
+
+    def build_header(self, ui_header):
+        raise NotImplementedError
+
+    def build_button(self, ui_button):
+        raise NotImplementedError
+
+    def build_entry(self, ui_entry):
+        raise NotImplementedError
+
+    def build_passwordentry(self, ui_passwordentry):
+        raise NotImplementedError
+
+    def build_divider(self, ui_divider):
+        raise NotImplementedError
+
+    def build_options(self, ui_options):
+        raise NotImplementedError
+
+    def build_checkbox(self, ui_checkbox):
+        raise NotImplementedError
+
+    def build_progressbar(self, ui_progressbar):
+        raise NotImplementedError
+
+    def build_table(self, ui_table):
+        raise NotImplementedError
+
+    def build_row(self, ui_row):
+        raise NotImplementedError
diff --git a/scripts/tui/src/ovirt/node/ui/builder.py b/scripts/tui/src/ovirt/node/ui/builder.py
deleted file mode 100644
index 87a2e8d..0000000
--- a/scripts/tui/src/ovirt/node/ui/builder.py
+++ /dev/null
@@ -1,346 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-#
-# builder.py - Copyright (C) 2012 Red Hat, Inc.
-# Written by Fabian Deutsch <fabiand at redhat.com>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; version 2 of the License.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-# MA  02110-1301, USA.  A copy of the GNU General Public License is
-# also available at http://www.gnu.org/copyleft/gpl.html.
-
-"""
-A visitor to build the urwid TUI from the abstract UI definitions.
-Is based on the visitor pattern
-"""
-
-from ovirt.node import ui
-import logging
-import ovirt.node.exceptions
-import urwid
-
-
-LOGGER = logging.getLogger(__name__)
-
-
-def page_from_plugin(tui, plugin):
-    element = plugin.ui_content()
-    widget = None
-
-    # FIXME could also be done using dict.
-    if type(element) is ui.Page:
-        widget = build_page(tui, plugin, element)
-    else:
-        raise Exception("Unknown element container: %s" % element)
-
-    return widget
-
-
-def build_page(tui, plugin, container):
-    widgets = []
-
-    for path, item in container.children:
-        widget = widget_for_item(tui, plugin, path, item)
-        widgets.append(("flow", widget))
-
-    # Add buttons
-    button_widgets = []
-    for path, item in container.buttons:
-        assert type(item) in [ui.SaveButton, ui.ResetButton, ui.CloseButton,
-                              ui.Button]
-        button_widgets.append(build_button(path, item, tui, plugin))
-
-    if button_widgets:
-        widgets.append(urwid.Filler(urwid.Columns(button_widgets)))
-
-    widgets.append(urwid.Filler(urwid.Text("")))
-
-    LOGGER.debug("Triggering initial sematic checks for '%s'" % plugin)
-    try:
-        plugin.check_semantics()
-    except:
-        tui.notify("error", "Initial model validation failed.")
-
-    page = ui.widgets.PageWidget(widgets, container.title)
-    page.plugin = plugin
-
-    return page
-
-
-def widget_for_item(tui, plugin, path, item):
-    item_to_builder = {
-        ui.Header: build_label,
-
-        ui.Label: build_label,
-        ui.KeywordLabel: build_label,
-
-        ui.Entry: build_entry,
-        ui.PasswordEntry: build_entry,
-
-        ui.Button: build_button,
-        ui.SaveButton: build_button,
-        ui.ResetButton: build_button,
-
-        ui.Options: build_options,
-        ui.ProgressBar: build_progressbar,
-        ui.Table: build_table,
-        ui.Checkbox: build_checkbox,
-
-        ui.Divider: build_divider,
-        ui.Row: build_row,
-    }
-
-    item_type = None
-
-    # Check if builder is available for UI Element
-    if item_type in item_to_builder:
-        item_type = type(item)
-    else:
-        # It could be a derived type, therefor find it's base:
-        for sub_type in type(item).mro():
-            if sub_type in item_to_builder:
-                item_type = sub_type
-
-    assert item_type, "No widget for item type"
-
-    # Build widget from UI Element
-    build_func = item_to_builder[item_type]
-    widget = build_func(path, item, tui, plugin)
-
-    # Populate with values
-    if item_type in [ui.Entry,
-                      ui.PasswordEntry,
-                      ui.Label,
-                      ui.KeywordLabel,
-                      ui.Options,
-                      ui.Checkbox]:
-        model = plugin.model()
-        if path in model:
-            text = model[path]
-            widget.set_text(text)
-
-    return widget
-
-
-def build_entry(path, item, tui, plugin):
-    widget_class = None
-    if type(item) is ui.Entry:
-        widget_class = ui.widgets.Entry
-    else:
-        widget_class = ui.widgets.PasswordEntry
-
-    widget = widget_class(item.label, align_vertical=item.align_vertical)
-    widget.enable(item.enabled())
-
-    def on_item_enabled_change_cb(w, v):
-        LOGGER.debug("Element changed, updating entry '%s': %s" % (w, v))
-        if widget.selectable() != v:
-            widget.enable(v)
-        if v == False:
-            widget.notice = ""
-            widget.valid(True)
-
-    item.connect_signal("enabled", on_item_enabled_change_cb)
-
-    def on_item_valid_change_cb(w, v):
-            widget.valid(v)
-
-    item.connect_signal("valid", on_item_valid_change_cb)
-
-    def on_item_text_change_cb(w, v):
-            widget.set_text(v)
-
-    item.connect_signal("text", on_item_text_change_cb)
-
-    def on_widget_value_change(widget, new_value):
-        LOGGER.debug("Entry %s changed, calling callback: '%s'" % (widget,
-                                                                   path))
-
-        try:
-            change = {path: new_value}
-            plugin._on_ui_change(change)
-            widget.notice = ""
-            widget.valid(True)
-
-        except ovirt.node.exceptions.Concern as e:
-            LOGGER.error("Concern when updating: %s" % e)
-
-        except ovirt.node.exceptions.InvalidData as e:
-            LOGGER.error("Invalid data when updating: %s" % e)
-            if widget._selectable:
-                widget.notice = e.message
-                widget.valid(False)
-
-#        tui._draw_screen()
-    urwid.connect_signal(widget, 'change', on_widget_value_change)
-
-    return widget
-
-
-def build_label(path, item, tui, plugin):
-    if type(item) is ui.KeywordLabel:
-        widget = ui.widgets.KeywordLabel(item.keyword,
-                                                    item.text())
-    elif type(item) is ui.Header:
-        widget = ui.widgets.Header(item.text(), item.template)
-    else:
-        widget = ui.widgets.Label(item.text())
-
-    def on_item_text_change_cb(w, v):
-        LOGGER.debug("Element changed, updating label '%s': %s" % (w, v))
-        widget.text(v)
-        # Redraw the screen if widget text is updated "outside" of the
-        # mainloop
-        tui._draw_screen()
-    item.connect_signal("text", on_item_text_change_cb)
-
-    return widget
-
-
-def build_button(path, item, tui, plugin):
-    itemtype = type(item)
-    widget = ui.widgets.Button(item.text())
-
-    if itemtype in [ui.SaveButton]:
-        def on_valid_cb(w, v):
-            widget.enable(plugin.is_valid_changes())
-        plugin.sig_valid.connect(on_valid_cb)
-        on_valid_cb(None, None)
-
-    if not item.action:
-        if itemtype in [ui.Button, ui.SaveButton]:
-            item.action = lambda: plugin._on_ui_save()
-        if itemtype in [ui.CloseButton]:
-            item.action = lambda: tui.close_topmost_dialog()
-        if itemtype in [ui.ResetButton]:
-            item.action = lambda: plugin._on_ui_reset()
-    else:
-        LOGGER.debug("Using custom callback for item " +
-                     "%s: %s" % (item, item.action.callback))
-
-    def on_widget_click_cb(widget, data=None):
-        LOGGER.debug("Button click: %s" % {"path": path, "widget": widget})
-        if itemtype is ui.Button:
-            plugin._on_ui_change({path: True})
-
-        r = item.action()
-
-        if itemtype in [ui.ResetButton]:
-            # Like a reload ...
-            tui.switch_to_plugin(plugin)
-
-        parse_plugin_result(tui, plugin, r)
-
-    urwid.connect_signal(widget, "click", on_widget_click_cb)
-
-    def on_item_enabled_change_cb(w, v):
-        widget.enable(v)
-    item.connect_signal("enabled", on_item_enabled_change_cb)
-
-    return widget
-
-
-def build_divider(path, item, tui, plugin):
-    return ui.widgets.Divider(item.char)
-
-
-def build_options(path, item, tui, plugin):
-    widget = ui.widgets.Options(item.label, item.options,
-                                           plugin.model()[path])
-
-    def on_widget_change_cb(widget, data):
-        item.option(data)
-        LOGGER.debug("Options changed, calling callback: %s" % data)
-        plugin._on_ui_change({path: data})
-    urwid.connect_signal(widget, "change", on_widget_change_cb)
-
-    return widget
-
-
-def build_checkbox(path, item, tui, plugin):
-    widget = ui.widgets.Checkbox(item.label, item.state())
-
-    def on_widget_change_cb(widget, data=None):
-        item.state(data)
-        LOGGER.debug("Checkbox changed, calling callback: %s" % data)
-        plugin._on_ui_change({path: data})
-
-    urwid.connect_signal(widget, "change", on_widget_change_cb)
-    return widget
-
-
-def build_row(path, container_item, tui, plugin):
-    widgets = []
-    for path, element in container_item.children:
-        child = widget_for_item(tui, plugin, path, element)
-        widgets.append(child)
-
-    return urwid.Columns(widgets)
-
-
-def build_progressbar(path, item, tui, plugin):
-    widget = ui.widgets.ProgressBarWidget(item.current(), item.done)
-
-    def on_item_current_change_cb(w, v):
-        LOGGER.debug("Model changed, updating progressbar '%s': %s" % (w, v))
-        widget.set_completion(v)
-        tui._draw_screen()
-    item.connect_signal("current", on_item_current_change_cb)
-
-    return widget
-
-
-def build_table(path, item, tui, plugin):
-    children = []
-    for key, label in item.items:
-        c = _build_tableitem(tui, path, plugin, key, label)
-        children.append(c)
-    widget = ui.widgets.TableWidget(item.label, item.header,
-                                               children,
-                                               item.height, item.enabled())
-
-    def on_change_cb(w, d=None):
-        plugin._on_ui_change({path: w._key})
-        item.select(w._key)
-    urwid.connect_signal(widget, "changed", on_change_cb)
-
-    return widget
-
-
-def _build_tableitem(tui, path, plugin, key, label):
-    c = ui.widgets.TableEntryWidget(label)
-    c._key = key
-
-    def on_activate_cb(w, data):
-        plugin._on_ui_change({path: w._key})
-        parse_plugin_result(tui, plugin, plugin._on_ui_save())
-    urwid.connect_signal(c, "activate", on_activate_cb)
-    return c
-
-
-def parse_plugin_result(tui, plugin, result):
-        LOGGER.debug("Parsing plugin change/save result: %s" % result)
-
-        if type(result) in [ui.Page]:
-            LOGGER.debug("Page requested.")
-            tui._show_page(result)
-
-        elif type(result) in [ui.Dialog]:
-            LOGGER.debug("Dialog requested.")
-            dialog = tui._show_dialog(result)
-
-            def on_item_close_changed_cb(i, v):
-                dialog.close()
-            result.connect_signal("close", on_item_close_changed_cb)
-
-        return result
diff --git a/scripts/tui/src/ovirt/node/ui/tui.py b/scripts/tui/src/ovirt/node/ui/tui.py
deleted file mode 100644
index 17218fd..0000000
--- a/scripts/tui/src/ovirt/node/ui/tui.py
+++ /dev/null
@@ -1,383 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-#
-# tui.py - Copyright (C) 2012 Red Hat, Inc.
-# Written by Fabian Deutsch <fabiand at redhat.com>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; version 2 of the License.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-# MA  02110-1301, USA.  A copy of the GNU General Public License is
-# also available at http://www.gnu.org/copyleft/gpl.html.
-
-"""
-The urwid TUI base library
-"""
-
-from ovirt.node import base, ui
-import ovirt.node.ui.builder
-import ovirt.node.ui.widgets
-import time
-import urwid
-
-
-def inherits(obj, t):
-    return t in type(obj).mro()
-
-
-class UrwidTUI(ovirt.node.ui.Window):
-    app = None
-
-    _plugins = {}
-    _hotkeys = {}
-
-    __loop = None
-    __main_frame = None
-    __menu = None
-    __page_frame = None
-
-    __widget_stack = []
-
-    _current_plugin = None
-
-    header = u"\n Configuration TUI\n"
-    footer = u"Press ctrl+c to quit"
-
-    with_menu = True
-
-    element_styles = {
-        "text": "black",
-        "label": "dark gray",
-        "disabled": "dark gray",
-        "background": "light gray",
-        "invalid": "dark red",
-    }
-
-    palette = [(None, 'default', element_styles["background"], 'bold',
-                None, None),
-               ('screen', None),
-               ('header', 'white', 'dark blue'),
-               ('table', element_styles["text"]),
-               ('table.label', element_styles["label"]),
-               ('table.header', element_styles["label"]),
-               ('table.entry', element_styles["text"]),
-               ('table.entry:focus', 'white', 'light blue'),
-               ('main.menu', 'black'),
-               ('main.menu.frame', element_styles["text"]),
-               ('notice', 'light red'),
-               ('plugin.widget.entry', element_styles["text"]),
-               ('plugin.widget.entry.disabled', element_styles["disabled"]),
-               ('plugin.widget.entry.invalid', element_styles["invalid"]),
-               ('plugin.widget.entry.label', element_styles["label"]),
-               ('plugin.widget.entry.label.invalid', element_styles["label"]),
-               ('plugin.widget.entry.frame', element_styles["text"]),
-               ('plugin.widget.entry.frame.invalid',
-                element_styles["invalid"]),
-               ('plugin.widget.entry.frame.disabled',
-                element_styles["disabled"]),
-               ('plugin.widget.notice', element_styles["invalid"]),
-               ('plugin.widget.header', 'black, bold'),
-               ('plugin.widget.divider', element_styles["text"]),
-               ('plugin.widget.button', 'dark blue'),
-               ('plugin.widget.button.disabled', element_styles["disabled"]),
-               ('plugin.widget.label', element_styles["text"]),
-               ('plugin.widget.label.keyword', element_styles["label"]),
-               ('plugin.widget.progressbar.box', element_styles["disabled"]),
-               ('plugin.widget.progressbar.uncomplete', None),
-               ('plugin.widget.progressbar.complete', "white",
-                element_styles["disabled"]),
-               ('plugin.widget.options', element_styles["label"]),
-               ('plugin.widget.options.label', element_styles["label"]),
-               ('plugin.widget.dialog', None),
-               ('plugin.widget.page', None),
-               ('plugin.widget.page.header', element_styles["label"]),
-               ('plugin.widget.page.frame', None),
-               ('plugin.widget.checkbox.label', element_styles["label"]),
-               ('plugin.widget.checkbox', element_styles["label"]),
-               ]
-
-    def __init__(self, app, with_menu=True):
-        super(UrwidTUI, self).__init__(app)
-        self.with_menu = with_menu
-        self.logger.debug("Creating urwid tui for '%s'" % app)
-        self.logger.debug("Detected encoding: %s" % urwid.get_encoding_mode())
-
-    def switch_to_plugin(self, plugin, check_for_changes=True):
-        self.logger.debug("Switching to plugin " +
-                          "%s, with checks? %s" % (plugin, check_for_changes))
-        if check_for_changes and self._check_outstanding_changes():
-            return
-        start = time.time()
-        self._current_plugin = plugin
-        plugin_page = ovirt.node.ui.builder.page_from_plugin(self, plugin)
-        self.__display_as_page(plugin_page)
-        stop = time.time()
-        diff = stop - start
-        self.logger.debug("Build and displayed plugin_page in %ss" %
-                          diff)
-
-    def _show_body(self, body):
-        """
-        """
-        assert inherits(body, ui.Page)
-        widget = ui.builder.build_page(self, self._current_plugin, body)
-        self.__display_as_body(widget)
-
-    def _show_page(self, page):
-        """Shows the ui.Page as a page.
-        This transforms the abstract ui.Page to a urwid specififc version
-        and displays it.
-        """
-        assert inherits(page, ui.Page)
-        widget = ui.builder.build_page(self, self._current_plugin, page)
-        self.__display_as_page(widget)
-
-    def _show_dialog(self, dialog):
-        """Shows the ui.Dialog as a dialog.
-        This transforms the abstract ui.Dialog to a urwid specififc version
-        and displays it.
-        """
-        if not inherits(dialog, ui.Dialog):
-            raise Exception("'%s' does not inherit from ui.Dialog" % dialog)
-        widget = ui.builder.build_page(self, self._current_plugin, dialog)
-        return self.__display_as_dialog(widget, dialog.title,
-                                        dialog.escape_key)
-
-    def topmost_dialog(self):
-        dialog = [w for w in self.__widget_stack
-                  if inherits(w, ovirt.node.ui.widgets.ModalDialog)][-1:]
-        if dialog:
-            dialog = dialog[0]
-        else:
-            dialog = None
-        return dialog
-
-    def close_topmost_dialog(self):
-        dialog = self.topmost_dialog()
-        if dialog:
-            self.__close_dialog(dialog)
-
-    def quit(self):
-        """Quit the UI
-        """
-        self.logger.info("Quitting, exitting mainloop")
-        raise urwid.ExitMainLoop()
-
-    def run(self):
-        """Run the UI
-        """
-        self.__main_frame = self.__create_screen()
-        self.__register_default_hotkeys()
-
-        self.__loop = urwid.MainLoop(self.__main_frame,
-                              self._convert_palette(),
-                              input_filter=self.__filter_hotkeys)
-
-        self.navigate.to_first_plugin()
-
-        self.__loop.run()
-
-    def __build_plugin_menu(self):
-        self.__menu = ovirt.node.ui.widgets.PluginMenu(self._plugins)
-
-        def menu_item_changed(plugin):
-            self.switch_to_plugin(plugin)
-        urwid.connect_signal(self.__menu, 'changed', menu_item_changed)
-
-    def __create_screen(self):
-        columns = []
-
-        self.__page_frame = urwid.Frame(urwid.Filler(urwid.Text("")))
-
-        if self.with_menu:
-            self.__build_plugin_menu()
-            self.__menu.set_focus(0)
-            columns += [("weight", 0.3, self.__menu)]
-
-        self.__notice = urwid.Text("Note: ")
-        self.__notice_filler = urwid.Filler(self.__notice)
-        self.__notice_attrmap = urwid.AttrMap(self.__notice_filler, "notice")
-        columns += [self.__page_frame]
-
-        menu_frame_columns = urwid.Columns(columns, 4)
-
-        body = urwid.Pile([
-#                           ("fixed", 3, self.__notice_attrmap),
-                           menu_frame_columns
-                        ])
-
-        header = urwid.Text(self.header, wrap='clip')
-        header = urwid.AttrMap(header, 'header')
-        footer = urwid.Text(self.footer, wrap='clip')
-        screen = urwid.Frame(body, header, footer)
-        return urwid.AttrMap(screen, "screen")
-
-    def _check_outstanding_changes(self):
-        has_outstanding_changes = False
-        if self._current_plugin:
-            pending_changes = self._current_plugin.pending_changes()
-            if pending_changes:
-                self.logger.warning("Pending changes: %s" % pending_changes)
-                msg = ""
-                widgets = dict(self._current_plugin.ui_content().children)
-                self.logger.debug("Available widgets: %s" % widgets)
-                for path, value in pending_changes.items():
-                    if path in widgets:
-                        widget = widgets[path]
-                        field = widget.name
-                        self.logger.debug("Changed widget: %s %s" % (path,
-                                                                     widget))
-                        msg += "- %s\n" % (field.strip(":"))
-                if msg:
-                    txt = "The following fields have changed:\n%s" % msg
-                    txt += "\n\nPlease save or reset the page."
-                    self.__display_as_dialog(urwid.Filler(ui.widgets.Label(
-                                txt)), "Pending changes")
-                    has_outstanding_changes = True
-        return has_outstanding_changes
-
-    def __display_as_body(self, widget):
-        self.__main_frame.body = widget
-
-    def __display_as_page(self, page):
-        self.logger.debug("Displaying page %s" % page)
-#        filler = urwid.Filler(page, ("fixed top", 1), height=35)
-        filler = urwid.Pile([page])
-        self.__page_frame.body = filler
-
-    def __display_as_dialog(self, body, title, escape_key="esc"):
-        self.logger.debug("Displaying dialog: %s / %s" % (body, title))
-#        filler = urwid.Filler(body, ("fixed top", 1), height=35)
-        filler = urwid.Pile([body])
-        dialog = ovirt.node.ui.widgets.ModalDialog(title, filler, escape_key,
-                                                   self.__loop.widget)
-        urwid.connect_signal(dialog, "close",
-                             lambda: self.__close_dialog(dialog))
-        self.__loop.widget = dialog
-        self.__widget_stack.append(dialog)
-        self._draw_screen()
-        return dialog
-
-    def __close_dialog(self, dialog):
-        self.logger.debug("Widget stack: %s" % self.__widget_stack)
-        new_stack = [w for w in self.__widget_stack if w != dialog]
-        self.__widget_stack = new_stack
-        self.logger.debug("New widget stack: %s" % self.__widget_stack)
-        if self.__widget_stack:
-            self.__loop.widget = self.__widget_stack[-1]
-        else:
-            self.__loop.widget = self.__main_frame
-        self.logger.debug("Dialog %s closed" % dialog)
-
-    def __filter_hotkeys(self, keys, raw):
-        key = str(keys)
-
-        if inherits(self.__loop.widget, ovirt.node.ui.widgets.ModalDialog):
-            self.logger.debug("Modal dialog escape: %s" % key)
-            if self.__loop.widget.escape_key is None:
-                self.logger.debug("Dialog can not be closed with magic key")
-            elif self.__loop.widget.escape_key in keys:
-                self.close_topmost_dialog()
-                return
-
-        if key in self._hotkeys.keys():
-            self.logger.debug("Running hotkeys: %s" % key)
-            self._hotkeys[key]()
-
-        self.logger.debug("Keypress: %s" % key)
-
-        return keys
-
-    def __register_default_hotkeys(self):
-        self.register_hotkey(["esc"], self._quit_if_no_dialogs)
-        self.register_hotkey(["window resize"], self._check_min_size_cb)
-
-    def _quit_if_no_dialogs(self):
-        if self.topmost_dialog() is None:
-            self.quit()
-        else:
-            self.logger.debug("There are still open dialogs")
-
-    def _draw_screen(self):
-        self.__loop.draw_screen()
-
-    def size(self):
-        if not self.__loop.screen:
-            # FIXME sometimes screen is None, but why?
-            return (0, 0)
-        return self.__loop.screen.get_cols_rows()
-
-    def _min_size(self):
-            return (80, 23)
-
-    def _check_min_size_cb(self):
-        size = self.size()
-        msize = self._min_size()
-        width, height = size
-        min_width, min_height = msize
-        if width < min_width or height < min_height:
-            msg = ("The current window size %s is smaller " +
-                                 "than the minimum size %s") % (size, msize)
-            self.logger.warning(msg)
-            if not hasattr(self, "_error_dialog") or not self._error_dialog:
-                d = ui.Dialog("Error", [("dialog.error", ui.Label(msg))])
-                d.buttons = []
-                self._error_dialog = self._show_dialog(d)
-        else:
-            if hasattr(self, "_error_dialog") and self._error_dialog:
-                self._error_dialog.close()
-                self._error_dialog = None
-
-    def watch_pipe(self, cb):
-        """Return a fd to be used as stdout, cb called for each line
-        """
-        return self.__loop.watch_pipe(cb)
-
-    def notify(self, category, msg):
-        self.logger.info("UI notification (%s): %s" % (category, msg))
-        # FIXME do notification
-
-    def suspended(self):
-        """Supspends the screen to do something in the foreground
-        """
-        class SuspendedScreen(base.Base):
-            def __init__(self, loop):
-                super(SuspendedScreen, self).__init__()
-                self.__loop = loop
-
-            def __enter__(self):
-                self.__loop.screen.stop()
-
-            def __exit__(self, a, b, c):
-                self.__loop.screen.start()
-        return SuspendedScreen(self.__loop)
-
-    def _convert_palette(self):
-        """Convert our palette to the format urwid understands.
-
-        Non existsing or None values are filled with the defaults.
-        """
-        p = {}
-        for t in self.palette:
-            k = t[0]
-            v = list(t[1:])
-            p[k] = v
-
-        palette = []
-        default = p[None]
-        for k, v in p.items():
-            if k == None:
-                continue
-            colors = [e or default[idx] for idx, e in enumerate(v)]
-            rest = default[len(colors):]
-            palette.append(tuple([k] + colors + rest))
-        return palette
diff --git a/scripts/tui/src/ovirt/node/utils/__init__.py b/scripts/tui/src/ovirt/node/utils/__init__.py
index dad0e46..49cf2ec 100644
--- a/scripts/tui/src/ovirt/node/utils/__init__.py
+++ b/scripts/tui/src/ovirt/node/utils/__init__.py
@@ -22,6 +22,7 @@
 import augeas as _augeas
 import hashlib
 import system_config_keyboard.keyboard
+import time
 import traceback
 
 """
@@ -297,3 +298,25 @@
             """Is run in case that one commit of a transaction fails.
             """
             pass
+
+
+class Timer(base.Base):
+    started = 0
+    ended = 0
+
+    def __enter__(self):
+        """Create backups when starting
+        """
+        self.started = time.time()
+        return self
+
+    def __exit__(self, a, b, c):
+        """Remove all backups when done
+        """
+        self.stopped = time.time()
+
+    def duration(self):
+        return self.stopped - self.started
+
+    def __str__(self):
+        return "<Timer duration='%s'>" % self.duration()


--
To view, visit http://gerrit.ovirt.org/10912
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ia6f52e82631483b27ad50a6be64f6b72b4539bc6
Gerrit-PatchSet: 1
Gerrit-Project: ovirt-node
Gerrit-Branch: master
Gerrit-Owner: Fabian Deutsch <fabiand at fedoraproject.org>



More information about the node-patches mailing list