[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