class PegasusGUI(WidgetWrap): def __init__(self, header=None, body=None, footer=None): _check_encoding() # Make sure terminal supports utf8 cb = self.show_exception_message utils.register_async_exception_callback(cb) self.header = header if header else Header() self.body = body if body else Banner() self.footer = footer if footer else StatusBar('') self.frame = Frame(self.body, header=self.header, footer=self.footer) self.services_view = None self.placement_view = None self.controller = None self.machine_wait_view = None self.add_services_dialog = None super().__init__(self.frame) def _build_overlay_widget(self, top_w, align, width, valign, height, min_width, min_height): return Overlay(top_w=Filler(top_w), bottom_w=self.frame, align=align, width=width, valign=valign, height=height, min_width=width, min_height=height) def show_widget_on_top(self, widget, width, height, align='center', valign='middle', min_height=0, min_width=0): """Show `widget` on top of :attr:`frame`.""" self._w = self._build_overlay_widget(top_w=widget, align=align, width=width, valign=valign, height=height, min_width=min_width, min_height=min_height) def focus_next(self): if hasattr(self.frame.body, 'scroll_down'): self.frame.body.scroll_down() def focus_previous(self): if hasattr(self.frame.body, 'scroll_up'): self.frame.body.scroll_up() def focus_first(self): if hasattr(self.frame.body, 'scroll_top'): self.frame.body.scroll_top() def focus_last(self): if hasattr(self.frame.body, 'scroll_bottom'): self.frame.body.scroll_bottom() def hide_widget_on_top(self): """Hide the topmost widget (if any).""" self._w = self.frame def show_help_info(self): widget = HelpScreen() self.show_widget_on_top(widget, width=80, height=22, align="center", valign="middle", min_height=10) def hide_help_info(self): self.hide_widget_on_top() def show_step_info(self, msg): self.hide_step_info() widget = StepInfo(msg) self.show_widget_on_top(widget, width=60, height=14, align="center", valign="middle", min_height=10) def hide_step_info(self): self.hide_widget_on_top() def show_selector_info(self, title, opts, cb): widget = Selector(title, opts, cb) self.show_widget_on_top(widget, width=50, height=10) def hide_selector_info(self): self.hide_widget_on_top() def show_selector_with_desc(self, title, opts, cb): widget = SelectorWithDescription(title, opts, cb) self.show_widget_on_top(widget, width=80, height=14) def hide_selector_with_desc(self): self.hide_widget_on_top() def show_fatal_error_message(self, msg, cb): w = InfoDialog(msg, cb) self.show_widget_on_top(w, width=50, height=20) def show_password_input(self, title, cb): widget = PasswordInput(title, cb) self.show_widget_on_top(widget, width=50, height=7) def hide_show_password_input(self): self.hide_widget_on_top() def show_maas_input(self, title, cb): widget = MaasServerInput(title, cb) self.show_widget_on_top(widget, width=50, height=10) def hide_show_maas_input(self): self.hide_widget_on_top() def show_dhcp_range(self, range_low, range_high, title, cb): widget = DhcpRangeInput(range_low, range_high, title, cb) self.show_widget_on_top(widget, width=50, height=10) def hide_show_dhcp_range(self): self.hide_widget_on_top() def show_landscape_input(self, title, cb): widget = LandscapeInput(title, cb) self.show_widget_on_top(widget, width=50, height=9) def hide_show_landscape_input(self): self.hide_widget_on_top() def set_pending_deploys(self, pending_charms): self.frame.footer.set_pending_deploys(pending_charms) def flash(self, msg): self.frame.body.flash("{}\N{HORIZONTAL ELLIPSIS}".format(msg)) def flash_reset(self): self.frame.body.flash_reset() def status_message(self, text): self.frame.footer.message(text) self.frame.set_footer(self.frame.footer) def status_error_message(self, message): self.frame.footer.error_message(message) def status_info_message(self, message): self.frame.footer.info_message( "{}\N{HORIZONTAL ELLIPSIS}".format(message)) def set_dashboard_url(self, ip, user, password): self.frame.footer.set_dashboard_url(ip, user, password) def set_jujugui_url(self, ip): self.frame.footer.set_jujugui_url(ip) def set_openstack_rel(self, text): self.frame.footer.set_openstack_rel(text) def clear_status(self): self.frame.footer = None self.frame.set_footer(self.frame.footer) def render_services_view(self, nodes, juju_state, maas_state, config, **kwargs): if self.services_view is None: self.services_view = ServicesView(nodes, juju_state, maas_state, config) self.services_view.update(nodes) self.frame.set_body(self.services_view) self.header.set_show_add_units_hotkey(True) def render_node_install_wait(self, message=None, **kwargs): self.frame.body = NodeInstallWaitMode(message, **kwargs) self.frame.set_body(self.frame.body) def render_placement_view(self, loop, config, cb): """ render placement view :param cb: deploy callback trigger """ if self.placement_view is None: assert self.controller is not None pc = self.controller.placement_controller self.placement_view = PlacementView(self, pc, loop, config, cb) self.placement_view.update() self.frame.body = self.placement_view def render_machine_wait_view(self, config): if self.machine_wait_view is None: self.machine_wait_view = MachineWaitView( self, self.current_installer, config) self.machine_wait_view.update() self.frame.body = self.machine_wait_view def render_add_services_dialog(self, deploy_cb, cancel_cb): def reset(): self.add_services_dialog = None def cancel(): reset() cancel_cb() def deploy(): reset() deploy_cb() if self.add_services_dialog is None: self.add_services_dialog = AddServicesDialog(self.controller, deploy_cb=deploy, cancel_cb=cancel) self.add_services_dialog.update() self.frame.body = Filler(self.add_services_dialog) def show_exception_message(self, ex, logpath="~/.cloud-install/commands"): def handle_done(*args, **kwargs): raise urwid.ExitMainLoop() self.hide_widget_on_top() msg = ("A fatal error has occurred: {}\n" "See {} for further info.".format(ex.args[0], logpath)) self.show_fatal_error_message(msg, handle_done) def select_install_type(self, install_types, cb): """ Dialog for selecting installation type """ self.status_info_message("Choose your installation path") self.show_selector_with_desc('Install Type', install_types, cb) def select_maas_type(self, cb): """ Perform multi install based on existing MAAS or if a new MAAS will be installed """ self.status_info_message( "If a MAAS exists please enter the Server IP and your " "administrator's API Key. Otherwise leave blank and a new " "MAAS will be created for you") self.show_maas_input("MAAS Setup", cb) def __repr__(self): return "<Ubuntu OpenStack Installer GUI Interface>" def tasker(self, loop, config): """ Interface with Tasker class :param loop: urwid.Mainloop :param dict config: config object """ return Tasker(self, loop, config) def exit(self, loop=None): """ Provide exit loop helper :param loop: Just a placeholder, exit with urwid. """ urwid.ExitMainLoop()
class CursesInterface(WidgetWrap): """ Creates a curses interface for the program, providing functions to draw all the components of the UI. Provides a facade API to draw the representation of the :class:`~turses.models.TimelineList`, help :class:`HelpBuffer` and intro :class:`Banner` screens. """ def __init__(self): self._editor = None # header header = TabsWidget() # body body = Banner() # footer self._status_bar = configuration.styles.get('status_bar', False) if self._status_bar: footer = StatusBar('') else: footer = None self.frame = Frame(body, header=header, footer=footer) super(CursesInterface, self).__init__(self.frame) def _build_overlay_widget(self, top_w, align, width, valign, height, min_width, min_height): return Overlay(top_w=Filler(top_w), bottom_w=self.frame, align=align, width=width, valign=valign, height=height, min_width=width, min_height=height) # -- Modes ---------------------------------------------------------------- def draw_timelines(self, timelines): self.frame.body = TimelinesBuffer(timelines) self.frame.set_body(self.frame.body) def show_info(self): self.frame.header.clear() self.frame.body = Banner() self.frame.set_body(self.frame.body) def show_help(self): self.clear_header() self.status_info_message(_('type <esc> to leave the help page.')) self.frame.body = HelpBuffer() self.frame.set_body(self.frame.body) # -- Header --------------------------------------------------------------- def clear_header(self): self.frame.header.clear() def set_tab_names(self, names): self.frame.header.set_tabs(names) self.frame.set_header(self.frame.header) def activate_tab(self, index): self.frame.header.set_active_tab(index) self.frame.set_header(self.frame.header) def highlight_tabs(self, indexes): self.frame.header.set_visible_tabs(indexes) # -- Footer --------------------------------------------------------------- @property def can_write_status(self): if self._status_bar: if self.frame.footer is None: self.frame.footer = StatusBar('') return True return False def status_message(self, text): if self.can_write_status: self.frame.footer.message(text) self.frame.set_footer(self.frame.footer) def status_error_message(self, message): if self.can_write_status: self.frame.footer.error_message(message) def status_info_message(self, message): if self.can_write_status: self.frame.footer.info_message(message) def clear_status(self): self.frame.footer = None self.frame.set_footer(self.frame.footer) # -- Timeline mode -------------------------------------------------------- def focus_timeline(self, index): """Give focus to the `index`-th visible timeline.""" self.frame.body.focus_timeline(index) def focus_status(self, index): if callable(getattr(self.frame.body, 'set_focus', None)): self.frame.body.set_focus(index) def center_focus(self): if callable(getattr(self.frame.body, 'set_focus_valign', None)): logging.debug('centering focus') self.frame.body.set_focus_valign('middle') # -- motions -------------------------------------------------------------- def focus_next(self): self.frame.body.scroll_down() def focus_previous(self): self.frame.body.scroll_up() def focus_first(self): self.frame.body.scroll_top() def focus_last(self): self.frame.body.scroll_bottom() # -- editors -------------------------------------------------------------- def _show_editor(self, editor_cls, prompt, content, done_signal_handler, **kwargs): self._editor = editor_cls(prompt=prompt, content=content, done_signal_handler=done_signal_handler, **kwargs) styles = configuration.styles horizontal_align = styles['editor_horizontal_align'] vertical_align = styles['editor_vertical_align'] self.show_widget_on_top(widget=self._editor, width=80, height=5, align=horizontal_align, valign=vertical_align, min_height=5,) return self._editor def show_text_editor(self, prompt='', content='', done_signal_handler=None, cursor_position=None): return self._show_editor(TextEditor, prompt, content, done_signal_handler, cursor_position=cursor_position) def show_tweet_editor(self, prompt='', content='', done_signal_handler=None, cursor_position=None): return self._show_editor(TweetEditor, prompt, content, done_signal_handler, cursor_position=cursor_position) def show_dm_editor(self, prompt='', content='', recipient='', done_signal_handler=None): return self._show_editor(DmEditor, prompt, content, done_signal_handler, recipient=recipient,) def hide_editor(self, done_signal_handler): try: disconnect_signal(self._editor, 'done', done_signal_handler) except Exception, message: # `disconnect_signal` raises an exception if no signal was # connected from `self._editor`. we can safely ignore it. logging.exception(message) self._editor = None self.hide_widget_on_top()
class PegasusGUI(WidgetWrap): def __init__(self, header=None, body=None, footer=None): _check_encoding() # Make sure terminal supports utf8 cb = self.show_exception_message utils.register_async_exception_callback(cb) self.header = header if header else Header() self.body = body if body else Banner() self.footer = footer if footer else StatusBar('') self.frame = Frame(self.body, header=self.header, footer=self.footer) self.services_view = None self.placement_view = None self.controller = None self.machine_wait_view = None self.add_services_dialog = None super().__init__(self.frame) def _build_overlay_widget(self, top_w, align, width, valign, height, min_width, min_height): return Overlay(top_w=Filler(top_w), bottom_w=self.frame, align=align, width=width, valign=valign, height=height, min_width=width, min_height=height) def show_widget_on_top(self, widget, width, height, align='center', valign='middle', min_height=0, min_width=0): """Show `widget` on top of :attr:`frame`.""" self._w = self._build_overlay_widget(top_w=widget, align=align, width=width, valign=valign, height=height, min_width=min_width, min_height=min_height) def focus_next(self): if hasattr(self.frame.body, 'scroll_down'): self.frame.body.scroll_down() def focus_previous(self): if hasattr(self.frame.body, 'scroll_up'): self.frame.body.scroll_up() def focus_first(self): if hasattr(self.frame.body, 'scroll_top'): self.frame.body.scroll_top() def focus_last(self): if hasattr(self.frame.body, 'scroll_bottom'): self.frame.body.scroll_bottom() def hide_widget_on_top(self): """Hide the topmost widget (if any).""" self._w = self.frame def show_help_info(self): widget = HelpScreen() self.show_widget_on_top(widget, width=80, height=22, align="center", valign="middle", min_height=10) def hide_help_info(self): self.hide_widget_on_top() def show_step_info(self, msg): self.hide_step_info() widget = StepInfo(msg) self.show_widget_on_top(widget, width=60, height=14, align="center", valign="middle", min_height=10) def hide_step_info(self): self.hide_widget_on_top() def show_selector_info(self, title, opts, cb): widget = Selector(title, opts, cb) self.show_widget_on_top(widget, width=50, height=10) def hide_selector_info(self): self.hide_widget_on_top() def show_selector_with_desc(self, title, opts, cb): widget = SelectorWithDescription(title, opts, cb) self.show_widget_on_top(widget, width=80, height=14) def hide_selector_with_desc(self): self.hide_widget_on_top() def show_fatal_error_message(self, msg, cb): w = InfoDialog(msg, cb) self.show_widget_on_top(w, width=50, height=20) def show_password_input(self, title, cb): widget = PasswordInput(title, cb) self.show_widget_on_top(widget, width=50, height=7) def hide_show_password_input(self): self.hide_widget_on_top() def show_maas_input(self, title, cb): widget = MaasServerInput(title, cb) self.show_widget_on_top(widget, width=50, height=10) def hide_show_maas_input(self): self.hide_widget_on_top() def show_dhcp_range(self, range_low, range_high, title, cb): widget = DhcpRangeInput(range_low, range_high, title, cb) self.show_widget_on_top(widget, width=50, height=10) def hide_show_dhcp_range(self): self.hide_widget_on_top() def show_landscape_input(self, title, cb): widget = LandscapeInput(title, cb) self.show_widget_on_top(widget, width=50, height=9) def hide_show_landscape_input(self): self.hide_widget_on_top() def set_pending_deploys(self, pending_charms): self.frame.footer.set_pending_deploys(pending_charms) def flash(self, msg): self.frame.body.flash("{}\N{HORIZONTAL ELLIPSIS}".format(msg)) def flash_reset(self): self.frame.body.flash_reset() def status_message(self, text): self.frame.footer.message(text) self.frame.set_footer(self.frame.footer) def status_error_message(self, message): self.frame.footer.error_message(message) def status_info_message(self, message): self.frame.footer.info_message( "{}\N{HORIZONTAL ELLIPSIS}".format(message)) def set_dashboard_url(self, ip, user, password): self.frame.footer.set_dashboard_url(ip, user, password) def set_jujugui_url(self, ip): self.frame.footer.set_jujugui_url(ip) def set_openstack_rel(self, text): self.frame.footer.set_openstack_rel(text) def clear_status(self): self.frame.footer = None self.frame.set_footer(self.frame.footer) def render_services_view(self, nodes, juju_state, maas_state, config, **kwargs): if self.services_view is None: self.services_view = ServicesView(nodes, juju_state, maas_state, config) self.services_view.update(nodes) self.frame.set_body(self.services_view) self.header.set_show_add_units_hotkey(True) def render_node_install_wait(self, message=None, **kwargs): self.frame.body = NodeInstallWaitMode(message, **kwargs) self.frame.set_body(self.frame.body) def render_placement_view(self, loop, config, cb): """ render placement view :param cb: deploy callback trigger """ if self.placement_view is None: assert self.controller is not None pc = self.controller.placement_controller self.placement_view = PlacementView(self, pc, loop, config, cb) self.placement_view.update() self.frame.body = self.placement_view def render_machine_wait_view(self, config): if self.machine_wait_view is None: self.machine_wait_view = MachineWaitView(self, self.current_installer, config) self.machine_wait_view.update() self.frame.body = self.machine_wait_view def render_add_services_dialog(self, deploy_cb, cancel_cb): def reset(): self.add_services_dialog = None def cancel(): reset() cancel_cb() def deploy(): reset() deploy_cb() if self.add_services_dialog is None: self.add_services_dialog = AddServicesDialog(self.controller, deploy_cb=deploy, cancel_cb=cancel) self.add_services_dialog.update() self.frame.body = Filler(self.add_services_dialog) def show_exception_message(self, ex, logpath="~/.cloud-install/commands.log"): def handle_done(*args, **kwargs): raise urwid.ExitMainLoop() self.hide_widget_on_top() msg = ("A fatal error has occurred: {}\n" "See {} for further info.".format(ex.args[0], logpath)) self.show_fatal_error_message(msg, handle_done) def select_install_type(self, install_types, cb): """ Dialog for selecting installation type """ self.status_info_message("Choose your installation path") self.show_selector_with_desc('Install Type', install_types, cb) def select_maas_type(self, cb): """ Perform multi install based on existing MAAS or if a new MAAS will be installed """ self.status_info_message( "Please enter the Server IP and your " "administrator's API Key of the existing MAAS server") self.show_maas_input("MAAS Setup", cb) def __repr__(self): return "<Ubuntu OpenStack Installer GUI Interface>" def tasker(self, loop, config): """ Interface with Tasker class :param loop: urwid.Mainloop :param dict config: config object """ return Tasker(self, loop, config) def exit(self, loop=None): """ Provide exit loop helper :param loop: Just a placeholder, exit with urwid. """ urwid.ExitMainLoop()
class PegasusGUI(WidgetWrap): key_conversion_map = {'tab': 'down', 'shift tab': 'up'} def __init__(self, header=None, body=None, footer=None): _check_encoding() # Make sure terminal supports utf8 cb = self.show_exception_message utils.register_async_exception_callback(cb) self.header = header if header else Header() self.body = body if body else Banner() self.footer = footer if footer else StatusBar('') self.frame = Frame(self.body, header=self.header, footer=self.footer) self.services_view = None self.placement_view = None self.controller = None self.machine_wait_view = None self.add_services_dialog = None super().__init__(self.frame) def keypress(self, size, key): key = self.key_conversion_map.get(key, key) return super().keypress(size, key) def _build_overlay_widget(self, top_w, align, width, valign, height, min_width, min_height): return Overlay(top_w=Filler(top_w), bottom_w=self.frame, align=align, width=width, valign=valign, height=height, min_width=width, min_height=height) def show_widget_on_top(self, widget, width, height, align='center', valign='middle', min_height=0, min_width=0): """Show `widget` on top of :attr:`frame`.""" self._w = self._build_overlay_widget(top_w=widget, align=align, width=width, valign=valign, height=height, min_width=min_width, min_height=min_height) def focus_next(self): if hasattr(self.frame.body, 'scroll_down'): self.frame.body.scroll_down() def focus_previous(self): if hasattr(self.frame.body, 'scroll_up'): self.frame.body.scroll_up() def focus_first(self): if hasattr(self.frame.body, 'scroll_top'): self.frame.body.scroll_top() def focus_last(self): if hasattr(self.frame.body, 'scroll_bottom'): self.frame.body.scroll_bottom() def hide_widget_on_top(self): """Hide the topmost widget (if any).""" self._w = self.frame def show_help_info(self): self.controller = self.frame.body self.frame.body = HelpScreen() def show_step_info(self, msg): self.frame.body = StepInfo(msg) def show_selector_with_desc(self, title, opts, cb): self.frame.body = SelectorWithDescription(title, opts, cb) def show_fatal_error_message(self, msg, cb): w = InfoDialog(msg, cb) self.show_widget_on_top(w, width=50, height=20) def show_password_input(self, title, cb): self.frame.body = PasswordInput(title, cb) def show_maas_input(self, title, cb): self.frame.body = MaasServerInput(title, cb) def show_landscape_input(self, title, cb): self.frame.body = LandscapeInput(title, cb) def set_pending_deploys(self, pending_charms): self.frame.footer.set_pending_deploys(pending_charms) def flash(self, msg): self.frame.body.flash("{}\N{HORIZONTAL ELLIPSIS}".format(msg)) def flash_reset(self): self.frame.body.flash_reset() def status_message(self, text): self.frame.footer.message(text) self.frame.set_footer(self.frame.footer) def status_error_message(self, message): self.frame.footer.error_message(message) def status_info_message(self, message): self.frame.footer.info_message( "{}\N{HORIZONTAL ELLIPSIS}".format(message)) def set_dashboard_url(self, ip, user, password): self.frame.footer.set_dashboard_url(ip, user, password) def set_jujugui_url(self, ip): self.frame.footer.set_jujugui_url(ip) def set_openstack_rel(self, release): self.frame.header.set_openstack_rel(release) def clear_status(self): self.frame.footer = None self.frame.set_footer(self.frame.footer) def render_services_view(self, nodes, juju_state, maas_state, config, **kwargs): if self.services_view is None: self.services_view = ServicesView(nodes, juju_state, maas_state, config) self.services_view.update(nodes) self.frame.set_body(self.services_view) self.header.set_show_add_units_hotkey(True) dc = config.getopt('deploy_complete') dcstr = "complete" if dc else "pending" rc = config.getopt('relations_complete') rcstr = "complete" if rc else "pending" ppc = config.getopt('postproc_complete') ppcstr = "complete" if ppc else "pending" self.status_info_message("Status: Deployments {}, " "Relations {}, " "Post-processing {} ".format( dcstr, rcstr, ppcstr)) def render_node_install_wait(self, message=None, **kwargs): self.frame.body = NodeInstallWaitMode(message, **kwargs) def render_placement_view(self, loop, config, cb): """ render placement view :param cb: deploy callback trigger """ if self.placement_view is None: assert self.controller is not None pc = self.controller.placement_controller self.placement_view = PlacementView(self, pc, loop, config, cb) self.placement_view.update() self.frame.body = self.placement_view def render_machine_wait_view(self, config): if self.machine_wait_view is None: self.machine_wait_view = MachineWaitView(self, self.current_installer, config) self.machine_wait_view.update() self.frame.body = self.machine_wait_view def render_add_services_dialog(self, deploy_cb, cancel_cb): def reset(): self.add_services_dialog = None def cancel(): reset() cancel_cb() def deploy(): reset() deploy_cb() if self.add_services_dialog is None: self.add_services_dialog = AddServicesDialog(self.controller, deploy_cb=deploy, cancel_cb=cancel) self.add_services_dialog.update() self.frame.body = Filler(self.add_services_dialog) def show_exception_message(self, ex): msg = ("A fatal error has occurred: {}\n".format(ex.args[0])) log.error(msg) self.frame.body = ErrorView(ex.args[0]) AlarmMonitor.remove_all() def select_install_type(self, install_types, cb): """ Dialog for selecting installation type """ self.show_selector_with_desc( 'Select the type of installation to perform', install_types, cb) def __repr__(self): return "<Ubuntu OpenStack Installer GUI Interface>" def tasker(self, loop, config): """ Interface with Tasker class :param loop: urwid.Mainloop :param dict config: config object """ return Tasker(self, loop, config) def exit(self, loop=None): """ Provide exit loop helper :param loop: Just a placeholder, exit with urwid. """ urwid.ExitMainLoop()
class CursesInterface(WidgetWrap): """ Creates a curses interface for the program, providing functions to draw all the components of the UI. Provides a facade API to draw the representation of the :class:`~turses.models.TimelineList`, help :class:`HelpBuffer` and intro :class:`Banner` screens. """ def __init__(self): self._editor = None # header header = TabsWidget() # body body = Banner() # footer self._status_bar = configuration.styles.get('status_bar', False) if self._status_bar: footer = StatusBar('') else: footer = None self.frame = Frame(body, header=header, footer=footer) super(CursesInterface, self).__init__(self.frame) def _build_overlay_widget(self, top_w, align, width, valign, height, min_width, min_height): return Overlay(top_w=Filler(top_w), bottom_w=self.frame, align=align, width=width, valign=valign, height=height, min_width=width, min_height=height) # -- Modes ---------------------------------------------------------------- def draw_timelines(self, timelines): self.frame.body = TimelinesBuffer(timelines) self.frame.set_body(self.frame.body) def show_info(self): self.frame.header.clear() self.frame.body = Banner() self.frame.set_body(self.frame.body) def show_help(self): self.clear_header() self.status_info_message(_('type <esc> to leave the help page.')) self.frame.body = HelpBuffer() self.frame.set_body(self.frame.body) # -- Header --------------------------------------------------------------- def clear_header(self): self.frame.header.clear() def set_tab_names(self, names): self.frame.header.set_tabs(names) self.frame.set_header(self.frame.header) def activate_tab(self, index): self.frame.header.set_active_tab(index) self.frame.set_header(self.frame.header) def highlight_tabs(self, indexes): self.frame.header.set_visible_tabs(indexes) # -- Footer --------------------------------------------------------------- @property def can_write_status(self): if self._status_bar: if self.frame.footer is None: self.frame.footer = StatusBar('') return True return False def status_message(self, text): if self.can_write_status: self.frame.footer.message(text) self.frame.set_footer(self.frame.footer) def status_error_message(self, message): if self.can_write_status: self.frame.footer.error_message(message) def status_info_message(self, message): if self.can_write_status: self.frame.footer.info_message(message) def clear_status(self): self.frame.footer = None self.frame.set_footer(self.frame.footer) # -- Timeline mode -------------------------------------------------------- def focus_timeline(self, index): """Give focus to the `index`-th visible timeline.""" self.frame.body.focus_timeline(index) def focus_status(self, index): if callable(getattr(self.frame.body, 'set_focus', None)): self.frame.body.set_focus(index) def center_focus(self): if callable(getattr(self.frame.body, 'set_focus_valign', None)): logging.debug('centering focus') self.frame.body.set_focus_valign('middle') # -- motions -------------------------------------------------------------- def focus_next(self): self.frame.body.scroll_down() def focus_previous(self): self.frame.body.scroll_up() def focus_first(self): self.frame.body.scroll_top() def focus_last(self): self.frame.body.scroll_bottom() # -- editors -------------------------------------------------------------- def _show_editor(self, editor_cls, prompt, content, done_signal_handler, **kwargs): self._editor = editor_cls(prompt=prompt, content=content, done_signal_handler=done_signal_handler, **kwargs) styles = configuration.styles horizontal_align = styles['editor_horizontal_align'] vertical_align = styles['editor_vertical_align'] self.show_widget_on_top(widget=self._editor, width=80, height=5, align=horizontal_align, valign=vertical_align, min_height=5,) return self._editor def show_text_editor(self, prompt='', content='', done_signal_handler=None, cursor_position=None): return self._show_editor(TextEditor, prompt, content, done_signal_handler, cursor_position=cursor_position) def show_tweet_editor(self, prompt='', content='', done_signal_handler=None, cursor_position=None): return self._show_editor(TweetEditor, prompt, content, done_signal_handler, cursor_position=cursor_position) def show_dm_editor(self, prompt='', content='', recipient='', done_signal_handler=None): return self._show_editor(DmEditor, prompt, content, done_signal_handler, recipient=recipient,) def hide_editor(self, done_signal_handler): try: disconnect_signal(self._editor, 'done', done_signal_handler) except Exception as message: # `disconnect_signal` raises an exception if no signal was # connected from `self._editor`. we can safely ignore it. logging.exception(message) self._editor = None self.hide_widget_on_top() # - pop ups --------------------------------------------------------------- def show_user_info(self, user, last_statuses): widget = UserInfo(user, last_statuses) # TODO: adjust height to widget size self.show_widget_on_top(widget, width=50, height=28) def hide_user_info(self): self.hide_widget_on_top() def show_widget_on_top(self, widget, width, height, align='center', valign='middle', min_height=0, min_width=0): """Show `widget` on top of :attr:`frame`.""" self._w = self._build_overlay_widget(top_w=widget, align=align, width=width, valign=valign, height=height, min_width=min_width, min_height=min_height) def hide_widget_on_top(self): """Hide the topmost widget (if any).""" self._w = self.frame
class PegasusGUI(WidgetWrap): def __init__(self, header=None, body=None, footer=None): _check_encoding() # Make sure terminal supports utf8 self.header = header if header else Header() self.body = body if body else Banner() self.footer = footer if footer else StatusBar('') self.frame = Frame(self.body, header=self.header, footer=self.footer) self.placement_view = None super().__init__(self.frame) def _build_overlay_widget(self, top_w, align, width, valign, height, min_width, min_height): return Overlay(top_w=Filler(top_w), bottom_w=self.frame, align=align, width=width, valign=valign, height=height, min_width=width, min_height=height) def show_widget_on_top(self, widget, width, height, align='center', valign='middle', min_height=0, min_width=0): """Show `widget` on top of :attr:`frame`.""" self._w = self._build_overlay_widget(top_w=widget, align=align, width=width, valign=valign, height=height, min_width=min_width, min_height=min_height) def focus_next(self): self.frame.body.scroll_down() def focus_previous(self): self.frame.body.scroll_up() def focus_first(self): self.frame.body.scroll_top() def focus_last(self): self.frame.body.scroll_bottom() def hide_widget_on_top(self): """Hide the topmost widget (if any).""" self._w = self.frame def show_help_info(self): widget = HelpScreen() self.show_widget_on_top(widget, width=80, height=22, align="center", valign="middle", min_height=10) def hide_help_info(self): self.hide_widget_on_top() def show_step_info(self, msg, width=60, height=10): self.hide_step_info() widget = StepInfo(msg, height) self.show_widget_on_top(widget, width=width, height=height + 1, align="center", valign="middle", min_height=10) def hide_step_info(self): self.hide_widget_on_top() def show_selector_info(self, title, opts, cb): widget = Selector(title, opts, cb) self.show_widget_on_top(widget, width=50, height=10) def hide_selector_info(self): self.hide_widget_on_top() def show_fatal_error_message(self, msg, cb): w = InfoDialog(msg, cb) self.show_widget_on_top(w, width=50, height=20) def show_password_input(self, title, cb): widget = PasswordInput(title, cb) self.show_widget_on_top(widget, width=50, height=10) def hide_show_password_input(self): self.hide_widget_on_top() def show_maas_input(self, title, cb): widget = MaasServerInput(title, cb) self.show_widget_on_top(widget, width=50, height=10) def hide_show_maas_input(self): self.hide_widget_on_top() def show_dhcp_range(self, range_low, range_high, title, cb): widget = DhcpRangeInput(range_low, range_high, title, cb) self.show_widget_on_top(widget, width=50, height=10) def hide_show_dhcp_range(self): self.hide_widget_on_top() def show_landscape_input(self, title, cb): widget = LandscapeInput(title, cb) self.show_widget_on_top(widget, width=50, height=10) def hide_show_landscape_input(self): self.hide_widget_on_top() def show_add_charm_info(self, charms, cb): widget = AddCharmDialog(charms, cb) self.show_widget_on_top(widget, width=50, height=10) def hide_add_charm_info(self): self.hide_widget_on_top() def set_pending_deploys(self, pending_charms): self.frame.footer.set_pending_deploys(pending_charms) def status_message(self, text): self.frame.footer.message(text) self.frame.set_footer(self.frame.footer) def status_error_message(self, message): self.frame.footer.error_message(message) def status_info_message(self, message): self.frame.footer.info_message(message) def status_dashboard_url(self, ip): self.frame.footer.set_dashboard_url(ip) def status_jujugui_url(self, ip): self.frame.footer.set_jujugui_url(ip) def status_openstack_rel(self, text): self.frame.footer.set_openstack_rel(text) def clear_status(self): self.frame.footer = None self.frame.set_footer(self.frame.footer) def render_nodes(self, nodes, juju_state, maas_state, **kwargs): self.frame.body = NodeViewMode(nodes, juju_state, maas_state) self.frame.set_body(self.frame.body) self.header.set_show_add_units_hotkey(True) def render_node_install_wait(self, **kwargs): self.frame.body = NodeInstallWaitMode(**kwargs) self.frame.set_body(self.frame.body) def render_placement_view(self, display_controller, placement_controller): if self.placement_view is None: self.placement_view = PlacementView(display_controller, placement_controller) self.placement_view.update() self.frame.body = self.placement_view
class PegasusGUI(WidgetWrap): key_conversion_map = {"tab": "down", "shift tab": "up"} def __init__(self, header=None, body=None, footer=None): _check_encoding() # Make sure terminal supports utf8 cb = self.show_exception_message utils.register_async_exception_callback(cb) self.header = header if header else Header() self.body = body if body else Banner() self.footer = footer if footer else StatusBar("") self.frame = Frame(self.body, header=self.header, footer=self.footer) self.services_view = None self.placement_view = None self.controller = None self.machine_wait_view = None self.add_services_dialog = None super().__init__(self.frame) def keypress(self, size, key): key = self.key_conversion_map.get(key, key) return super().keypress(size, key) def _build_overlay_widget(self, top_w, align, width, valign, height, min_width, min_height): return Overlay( top_w=Filler(top_w), bottom_w=self.frame, align=align, width=width, valign=valign, height=height, min_width=width, min_height=height, ) def show_widget_on_top(self, widget, width, height, align="center", valign="middle", min_height=0, min_width=0): """Show `widget` on top of :attr:`frame`.""" self._w = self._build_overlay_widget( top_w=widget, align=align, width=width, valign=valign, height=height, min_width=min_width, min_height=min_height, ) def focus_next(self): if hasattr(self.frame.body, "scroll_down"): self.frame.body.scroll_down() def focus_previous(self): if hasattr(self.frame.body, "scroll_up"): self.frame.body.scroll_up() def focus_first(self): if hasattr(self.frame.body, "scroll_top"): self.frame.body.scroll_top() def focus_last(self): if hasattr(self.frame.body, "scroll_bottom"): self.frame.body.scroll_bottom() def hide_widget_on_top(self): """Hide the topmost widget (if any).""" self._w = self.frame def show_help_info(self): self.controller = self.frame.body self.frame.body = HelpScreen() def show_step_info(self, msg): self.frame.body = StepInfo(msg) def show_selector_with_desc(self, title, opts, cb): self.frame.body = SelectorWithDescription(title, opts, cb) def show_fatal_error_message(self, msg, cb): w = InfoDialog(msg, cb) self.show_widget_on_top(w, width=50, height=20) def show_password_input(self, title, cb): self.frame.body = PasswordInput(title, cb) def show_maas_input(self, title, cb): self.frame.body = MaasServerInput(title, cb) def show_landscape_input(self, title, cb): self.frame.body = LandscapeInput(title, cb) def set_pending_deploys(self, pending_charms): self.frame.footer.set_pending_deploys(pending_charms) def flash(self, msg): self.frame.body.flash("{}\N{HORIZONTAL ELLIPSIS}".format(msg)) def flash_reset(self): self.frame.body.flash_reset() def status_message(self, text): self.frame.footer.message(text) self.frame.set_footer(self.frame.footer) def status_error_message(self, message): self.frame.footer.error_message(message) def status_info_message(self, message): self.frame.footer.info_message("{}\N{HORIZONTAL ELLIPSIS}".format(message)) def set_dashboard_url(self, ip, user, password): self.frame.footer.set_dashboard_url(ip, user, password) def set_jujugui_url(self, ip): self.frame.footer.set_jujugui_url(ip) def set_openstack_rel(self, release): self.frame.header.set_openstack_rel(release) def clear_status(self): self.frame.footer = None self.frame.set_footer(self.frame.footer) def render_services_view(self, nodes, juju_state, maas_state, config, **kwargs): if self.services_view is None: self.services_view = ServicesView(nodes, juju_state, maas_state, config) self.services_view.update(nodes) self.frame.set_body(self.services_view) self.header.set_show_add_units_hotkey(True) dc = config.getopt("deploy_complete") dcstr = "complete" if dc else "pending" rc = config.getopt("relations_complete") rcstr = "complete" if rc else "pending" ppc = config.getopt("postproc_complete") ppcstr = "complete" if ppc else "pending" self.status_info_message( "Status: Deployments {}, " "Relations {}, " "Post-processing {} ".format(dcstr, rcstr, ppcstr) ) def render_node_install_wait(self, message=None, **kwargs): self.frame.body = NodeInstallWaitMode(message, **kwargs) def render_placement_view(self, loop, config, cb): """ render placement view :param cb: deploy callback trigger """ if self.placement_view is None: assert self.controller is not None pc = self.controller.placement_controller self.placement_view = PlacementView(self, pc, loop, config, cb) self.placement_view.update() self.frame.body = self.placement_view def render_machine_wait_view(self, config): if self.machine_wait_view is None: self.machine_wait_view = MachineWaitView(self, self.current_installer, config) self.machine_wait_view.update() self.frame.body = self.machine_wait_view def render_add_services_dialog(self, deploy_cb, cancel_cb): def reset(): self.add_services_dialog = None def cancel(): reset() cancel_cb() def deploy(): reset() deploy_cb() if self.add_services_dialog is None: self.add_services_dialog = AddServicesDialog(self.controller, deploy_cb=deploy, cancel_cb=cancel) self.add_services_dialog.update() self.frame.body = Filler(self.add_services_dialog) def show_exception_message(self, ex): msg = "A fatal error has occurred: {}\n".format(ex.args[0]) log.error(msg) self.frame.body = ErrorView(ex.args[0]) AlarmMonitor.remove_all() def select_install_type(self, install_types, cb): """ Dialog for selecting installation type """ self.show_selector_with_desc("Select the type of installation to perform", install_types, cb) def __repr__(self): return "<Ubuntu OpenStack Installer GUI Interface>" def tasker(self, loop, config): """ Interface with Tasker class :param loop: urwid.Mainloop :param dict config: config object """ return Tasker(self, loop, config) def exit(self, loop=None): """ Provide exit loop helper :param loop: Just a placeholder, exit with urwid. """ urwid.ExitMainLoop()
class PegasusGUI(WidgetWrap): def __init__(self): _check_encoding() # Make sure terminal supports utf8 header = Header() body = Banner() footer = StatusBar('') self.frame = Frame(body, header=header, footer=footer) super().__init__(self.frame) def _build_overlay_widget(self, top_w, align, width, valign, height, min_width, min_height): return Overlay(top_w=Filler(top_w), bottom_w=self.frame, align=align, width=width, valign=valign, height=height, min_width=width, min_height=height) def show_widget_on_top(self, widget, width, height, align='center', valign='middle', min_height=0, min_width=0): """Show `widget` on top of :attr:`frame`.""" self._w = self._build_overlay_widget(top_w=widget, align=align, width=width, valign=valign, height=height, min_width=min_width, min_height=min_height) def focus_next(self): self.frame.body.scroll_down() def focus_previous(self): self.frame.body.scroll_up() def focus_first(self): self.frame.body.scroll_top() def focus_last(self): self.frame.body.scroll_bottom() def hide_widget_on_top(self): """Hide the topmost widget (if any).""" self._w = self.frame def show_help_info(self): widget = HelpScreen() self.show_widget_on_top(widget, width=80, height=22, align="center", valign="middle", min_height=10) def hide_help_info(self): self.hide_widget_on_top() def show_step_info(self, msg=None): self.hide_step_info() widget = StepInfo(msg) self.show_widget_on_top(widget, width=50, height=3, align="center", valign="middle", min_height=10) def hide_step_info(self): self.hide_widget_on_top() def show_add_charm_info(self, charms, cb): widget = AddCharmDialog(charms, cb) self.show_widget_on_top(widget, width=50, height=10) def hide_add_charm_info(self): self.hide_widget_on_top() def set_pending_deploys(self, pending_charms): self.frame.footer.set_pending_deploys(pending_charms) def status_message(self, text): self.frame.footer.message(text) self.frame.set_footer(self.frame.footer) def status_error_message(self, message): self.frame.footer.error_message(message) def status_info_message(self, message): self.frame.footer.info_message(message) def status_dashboard_url(self, ip): self.frame.footer.set_dashboard_url(ip) def status_jujugui_url(self, ip): self.frame.footer.set_jujugui_url(ip) def clear_status(self): self.frame.footer = None self.frame.set_footer(self.frame.footer) def render_nodes(self, nodes, juju_state, maas_state, **kwargs): self.frame.body = NodeViewMode(nodes, juju_state, maas_state) self.frame.set_body(self.frame.body) def render_node_install_wait(self, **kwargs): self.frame.body = NodeInstallWaitMode() self.frame.set_body(self.frame.body)