def __init__(self, controller): # constants self.left_margin = 0 self.top_margin = 0 # main control self.controller = controller self.main_window_w = [] # general urwid items self.clock_view = urwid.Text(ZERO_TIME, align="center") self.refresh_rate_ctrl = urwid.Edit((u'Refresh[s]:'), self.controller.refresh_rate) self.hline = urwid.AttrWrap(urwid.SolidFill(u' '), 'line') self.mode_buttons = [] self.summary_widget_index = None # Visible graphs are the graphs currently displayed, this is a # subset of the available graphs for display self.graph_place_holder = urwid.WidgetPlaceholder(urwid.Pile([])) # construct the various menus during init phase self.stress_menu = StressMenu(self.on_menu_close, self.controller.stress_exe) self.help_menu = HelpMenu(self.on_menu_close) self.about_menu = AboutMenu(self.on_menu_close) self.graphs_menu = SensorsMenu(self.on_graphs_menu_close, self.controller.sources, self.controller.graphs_default_conf) self.summary_menu = SensorsMenu(self.on_summary_menu_close, self.controller.sources, self.controller.summary_default_conf) # call super urwid.WidgetPlaceholder.__init__(self, self.main_window()) urwid.connect_signal(self.refresh_rate_ctrl, 'change', self.update_refresh_rate)
class GraphView(urwid.WidgetPlaceholder): """ A class responsible for providing the application's interface and graph display. The GraphView can change the state of the graph, since it provides the UI The change is state should be reflected in the GraphController """ def __init__(self, controller): # constants self.left_margin = 0 self.top_margin = 0 # main control self.controller = controller self.main_window_w = [] # general urwid items self.clock_view = urwid.Text(ZERO_TIME, align="center") self.refresh_rate_ctrl = urwid.Edit((u'Refresh[s]:'), self.controller.refresh_rate) self.hline = urwid.AttrWrap(urwid.SolidFill(u' '), 'line') self.mode_buttons = [] self.summary_widget_index = None # Visible graphs are the graphs currently displayed, this is a # subset of the available graphs for display self.graph_place_holder = urwid.WidgetPlaceholder(urwid.Pile([])) # construct the various menus during init phase self.stress_menu = StressMenu(self.on_menu_close, self.controller.stress_exe) self.help_menu = HelpMenu(self.on_menu_close) self.about_menu = AboutMenu(self.on_menu_close) self.graphs_menu = SensorsMenu(self.on_graphs_menu_close, self.controller.sources, self.controller.graphs_default_conf) self.summary_menu = SensorsMenu(self.on_summary_menu_close, self.controller.sources, self.controller.summary_default_conf) # call super urwid.WidgetPlaceholder.__init__(self, self.main_window()) urwid.connect_signal(self.refresh_rate_ctrl, 'change', self.update_refresh_rate) def update_refresh_rate(self, _, new_refresh_rate): # Special case of 'q' in refresh rate if 'q' in new_refresh_rate: self.on_exit_program() try: if float(new_refresh_rate) <= 0.001: pass else: self.controller.refresh_rate = new_refresh_rate except ValueError: self.controller.refresh_rate = '2.0' def update_displayed_information(self): """ Update all the graphs that are being displayed """ for source in self.controller.sources: source_name = source.get_source_name() if (any(self.graphs_menu.active_sensors[source_name]) or any(self.summary_menu.active_sensors[source_name])): source.update() for graph in self.visible_graphs.values(): try: graph.update() except IndexError: logging.debug("Graph update failed") pass # update graph summery for summary in self.visible_summaries.values(): try: summary.update() except IndexError: logging.debug("Summary update failed") pass # Only update clock if not is stress mode if self.controller.stress_conroller.get_current_mode() != 'Monitor': self.clock_view.set_text( seconds_to_text((timeit.default_timer() - self.controller.stress_start_time))) def on_reset_button(self, _): """Reset graph data and display empty graph""" for graph in self.visible_graphs.values(): graph.reset() for graph in self.graphs.values(): try: graph.source.reset() except NotImplementedError: pass # Reset clock self.clock_view.set_text(ZERO_TIME) self.update_displayed_information() def on_menu_close(self): """Return to main screen""" self.original_widget = self.main_window_w def on_graphs_menu_close(self, update): """Return to main screen and update sensor that are active in the view""" logging.info("closing sensor menu, update=%s", update) if update: for sensor, visible_sensors in \ self.graphs_menu.active_sensors.items(): self.graphs[sensor].set_visible_graphs(visible_sensors) # If not sensor is selected, do not display the graph if sensor in self.visible_graphs and not any(visible_sensors): del self.visible_graphs[sensor] elif not any(visible_sensors): pass # Update visible graphs if a sensor was selected else: self.visible_graphs[sensor] = self.graphs[sensor] self.show_graphs() self.original_widget = self.main_window_w def on_summary_menu_close(self, update): """Return to main screen and update sensor that are active in the view""" logging.info("closing summary_menu menu, update=%s", update) if update: for sensor, visible_sensors in \ self.summary_menu.active_sensors.items(): self.visible_summaries[sensor].update_visibility( visible_sensors) self.main_window_w.base_widget[0].body[self.summary_widget_index] =\ self._generate_summaries() self.original_widget = self.main_window_w def on_stress_menu_open(self, widget): """Open stress options""" self.original_widget = urwid.Overlay(self.stress_menu.main_window, self.original_widget, ('relative', self.left_margin), self.stress_menu.get_size()[1], ('relative', self.top_margin), self.stress_menu.get_size()[0]) def on_help_menu_open(self, widget): """Open Help menu""" self.original_widget = urwid.Overlay(self.help_menu.main_window, self.original_widget, ('relative', self.left_margin), self.help_menu.get_size()[1], ('relative', self.top_margin), self.help_menu.get_size()[0]) def on_about_menu_open(self, widget): """Open About menu""" self.original_widget = urwid.Overlay(self.about_menu.main_window, self.original_widget, ('relative', self.left_margin), self.about_menu.get_size()[1], ('relative', self.top_margin), self.about_menu.get_size()[0]) def on_graphs_menu_open(self, widget): """Open Sensor menu on top of existing frame""" self.original_widget = urwid.Overlay(self.graphs_menu.main_window, self.original_widget, ('relative', self.left_margin), self.graphs_menu.get_size()[1], ('relative', self.top_margin), self.graphs_menu.get_size()[0]) def on_summary_menu_open(self, widget): """Open Sensor menu on top of existing frame""" self.original_widget = urwid.Overlay(self.summary_menu.main_window, self.original_widget, ('relative', self.left_margin), self.summary_menu.get_size()[1], ('relative', self.top_margin), self.summary_menu.get_size()[0]) def on_mode_button(self, my_button, state): """Notify the controller of a new mode setting.""" if state: # The new mode is the label of the button self.controller.set_mode(my_button.get_label()) def on_unicode_checkbox(self, w=None, state=False): """Enable smooth edges if utf-8 is supported""" logging.debug("unicode State is %s", state) # Update the controller to the state of the checkbox self.controller.smooth_graph_mode = state if state: self.hline = urwid.AttrWrap( urwid.SolidFill(u'\N{LOWER ONE QUARTER BLOCK}'), 'line') else: self.hline = urwid.AttrWrap(urwid.SolidFill(u' '), 'line') for graph in self.graphs.values(): graph.set_smooth_colors(state) self.show_graphs() def on_save_settings(self, w=None): """ Calls controller save settings method """ self.controller.save_settings() def on_exit_program(self, w=None): """ Calls controller exit_program method """ self.controller.exit_program() def _generate_graph_controls(self): """ Display sidebar controls. i.e. buttons, and controls""" # setup mode radio buttons stress_modes = self.controller.stress_conroller.get_modes() group = [] for mode in stress_modes: self.mode_buttons.append( radio_button(group, mode, self.on_mode_button)) # Set default radio button to "Monitor" mode self.mode_buttons[0].set_state(True, do_callback=False) # Create list of buttons control_options = list() control_options.append(button('Graphs', self.on_graphs_menu_open)) control_options.append(button('Summaries', self.on_summary_menu_open)) if self.controller.stress_exe: control_options.append( button('Stress Options', self.on_stress_menu_open)) control_options.append(button("Reset", self.on_reset_button)) control_options.append(button('Help', self.on_help_menu_open)) control_options.append(button('About', self.on_about_menu_open)) control_options.append(button("Save Settings", self.on_save_settings)) control_options.append(button("Quit", self.on_exit_program)) # Create the menu animate_controls = urwid.GridFlow(control_options, 18, 2, 0, 'center') # Create smooth graph selection button default_smooth = self.controller.smooth_graph_mode if urwid.get_encoding_mode() == "utf8": unicode_checkbox = urwid.CheckBox( "UTF-8", state=default_smooth, on_state_change=self.on_unicode_checkbox) # Init the state of the graph accoding to the selected mode self.on_unicode_checkbox(state=default_smooth) else: unicode_checkbox = urwid.Text("[N/A] UTF-8") install_stress_message = urwid.Text("") if not self.controller.firestarter and not self.controller.stress_exe: install_stress_message = urwid.Text( ('button normal', u"(N/A) install stress")) clock_widget = [] # if self.controller.stress_exe or self.controller.firestarter: if self.controller.stress_exe or self.controller.firestarter: clock_widget = [ urwid.Text(('bold text', u"Stress Timer"), align="center"), self.clock_view ] controls = [urwid.Text(('bold text', u"Modes"), align="center")] controls += self.mode_buttons controls += [install_stress_message] controls += clock_widget controls += [ urwid.Divider(), urwid.Text(('bold text', u"Control Options"), align="center"), animate_controls, urwid.Divider(), urwid.Text(('bold text', u"Visual Options"), align="center"), unicode_checkbox, self.refresh_rate_ctrl, urwid.Divider(), urwid.Text(('bold text', u"Summaries"), align="center"), ] return controls @staticmethod def _generate_cpu_stats(): """Read and display processor name """ cpu_name = urwid.Text("CPU Name N/A", align="center") try: cpu_name = urwid.Text(get_processor_name().strip(), align="center") except OSError: logging.info("CPU name not available") return [ urwid.Text(('bold text', "CPU Detected"), align="center"), cpu_name, urwid.Divider() ] def _generate_summaries(self): fixed_stats = [] for summary in self.visible_summaries.values(): fixed_stats += summary.get_text_item_list() fixed_stats += [urwid.Text('')] # return fixed_stats pile widget return urwid.Pile(fixed_stats) def show_graphs(self): """Show a pile of the graph selected for dislpay""" elements = itertools.chain.from_iterable( ([graph] for graph in self.visible_graphs.values())) self.graph_place_holder.original_widget = urwid.Pile(elements) def main_window(self): # initiating the graphs self.graphs = OrderedDict() self.summaries = OrderedDict() for source in self.controller.sources: source_name = source.get_source_name() color_pallet = source.get_pallet() alert_pallet = source.get_alert_pallet() self.graphs[source_name] = BarGraphVector( source, color_pallet, len(source.get_sensor_list()), self.graphs_menu.active_sensors[source_name], alert_colors=alert_pallet) if self.controller.script_hooks_enabled: source.add_edge_hook( self.controller.script_loader.load_script( source.__class__.__name__, HOOK_INTERVAL)) # Invoke threshold script every 30s self.summaries[source_name] = SummaryTextList( self.graphs[source_name].source, self.summary_menu.active_sensors[source_name], ) # Check if source is available and add it to a dict of visible graphs # and summaries. # All available summaries are always visible self.visible_graphs = OrderedDict((key, val) for key, val in self.graphs.items() if val.get_is_available()) # Do not show the graph if there is no selected sensors for key in self.graphs.keys(): if not any(self.graphs_menu.active_sensors[key]): del self.visible_graphs[key] self.visible_summaries = OrderedDict( (key, val) for key, val in self.summaries.items() if val.get_is_available()) cpu_stats = self._generate_cpu_stats() graph_controls = self._generate_graph_controls() summaries = self._generate_summaries() text_col = ViListBox( urwid.SimpleListWalker(cpu_stats + graph_controls + [summaries])) vline = urwid.AttrWrap(urwid.SolidFill(u'|'), 'line') widget = urwid.Columns([('fixed', 20, text_col), ('fixed', 1, vline), ('weight', 2, self.graph_place_holder)], dividechars=0, focus_column=0) widget = urwid.Padding(widget, ('fixed left', 1), ('fixed right', 1)) self.main_window_w = widget base = self.main_window_w.base_widget[0].body self.summary_widget_index = len(base) - 1 logging.debug("Pile index: %s", self.summary_widget_index) return self.main_window_w