def treeview_widgets(self): # Treeview of current suite state, with filtering and sorting. # sorting is handled somewhat manually because the simple method # of interposing a TreeModelSort at the top: # treestore = gtk.TreeStore(str, ...) # tms = gtk.TreeModelSort( treestore ) #\ # tmf = tms.filter_new() #-- or other way round? # tv = gtk.TreeView() # tv.set_model(tms) # failed to produce correct results (the data displayed was not # consistently what should have been displayed given the # filtering in use) although the exact same code worked for a # liststore. self.sort_col_num = 0 self.ttreestore = gtk.TreeStore(str, str, str, str, str, str, str, str, str, str, gtk.gdk.Pixbuf) self.tmodelfilter = self.ttreestore.filter_new() self.tmodelfilter.set_visible_func(self.visible_cb) self.tmodelsort = gtk.TreeModelSort(self.tmodelfilter) self.ttreeview = gtk.TreeView() self.ttreeview.set_rules_hint(True) self.ttreeview.set_model(self.tmodelsort) ts = self.ttreeview.get_selection() ts.set_mode(gtk.SELECTION_SINGLE) self.ttreeview.connect('button_press_event', self.on_treeview_button_pressed) headings = [ None, 'task', 'state', 'host', 'Job ID', 'T-submit', 'T-start', 'T-finish', 'dT-mean', 'latest message' ] for n in range(1, len(headings)): # Skip first column (cycle point) tvc = gtk.TreeViewColumn(headings[n]) if n == 1: crp = gtk.CellRendererPixbuf() tvc.pack_start(crp, False) tvc.set_attributes(crp, pixbuf=10) cr = gtk.CellRendererText() tvc.pack_start(cr, True) if n == 7: tvc.set_attributes(cr, markup=n) else: tvc.set_attributes(cr, text=n) tvc.set_resizable(True) tvc.set_clickable(True) self.ttreeview.append_column(tvc) tvc.set_sort_column_id(n - 1) self.tmodelsort.set_sort_func(n - 1, self.sort_column, n - 1) sw = gtk.ScrolledWindow() sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) sw.add(self.ttreeview) self.tfilterbox = gtk.VBox() subbox1 = gtk.HBox(homogeneous=True) subbox2 = gtk.HBox(homogeneous=True) self.tfilterbox.pack_start(subbox1) self.tfilterbox.pack_start(subbox2) self.tfilter_states = [] dotm = DotMaker(self.theme, size='small') cnt = 0 for st in task_state.legal: box = gtk.HBox() icon = dotm.get_image(st) cb = gtk.CheckButton(task_state.labels[st]) tooltip = gtk.Tooltips() tooltip.enable() tooltip.set_tip(cb, "Filter by task state = %s" % st) box.pack_start(icon, expand=False) box.pack_start(cb, expand=False) cnt += 1 if cnt > (len(task_state.legal) + 1) // 2: subbox2.pack_start(box, expand=False, fill=True) else: subbox1.pack_start(box, expand=False, fill=True) if st in self.tfilter_states: cb.set_active(False) else: cb.set_active(True) cb.connect('toggled', self.check_tfilter_buttons) self.filter_entry = EntryTempText() self.filter_entry.set_width_chars(7) self.filter_entry.connect("activate", self.check_filter_entry) self.filter_entry.set_temp_text("filter") tooltip = gtk.Tooltips() tooltip.enable() tooltip.set_tip( self.filter_entry, "Filter by task name.\n" "Enter a sub-string or regex and hit Enter\n" "(to reset, clear the entry and hit Enter)") subbox2.pack_start(self.filter_entry) cnt += 1 if cnt % 2 != 0: # subbox2 needs another entry to line things up. subbox2.pack_start(gtk.HBox(), expand=False, fill=True) filter_hbox = gtk.HBox() filter_hbox.pack_start(self.tfilterbox, True, True, 10) vbox = gtk.VBox() vbox.pack_start(sw, True) vbox.pack_end(filter_hbox, False) return vbox
class SummaryPanelAppletUpdater(BaseSummaryTimeoutUpdater): """Update the summary panel applet - subclass of gsummary equivalent.""" IDLE_STOPPED_TIME = 3600 # 1 hour. MAX_INDIVIDUAL_SUITES = 5 def __init__(self, hosts, dot_hbox, gcylc_image, is_compact, owner=None, poll_interval=None): self.quit = True self.dot_hbox = dot_hbox self.gcylc_image = gcylc_image self.is_compact = is_compact self._set_gcylc_image_tooltip() self.gcylc_image.set_sensitive(False) self.theme_name = gcfg.get(['use theme']) self.theme = gcfg.get(['themes', self.theme_name]) self.dots = DotMaker(self.theme) self.statuses = {} self.stop_summaries = {} self._set_exception_hook() super(SummaryPanelAppletUpdater, self).__init__(hosts, owner=owner, poll_interval=poll_interval) def clear_stopped_suites(self): """Clear stopped suite information that may have built up.""" self.stop_summaries.clear() gobject.idle_add(self.update) def start(self): self.gcylc_image.set_sensitive(True) super(SummaryPanelAppletUpdater, self).start() self._set_gcylc_image_tooltip() def stop(self): self.gcylc_image.set_sensitive(False) super(SummaryPanelAppletUpdater, self).stop() self._set_gcylc_image_tooltip() def launch_context_menu(self, event, suite_host_tuples=None, extra_items=None): has_stopped_suites = bool(self.stop_summaries) if suite_host_tuples is None: suite_host_tuples = [] if extra_items is None: extra_items = [] gsummary_item = gtk.ImageMenuItem("Launch cylc gsummary") img = gtk.image_new_from_stock("gcylc", gtk.ICON_SIZE_MENU) gsummary_item.set_image(img) gsummary_item.show() gsummary_item.connect("button-press-event", self._on_button_press_event_gsummary) extra_items.append(gsummary_item) menu = get_summary_menu(suite_host_tuples, self.theme_name, self._set_theme, has_stopped_suites, self.clear_stopped_suites, self.hosts, self.set_hosts, self.update_now, self.start, program_name="cylc gpanel", extra_items=extra_items, owner=self.owner, is_stopped=self.quit) menu.popup(None, None, None, event.button, event.time) return False def update(self, update_time=None): """Update the Applet.""" suite_host_tuples = [] statuses = copy.deepcopy(self.statuses) stop_summaries = copy.deepcopy(self.stop_summaries) for host in self.hosts: suites = (statuses.get(host, {}).keys() + stop_summaries.get(host, {}).keys()) for suite in suites: suite_host_tuples.append((suite, host)) suite_host_tuples.sort() for child in self.dot_hbox.get_children(): self.dot_hbox.remove(child) number_mode = (not self.is_compact and len(suite_host_tuples) > self.MAX_INDIVIDUAL_SUITES) suite_statuses = {} compact_suite_statuses = [] for suite, host in suite_host_tuples: if suite in statuses.get(host, {}): status_map = statuses[host][suite] is_stopped = False else: info = stop_summaries[host][suite] status_map, suite_time = info is_stopped = True status = extract_group_state(status_map.keys(), is_stopped=is_stopped) if number_mode: suite_statuses.setdefault(is_stopped, {}) suite_statuses[is_stopped].setdefault(status, []) suite_statuses[is_stopped][status].append( (suite, host, status_map.items())) elif self.is_compact: compact_suite_statuses.append( (suite, host, status, status_map.items(), is_stopped)) else: self._add_image_box([(suite, host, status, status_map.items(), is_stopped)]) if number_mode: for is_stopped in sorted(suite_statuses.keys()): statuses = suite_statuses[is_stopped].items() statuses.sort(lambda x, y: cmp(len(y[1]), len(x[1]))) for status, suite_host_states_tuples in statuses: label = gtk.Label(str(len(suite_host_states_tuples)) + ":") label.show() self.dot_hbox.pack_start(label, expand=False, fill=False) suite_info_tuples = [] for suite, host, task_states in suite_host_states_tuples: suite_info_tuples.append( (suite, host, status, task_states, is_stopped)) self._add_image_box(suite_info_tuples) if self.is_compact: if not compact_suite_statuses: # No suites running or stopped. self.gcylc_image.show() return False self.gcylc_image.hide() self._add_image_box(compact_suite_statuses) return False def _add_image_box(self, suite_host_info_tuples): image_eb = gtk.EventBox() image_eb.show() is_all_stopped = False running_status_list = [] status_list = [] suite_host_tuples = [] for info_tuple in suite_host_info_tuples: suite, host, status, task_states, is_stopped = info_tuple suite_host_tuples.append((suite, host)) if not is_stopped: running_status_list.append(status) status_list.append(status) if running_status_list: status = extract_group_state(running_status_list, is_stopped=False) image = self.dots.get_image(status, is_stopped=False) else: status = extract_group_state(status_list, is_stopped=True) image = self.dots.get_image(status, is_stopped=True) image.show() image_eb.add(image) image_eb._connect_args = suite_host_tuples image_eb.connect("button-press-event", self._on_button_press_event) text_format = "%s - %s - %s" long_text_format = text_format + "\n Tasks: %s\n" text = "" tip_vbox = gtk.VBox() # Only used in PyGTK 2.12+ tip_vbox.show() for info_tuple in suite_host_info_tuples: suite, host, status, task_states, is_stopped = info_tuple task_states.sort(lambda x, y: cmp(len(y[1]), len(x[1]))) tip_hbox = gtk.HBox() tip_hbox.show() state_info = [] for state_name, tasks in task_states: state_info.append(str(len(tasks)) + " " + state_name) image = self.dots.get_image(state_name, is_stopped=is_stopped) image.show() tip_hbox.pack_start(image, expand=False, fill=False) states_text = ", ".join(state_info) if status is None: suite_summary = "?" else: suite_summary = status if is_stopped: suite_summary = "stopped with " + suite_summary tip_label = gtk.Label(text_format % (suite, suite_summary, host)) tip_label.show() tip_hbox.pack_start(tip_label, expand=False, fill=False, padding=5) tip_vbox.pack_start(tip_hbox, expand=False, fill=False) text += long_text_format % (suite, suite_summary, host, states_text) text = text.rstrip() if hasattr(gtk, "Tooltip"): image_eb.set_has_tooltip(True) image_eb.connect("query-tooltip", self._on_img_tooltip_query, tip_vbox) else: self._set_tooltip(image_eb, text) self.dot_hbox.pack_start(image_eb, expand=False, fill=False, padding=1) def _on_button_press_event(self, widget, event): if event.button == 1: self.launch_context_menu(event, suite_host_tuples=widget._connect_args) return False def _on_button_press_event_gsummary(self, widget, event): launch_gsummary(hosts=self.hosts, owner=self.owner) def _on_img_tooltip_query(self, widget, x, y, kbd, tooltip, tip_widget): tooltip.set_custom(tip_widget) return True def _set_exception_hook(self): # Handle an uncaught exception. old_hook = sys.excepthook sys.excepthook = ( lambda *a: self._handle_exception(*a, old_hook=old_hook)) def _handle_exception(self, e_type, e_value, e_traceback, old_hook=None): self.gcylc_image.set_from_stock(gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_MENU) exc_lines = traceback.format_exception(e_type, e_value, e_traceback) exc_text = "".join(exc_lines) info = "cylc gpanel has a problem.\n\n%s" % exc_text self._set_tooltip(self.gcylc_image, info.rstrip()) if old_hook is not None: old_hook(exception_class, exception, trace) def _set_gcylc_image_tooltip(self): if self.quit: self._set_tooltip(self.gcylc_image, "Cylc Applet - Off") else: self._set_tooltip(self.gcylc_image, "Cylc Applet - Active") def _set_theme(self, new_theme_name): self.theme_name = new_theme_name self.theme = gcfg.get(['themes', self.theme_name]) self.dots = DotMaker(self.theme) def _set_tooltip(self, widget, text): tooltip = gtk.Tooltips() tooltip.enable() tooltip.set_tip(widget, text)
class SummaryPanelAppletUpdater(BaseSummaryTimeoutUpdater): """Update the summary panel applet - subclass of gsummary equivalent.""" IDLE_STOPPED_TIME = 3600 # 1 hour. MAX_INDIVIDUAL_SUITES = 5 def __init__(self, hosts, dot_hbox, gcylc_image, is_compact, owner=None, poll_interval=None): self.quit = True self.dot_hbox = dot_hbox self.gcylc_image = gcylc_image self.is_compact = is_compact self._set_gcylc_image_tooltip() self.gcylc_image.set_sensitive(False) self.theme_name = gcfg.get( ['use theme'] ) self.theme = gcfg.get( ['themes', self.theme_name] ) self.dots = DotMaker(self.theme) self.statuses = {} self.stop_summaries = {} self._set_exception_hook() super(SummaryPanelAppletUpdater, self).__init__( hosts, owner=owner, poll_interval=poll_interval) def clear_stopped_suites(self): """Clear stopped suite information that may have built up.""" self.stop_summaries.clear() gobject.idle_add(self.update) def start(self): self.gcylc_image.set_sensitive(True) super(SummaryPanelAppletUpdater, self).start() self._set_gcylc_image_tooltip() def stop(self): self.gcylc_image.set_sensitive(False) super(SummaryPanelAppletUpdater, self).stop() self._set_gcylc_image_tooltip() def launch_context_menu(self, event, suite_host_tuples=None, extra_items=None): has_stopped_suites = bool(self.stop_summaries) if suite_host_tuples is None: suite_host_tuples = [] if extra_items is None: extra_items = [] gsummary_item = gtk.ImageMenuItem("Launch cylc gsummary") img = gtk.image_new_from_stock("gcylc", gtk.ICON_SIZE_MENU) gsummary_item.set_image(img) gsummary_item.show() gsummary_item.connect("button-press-event", self._on_button_press_event_gsummary) extra_items.append(gsummary_item) menu = get_summary_menu(suite_host_tuples, self.theme_name, self._set_theme, has_stopped_suites, self.clear_stopped_suites, self.hosts, self.set_hosts, self.update_now, self.start, program_name="cylc gpanel", extra_items=extra_items, owner=self.owner, is_stopped=self.quit) menu.popup( None, None, None, event.button, event.time ) return False def update(self, update_time=None): """Update the Applet.""" suite_host_tuples = [] statuses = copy.deepcopy(self.statuses) stop_summaries = copy.deepcopy(self.stop_summaries) for host in self.hosts: suites = (statuses.get(host, {}).keys() + stop_summaries.get(host, {}).keys()) for suite in suites: suite_host_tuples.append((suite, host)) suite_host_tuples.sort() for child in self.dot_hbox.get_children(): self.dot_hbox.remove(child) number_mode = (not self.is_compact and len(suite_host_tuples) > self.MAX_INDIVIDUAL_SUITES) suite_statuses = {} compact_suite_statuses = [] for suite, host in suite_host_tuples: if suite in statuses.get(host, {}): status_map = statuses[host][suite] is_stopped = False else: info = stop_summaries[host][suite] status_map, suite_time = info is_stopped = True status = extract_group_state(status_map.keys(), is_stopped=is_stopped) if number_mode: suite_statuses.setdefault(is_stopped, {}) suite_statuses[is_stopped].setdefault(status, []) suite_statuses[is_stopped][status].append( (suite, host, status_map.items())) elif self.is_compact: compact_suite_statuses.append((suite, host, status, status_map.items(), is_stopped)) else: self._add_image_box([(suite, host, status, status_map.items(), is_stopped)]) if number_mode: for is_stopped in sorted(suite_statuses.keys()): statuses = suite_statuses[is_stopped].items() statuses.sort(lambda x, y: cmp(len(y[1]), len(x[1]))) for status, suite_host_states_tuples in statuses: label = gtk.Label( str(len(suite_host_states_tuples)) + ":") label.show() self.dot_hbox.pack_start(label, expand=False, fill=False) suite_info_tuples = [] for suite, host, task_states in suite_host_states_tuples: suite_info_tuples.append((suite, host, status, task_states, is_stopped)) self._add_image_box(suite_info_tuples) if self.is_compact: if not compact_suite_statuses: # No suites running or stopped. self.gcylc_image.show() return False self.gcylc_image.hide() self._add_image_box(compact_suite_statuses) return False def _add_image_box(self, suite_host_info_tuples): image_eb = gtk.EventBox() image_eb.show() is_all_stopped = False running_status_list = [] status_list = [] suite_host_tuples = [] for info_tuple in suite_host_info_tuples: suite, host, status, task_states, is_stopped = info_tuple suite_host_tuples.append((suite, host)) if not is_stopped: running_status_list.append(status) status_list.append(status) if running_status_list: status = extract_group_state(running_status_list, is_stopped=False) image = self.dots.get_image(status, is_stopped=False) else: status = extract_group_state(status_list, is_stopped=True) image = self.dots.get_image(status, is_stopped=True) image.show() image_eb.add(image) image_eb._connect_args = suite_host_tuples image_eb.connect("button-press-event", self._on_button_press_event) text_format = "%s - %s - %s" long_text_format = text_format + "\n Tasks: %s\n" text = "" tip_vbox = gtk.VBox() # Only used in PyGTK 2.12+ tip_vbox.show() for info_tuple in suite_host_info_tuples: suite, host, status, task_states, is_stopped = info_tuple task_states.sort(lambda x, y: cmp(len(y[1]), len(x[1]))) tip_hbox = gtk.HBox() tip_hbox.show() state_info = [] for state_name, tasks in task_states: state_info.append(str(len(tasks)) + " " + state_name) image = self.dots.get_image(state_name, is_stopped=is_stopped) image.show() tip_hbox.pack_start(image, expand=False, fill=False) states_text = ", ".join(state_info) if status is None: suite_summary = "?" else: suite_summary = status if is_stopped: suite_summary = "stopped with " + suite_summary tip_label = gtk.Label(text_format % (suite, suite_summary, host)) tip_label.show() tip_hbox.pack_start(tip_label, expand=False, fill=False, padding=5) tip_vbox.pack_start(tip_hbox, expand=False, fill=False) text += long_text_format % (suite, suite_summary, host, states_text) text = text.rstrip() if hasattr(gtk, "Tooltip"): image_eb.set_has_tooltip(True) image_eb.connect("query-tooltip", self._on_img_tooltip_query, tip_vbox) else: self._set_tooltip(image_eb, text) self.dot_hbox.pack_start(image_eb, expand=False, fill=False, padding=1) def _on_button_press_event(self, widget, event): if event.button == 1: self.launch_context_menu(event, suite_host_tuples=widget._connect_args) return False def _on_button_press_event_gsummary(self, widget, event): launch_gsummary(hosts=self.hosts, owner=self.owner) def _on_img_tooltip_query(self, widget, x, y, kbd, tooltip, tip_widget): tooltip.set_custom(tip_widget) return True def _set_exception_hook(self): # Handle an uncaught exception. old_hook = sys.excepthook sys.excepthook = (lambda *a: self._handle_exception(*a, old_hook=old_hook)) def _handle_exception(self, e_type, e_value, e_traceback, old_hook=None): self.gcylc_image.set_from_stock(gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_MENU) exc_lines = traceback.format_exception(e_type, e_value, e_traceback) exc_text = "".join(exc_lines) info = "cylc gpanel has a problem.\n\n%s" % exc_text self._set_tooltip(self.gcylc_image, info.rstrip()) if old_hook is not None: old_hook(exception_class, exception, trace) def _set_gcylc_image_tooltip(self): if self.quit: self._set_tooltip(self.gcylc_image, "Cylc Applet - Off") else: self._set_tooltip(self.gcylc_image, "Cylc Applet - Active") def _set_theme(self, new_theme_name): self.theme_name = new_theme_name self.theme = gcfg.get( ['themes', self.theme_name] ) self.dots = DotMaker(self.theme) def _set_tooltip(self, widget, text): tooltip = gtk.Tooltips() tooltip.enable() tooltip.set_tip(widget, text)