def get_control_widgets(self): self.t = GraphUpdater(self.cfg, self.updater, self.theme, self.info_bar, self.xdot) self.t.start() return self.xdot.get()
class ControlGraph(object): """ Dependency graph suite control interface. """ def __init__(self, cfg, updater, theme, dot_size, info_bar, get_right_click_menu, log_colors, insert_task_popup): # NOTE: this view has separate family Group and Ungroup buttons # instead of a single Group/Ungroup toggle button, unlike the # other views the graph view can display intermediate states # between fully grouped and fully ungrouped (well, so can the # tree view, but in that case the toggle button determines # whether it displays a flat list or a proper tree view). self.cfg = cfg self.updater = updater self.theme = theme self.info_bar = info_bar self.get_right_click_menu = get_right_click_menu self.log_colors = log_colors self.insert_task_popup = insert_task_popup self.gcapture_windows = [] self.xdot = xdot_widgets() self.xdot.widget.connect('clicked', self.on_url_clicked) self.xdot.widget.connect_after('motion-notify-event', self.on_motion_notify) self.last_url = None def get_control_widgets(self): self.t = GraphUpdater(self.cfg, self.updater, self.theme, self.info_bar, self.xdot) self.t.start() return self.xdot.get() def toggle_graph_disconnect(self, w, update_button): if w.get_active(): self.t.graph_disconnect = True self._set_tooltip(w, "Click to reconnect") update_button.set_sensitive(True) else: self.t.graph_disconnect = False self._set_tooltip(w, "Click to disconnect") update_button.set_sensitive(False) return True def graph_update(self, w): self.t.action_required = True def on_url_clicked(self, widget, url, event): if event.button != 3: return False m = re.match('base:(.*)', url) if m: # base graph node task_id = m.groups()[0] self.right_click_menu(event, task_id, type_='base graph task') return self.right_click_menu(event, url, type_='live task') def on_motion_notify(self, widget, event): """Add a new tooltip when the cursor moves in the graph.""" url = self.xdot.widget.get_url(event.x, event.y) if url == self.last_url: return False self.last_url = url if not hasattr(self.xdot.widget, "set_tooltip_text"): # Unfortunately, the older gtk.Tooltips doesn't work well here. # gtk.Widget.set_tooltip_text was introduced at PyGTK 2.12 return False if url is None: self.xdot.widget.set_tooltip_text(None) return False url = unicode(url.url) m = re.match('base:(.*)', url) if m: task_id = m.groups()[0] self.xdot.widget.set_tooltip_text(self.t.get_summary(task_id)) return False # URL is task ID self.xdot.widget.set_tooltip_text(self.t.get_summary(url)) return False def stop(self): self.t.quit = True def right_click_menu(self, event, task_id, type_='live task'): name, point_string = TaskID.split(task_id) menu = gtk.Menu() menu_root = gtk.MenuItem(task_id) menu_root.set_submenu(menu) timezoom_item_direct = gtk.MenuItem('Focus on ' + point_string) timezoom_item_direct.connect('activate', self.focused_timezoom_direct, point_string) # TODO - pre cylc-6 could focus on a range of points (was hours-based). # timezoom_item = gtk.MenuItem('Focus on Range') # timezoom_item.connect( # 'activate', self.focused_timezoom_popup, task_id) timezoom_reset_item = gtk.MenuItem('Focus Reset') timezoom_reset_item.connect('activate', self.focused_timezoom_direct, None) group_item = gtk.ImageMenuItem('Group') img = gtk.image_new_from_stock('group', gtk.ICON_SIZE_MENU) group_item.set_image(img) group_item.set_sensitive(not self.t.have_leaves_and_feet or name not in self.t.feet) group_item.connect('activate', self.grouping, name, True) ungroup_item = gtk.ImageMenuItem('UnGroup') img = gtk.image_new_from_stock('ungroup', gtk.ICON_SIZE_MENU) ungroup_item.set_image(img) ungroup_item.set_sensitive(not self.t.have_leaves_and_feet or name not in self.t.leaves) ungroup_item.connect('activate', self.grouping, name, False) ungroup_rec_item = gtk.ImageMenuItem('Recursive UnGroup') img = gtk.image_new_from_stock('ungroup', gtk.ICON_SIZE_MENU) ungroup_rec_item.set_image(img) ungroup_rec_item.set_sensitive(not self.t.have_leaves_and_feet or name not in self.t.leaves) ungroup_rec_item.connect('activate', self.grouping, name, False, True) menu.append(gtk.SeparatorMenuItem()) if type is not 'live task': insert_item = gtk.ImageMenuItem('Insert ...') img = gtk.image_new_from_stock(gtk.STOCK_DIALOG_INFO, gtk.ICON_SIZE_MENU) insert_item.set_image(img) menu.append(insert_item) insert_item.connect( 'button-press-event', lambda *a: self.insert_task_popup( is_fam=(name in self.t.descendants), name=name, point_string=point_string)) menu.append(gtk.SeparatorMenuItem()) menu.append(timezoom_item_direct) menu.append(timezoom_reset_item) menu.append(gtk.SeparatorMenuItem()) menu.append(group_item) menu.append(ungroup_item) menu.append(ungroup_rec_item) if type_ == 'live task': is_fam = (name in self.t.descendants) if is_fam: if task_id not in self.t.fam_state_summary: return False t_state = self.t.fam_state_summary[task_id]['state'] else: if task_id not in self.t.state_summary: return False t_state = self.t.state_summary[task_id]['state'] default_menu = self.get_right_click_menu([task_id], [t_state], task_is_family=[is_fam], is_graph_view=True) dm_kids = default_menu.get_children() for item in reversed(dm_kids[:2]): # Put task name and URL at the top. default_menu.remove(item) menu.prepend(item) for item in dm_kids[2:]: # And the rest of the default menu at the bottom. default_menu.remove(item) menu.append(item) menu.show_all() menu.popup(None, None, None, event.button, event.time) # TODO - popup menus are not automatically destroyed and can be # reused if saved; however, we need to reconstruct or at least # alter ours dynamically => should destroy after each use to # prevent a memory leak? But I'm not sure how to do this as yet.) return True def grouping(self, w, name, group, rec=False): self.t.ungroup_recursive = rec self.t.group_all = False self.t.ungroup_all = False if group: self.t.group = [name] self.t.ungroup = [] else: self.t.ungroup = [name] self.t.group = [] self.t.action_required = True self.t.best_fit = True def rearrange(self, col, n): cols = self.ttreeview.get_columns() for i_n in range(len(cols)): if i_n == n: cols[i_n].set_sort_indicator(True) else: cols[i_n].set_sort_indicator(False) # col is cols[n] if col.get_sort_order() == gtk.SORT_ASCENDING: col.set_sort_order(gtk.SORT_DESCENDING) else: col.set_sort_order(gtk.SORT_ASCENDING) self.ttreestore.set_sort_column_id(n, col.get_sort_order()) def refresh(self): self.t.update() self.t.best_fit = True self.t.action_required = True def get_menuitems(self): """Return the menu items specific to this view.""" items = [] graph_range_item = gtk.MenuItem('Time Range Focus ...') items.append(graph_range_item) graph_range_item.connect('activate', self.graph_timezoom_popup) crop_item = gtk.CheckMenuItem('Toggle _Crop Base Graph') items.append(crop_item) crop_item.set_active(self.t.crop) crop_item.connect('activate', self.toggle_crop) self.menu_group_item = gtk.ImageMenuItem('_Group All Families') img = gtk.image_new_from_stock('group', gtk.ICON_SIZE_MENU) self.menu_group_item.set_image(img) items.append(self.menu_group_item) self.menu_group_item.connect('activate', self.group_all, True) self.menu_ungroup_item = gtk.ImageMenuItem('_UnGroup All Families') img = gtk.image_new_from_stock('ungroup', gtk.ICON_SIZE_MENU) self.menu_ungroup_item.set_image(img) items.append(self.menu_ungroup_item) self.menu_ungroup_item.connect('activate', self.group_all, False) menu_left_to_right_item = gtk.CheckMenuItem('_Transpose Graph') items.append(menu_left_to_right_item) menu_left_to_right_item.set_active(self.t.orientation == "LR") menu_left_to_right_item.connect('activate', self.toggle_left_to_right_mode) self.menu_subgraphs_item = gtk.CheckMenuItem( '_Organise by Cycle Point') items.append(self.menu_subgraphs_item) self.menu_subgraphs_item.set_active(self.t.subgraphs_on) self.menu_subgraphs_item.connect('activate', self.toggle_cycle_point_subgraphs) igsui_item = gtk.CheckMenuItem('_Ignore Suicide Triggers') items.append(igsui_item) igsui_item.set_active(self.t.ignore_suicide) igsui_item.connect('activate', self.toggle_ignore_suicide_triggers) self.menu_frames_item = gtk.CheckMenuItem('_Write Graph Frames') items.append(self.menu_frames_item) self.menu_frames_item.set_active(self.t.write_dot_frames) self.menu_frames_item.connect('activate', self.toggle_write_dot_frames) return items def _set_tooltip(self, widget, tip_text): tip = gtk.Tooltips() tip.enable() tip.set_tip(widget, tip_text) def get_toolitems(self): """Return the tool bar items specific to this view.""" items = [] for child in self.xdot.vbox.get_children(): if isinstance(child, gtk.HButtonBox): self.xdot.vbox.remove(child) self.group_toolbutton = gtk.ToolButton() g_image = gtk.image_new_from_stock('group', gtk.ICON_SIZE_SMALL_TOOLBAR) self.group_toolbutton.set_icon_widget(g_image) self.group_toolbutton.set_label("Group") self.group_toolbutton.connect('clicked', self.group_all, True) self._set_tooltip(self.group_toolbutton, "Graph View - Click to group all task families") items.append(self.group_toolbutton) self.ungroup_toolbutton = gtk.ToolButton() g_image = gtk.image_new_from_stock('ungroup', gtk.ICON_SIZE_SMALL_TOOLBAR) self.ungroup_toolbutton.set_icon_widget(g_image) self.ungroup_toolbutton.set_label("Ungroup") self.ungroup_toolbutton.connect('clicked', self.group_all, False) self._set_tooltip(self.ungroup_toolbutton, "Graph View - Click to ungroup all task families") items.append(self.ungroup_toolbutton) self.transpose_toolbutton = gtk.ToggleToolButton() self.transpose_toolbutton.set_active(not self.t.orientation == "TB") g_image = gtk.image_new_from_stock('transpose', gtk.ICON_SIZE_SMALL_TOOLBAR) self.transpose_toolbutton.set_icon_widget(g_image) self.transpose_toolbutton.set_label("Transpose") self.transpose_toolbutton.connect('clicked', self.toggle_left_to_right_mode) self._set_tooltip(self.transpose_toolbutton, "Graph View - Click to transpose graph") items.append(self.transpose_toolbutton) self.subgraphs_button = gtk.ToggleToolButton() image = gtk.image_new_from_stock(gtk.STOCK_LEAVE_FULLSCREEN, gtk.ICON_SIZE_SMALL_TOOLBAR) self.subgraphs_button.set_icon_widget(image) self.subgraphs_button.set_label("Subgraphs") self.subgraphs_button.connect('clicked', self.toggle_cycle_point_subgraphs) self._set_tooltip(self.subgraphs_button, "Graph View - Click to organise by cycle point") items.append(self.subgraphs_button) zoomin_button = gtk.ToolButton(gtk.STOCK_ZOOM_IN) zoomin_button.connect('clicked', self.xdot.widget.on_zoom_in) zoomin_button.set_label(None) self._set_tooltip(zoomin_button, "Graph View - Zoom In") items.append(zoomin_button) zoomout_button = gtk.ToolButton(gtk.STOCK_ZOOM_OUT) zoomout_button.connect('clicked', self.xdot.widget.on_zoom_out) zoomout_button.set_label(None) self._set_tooltip(zoomout_button, "Graph View - Zoom Out") items.append(zoomout_button) zoomfit_button = gtk.ToolButton(gtk.STOCK_ZOOM_FIT) zoomfit_button.connect('clicked', self.xdot.widget.on_zoom_fit) zoomfit_button.set_label(None) self._set_tooltip(zoomfit_button, "Graph View - Best Fit") items.append(zoomfit_button) zoom100_button = gtk.ToolButton(gtk.STOCK_ZOOM_100) zoom100_button.connect('clicked', self.xdot.widget.on_zoom_100) zoom100_button.set_label(None) self._set_tooltip(zoom100_button, "Graph View - Normal Size") items.append(zoom100_button) connect_button = gtk.ToggleToolButton() image = gtk.image_new_from_stock(gtk.STOCK_DISCONNECT, gtk.ICON_SIZE_SMALL_TOOLBAR) connect_button.set_icon_widget(image) connect_button.set_label("Disconnect") self._set_tooltip(connect_button, "Graph View - Click to disconnect") items.append(connect_button) update_button = gtk.ToolButton(gtk.STOCK_REFRESH) update_button.connect('clicked', self.graph_update) update_button.set_label(None) update_button.set_sensitive(False) self._set_tooltip(update_button, "Graph View - Update graph") items.append(update_button) connect_button.connect('clicked', self.toggle_graph_disconnect, update_button) return items def group_all(self, w, group): if group: self.t.group_all = True self.t.ungroup_all = False if "graph" in self.cfg.ungrouped_views: self.cfg.ungrouped_views.remove("graph") else: self.t.ungroup_all = True self.t.group_all = False if "graph" not in self.cfg.ungrouped_views: self.cfg.ungrouped_views.append("graph") self.t.action_required = True self.t.best_fit = True def toggle_crop(self, w): self.t.crop = not self.t.crop self.t.action_required = True def toggle_write_dot_frames(self, w): self.t.toggle_write_dot_frames() def toggle_cycle_point_subgraphs(self, toggle_item): subgraphs_on = toggle_item.get_active() if subgraphs_on == self.t.subgraphs_on: return self.t.subgraphs_on = subgraphs_on if isinstance(toggle_item, gtk.ToggleToolButton): self.menu_subgraphs_item.set_active(self.t.subgraphs_on) else: self.subgraphs_button.set_active(self.t.subgraphs_on) self.t.action_required = True def toggle_left_to_right_mode(self, w): """Change the graph to left-to-right or top-to-bottom.""" if self.t.orientation == "TB": # Top -> bottom ordering self.t.orientation = "LR" # Left -> right ordering elif self.t.orientation == "LR": self.t.orientation = "TB" self.t.best_fit = True self.t.action_required = True def toggle_ignore_suicide_triggers(self, w): self.t.ignore_suicide = not self.t.ignore_suicide self.t.action_required = True # def focused_timezoom_popup(self, w, id): # window = gtk.Window() # window.modify_bg(gtk.STATE_NORMAL, # gtk.gdk.color_parse(self.log_colors.get_color())) # window.set_border_width(5) # window.set_title("Cycle Point Zoom") # parent_window = self.xdot.widget.get_toplevel() # if isinstance(parent_window, gtk.Window): # window.set_transient_for(parent_window) # window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) # vbox = gtk.VBox() # # name, point_string = TaskID.split(id) # # TODO - do we need to check that oldest_point_string is defined yet? # # #diff_pre = ctime - self.t.oldest_ctime.hours # #diff_post = self.t.newest_ctime - ctime.hours # # ... # #start_entry.set_text(str(diff_pre)) # #stop_entry.set_text(str(diff_post)) # # # TODO - error checking on date range given # box = gtk.HBox() # label = gtk.Label('Pre (hours)') # box.pack_start(label, True) # start_entry = gtk.Entry() # box.pack_start (start_entry, True) # vbox.pack_start(box) # # box = gtk.HBox() # label = gtk.Label('Post (hours)') # box.pack_start(label, True) # stop_entry = gtk.Entry() # box.pack_start (stop_entry, True) # vbox.pack_start(box) # # cancel_button = gtk.Button("_Close") # cancel_button.connect("clicked", lambda x: window.destroy()) # # reset_button = gtk.Button("_Reset (No Zoom)") # reset_button.connect("clicked", self.focused_timezoom_direct, None) # # apply_button = gtk.Button("_Apply") # apply_button.connect("clicked", self.focused_timezoom, # point_string, start_entry, stop_entry) # # hbox = gtk.HBox() # hbox.pack_start(apply_button, False) # hbox.pack_start(reset_button, False) # hbox.pack_end(cancel_button, False) # #hbox.pack_end(help_button, False) # vbox.pack_start(hbox) # # window.add(vbox) # window.show_all() def focused_timezoom_direct(self, w, point_string): self.t.focus_start_point_string = point_string self.t.focus_stop_point_string = point_string self.t.action_required = True self.t.best_fit = True def graph_timezoom_popup(self, w): window = gtk.Window() window.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.log_colors.get_color())) window.set_border_width(5) window.set_title("Time Zoom") parent_window = self.xdot.widget.get_toplevel() if isinstance(parent_window, gtk.Window): window.set_transient_for(parent_window) window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) vbox = gtk.VBox() # TODO - error checking on date range given box = gtk.HBox() label = gtk.Label('Start CYCLE') box.pack_start(label, True) start_entry = gtk.Entry() start_entry.set_max_length(14) if self.t.oldest_point_string: start_entry.set_text(self.t.oldest_point_string) box.pack_start(start_entry, True) vbox.pack_start(box) box = gtk.HBox() label = gtk.Label('Stop CYCLE') box.pack_start(label, True) stop_entry = gtk.Entry() stop_entry.set_max_length(14) if self.t.newest_point_string: stop_entry.set_text(self.t.newest_point_string) box.pack_start(stop_entry, True) vbox.pack_start(box) cancel_button = gtk.Button("_Close") cancel_button.connect("clicked", lambda x: window.destroy()) reset_button = gtk.Button("_Reset (No Zoom)") reset_button.connect("clicked", self.focused_timezoom_direct, None) apply_button = gtk.Button("_Apply") apply_button.connect("clicked", self.graph_timezoom, start_entry, stop_entry) hbox = gtk.HBox() hbox.pack_start(apply_button, False) hbox.pack_start(reset_button, False) hbox.pack_end(cancel_button, False) vbox.pack_start(hbox) window.add(vbox) window.show_all() def graph_timezoom(self, w, start_e, stop_e): self.t.focus_start_point_string = start_e.get_text() self.t.focus_stop_point_string = stop_e.get_text() self.t.best_fit = True self.t.action_required = True
class ControlGraph(object): """ Dependency graph suite control interface. """ def __init__(self, cfg, updater, theme, dot_size, info_bar, get_right_click_menu, log_colors, insert_task_popup): # NOTE: this view has separate family Group and Ungroup buttons # instead of a single Group/Ungroup toggle button, unlike the # other views the graph view can display intermediate states # between fully grouped and fully ungrouped (well, so can the # tree view, but in that case the toggle button determines # whether it displays a flat list or a proper tree view). self.cfg = cfg self.updater = updater self.theme = theme self.info_bar = info_bar self.get_right_click_menu = get_right_click_menu self.log_colors = log_colors self.insert_task_popup = insert_task_popup self.gcapture_windows = [] self.xdot = xdot_widgets() self.xdot.widget.connect('clicked', self.on_url_clicked) self.xdot.widget.connect_after('motion-notify-event', self.on_motion_notify) self.last_url = None def get_control_widgets(self): self.t = GraphUpdater(self.cfg, self.updater, self.theme, self.info_bar, self.xdot) self.t.start() return self.xdot.get() def toggle_graph_disconnect(self, w, update_button): if w.get_active(): self.t.graph_disconnect = True self._set_tooltip(w, "Click to reconnect") update_button.set_sensitive(True) else: self.t.graph_disconnect = False self._set_tooltip(w, "Click to disconnect") update_button.set_sensitive(False) return True def graph_update(self, w): self.t.action_required = True def on_url_clicked(self, widget, url, event): if event.button != 3: return False m = re.match('base:(.*)', url) if m: # base graph node task_id = m.groups()[0] self.right_click_menu(event, task_id, type='base graph task') return self.right_click_menu(event, url, type='live task') def on_motion_notify(self, widget, event): """Add a new tooltip when the cursor moves in the graph.""" url = self.xdot.widget.get_url(event.x, event.y) if url == self.last_url: return False self.last_url = url if not hasattr(self.xdot.widget, "set_tooltip_text"): # Unfortunately, the older gtk.Tooltips doesn't work well here. # gtk.Widget.set_tooltip_text was introduced at PyGTK 2.12 return False if url is None: self.xdot.widget.set_tooltip_text(None) return False url = unicode(url.url) m = re.match('base:(.*)', url) if m: task_id = m.groups()[0] self.xdot.widget.set_tooltip_text(self.t.get_summary(task_id)) return False # URL is task ID self.xdot.widget.set_tooltip_text(self.t.get_summary(url)) return False def stop(self): self.t.quit = True def right_click_menu(self, event, task_id, type='live task'): name, point_string = TaskID.split(task_id) menu = gtk.Menu() menu_root = gtk.MenuItem(task_id) menu_root.set_submenu(menu) timezoom_item_direct = gtk.MenuItem('Focus on ' + point_string) timezoom_item_direct.connect( 'activate', self.focused_timezoom_direct, point_string) # TODO - pre cylc-6 could focus on a range of points (was hours-based). # timezoom_item = gtk.MenuItem('Focus on Range') # timezoom_item.connect( # 'activate', self.focused_timezoom_popup, task_id) timezoom_reset_item = gtk.MenuItem('Focus Reset') timezoom_reset_item.connect('activate', self.focused_timezoom_direct, None) group_item = gtk.ImageMenuItem('Group') img = gtk.image_new_from_stock('group', gtk.ICON_SIZE_MENU) group_item.set_image(img) group_item.set_sensitive(not self.t.have_leaves_and_feet or name not in self.t.feet) group_item.connect('activate', self.grouping, name, True) ungroup_item = gtk.ImageMenuItem('UnGroup') img = gtk.image_new_from_stock('ungroup', gtk.ICON_SIZE_MENU) ungroup_item.set_image(img) ungroup_item.set_sensitive(not self.t.have_leaves_and_feet or name not in self.t.leaves) ungroup_item.connect('activate', self.grouping, name, False) ungroup_rec_item = gtk.ImageMenuItem('Recursive UnGroup') img = gtk.image_new_from_stock('ungroup', gtk.ICON_SIZE_MENU) ungroup_rec_item.set_image(img) ungroup_rec_item.set_sensitive(not self.t.have_leaves_and_feet or name not in self.t.leaves) ungroup_rec_item.connect('activate', self.grouping, name, False, True) menu.append(gtk.SeparatorMenuItem()) if type is not 'live task': insert_item = gtk.ImageMenuItem('Insert ...') img = gtk.image_new_from_stock(gtk.STOCK_DIALOG_INFO, gtk.ICON_SIZE_MENU) insert_item.set_image(img) menu.append(insert_item) insert_item.connect( 'button-press-event', lambda *a: self.insert_task_popup( is_fam=(name in self.t.descendants), name=name, point_string=point_string ) ) menu.append(gtk.SeparatorMenuItem()) menu.append(timezoom_item_direct) menu.append(timezoom_reset_item) menu.append(gtk.SeparatorMenuItem()) menu.append(group_item) menu.append(ungroup_item) menu.append(ungroup_rec_item) if type == 'live task': is_fam = (name in self.t.descendants) if is_fam: if task_id not in self.t.fam_state_summary: return False t_state = self.t.fam_state_summary[task_id]['state'] submit_num = None else: if task_id not in self.t.state_summary: return False t_state = self.t.state_summary[task_id]['state'] submit_num = self.t.state_summary[task_id]['submit_num'] default_menu = self.get_right_click_menu( task_id, t_state, task_is_family=is_fam, submit_num=submit_num) dm_kids = default_menu.get_children() for item in reversed(dm_kids[:2]): # Put task name and URL at the top. default_menu.remove(item) menu.prepend(item) for item in dm_kids[2:]: # And the rest of the default menu at the bottom. default_menu.remove(item) menu.append(item) menu.show_all() menu.popup(None, None, None, event.button, event.time) # TODO - popup menus are not automatically destroyed and can be # reused if saved; however, we need to reconstruct or at least # alter ours dynamically => should destroy after each use to # prevent a memory leak? But I'm not sure how to do this as yet.) return True def grouping(self, w, name, group, rec=False): self.t.ungroup_recursive = rec self.t.group_all = False self.t.ungroup_all = False if group: self.t.group = [name] self.t.ungroup = [] else: self.t.ungroup = [name] self.t.group = [] self.t.action_required = True self.t.best_fit = True def rearrange(self, col, n): cols = self.ttreeview.get_columns() for i_n in range(len(cols)): if i_n == n: cols[i_n].set_sort_indicator(True) else: cols[i_n].set_sort_indicator(False) # col is cols[n] if col.get_sort_order() == gtk.SORT_ASCENDING: col.set_sort_order(gtk.SORT_DESCENDING) else: col.set_sort_order(gtk.SORT_ASCENDING) self.ttreestore.set_sort_column_id(n, col.get_sort_order()) def refresh(self): self.t.update() self.t.best_fit = True self.t.action_required = True def get_menuitems(self): """Return the menu items specific to this view.""" items = [] graph_range_item = gtk.MenuItem('Time Range Focus ...') items.append(graph_range_item) graph_range_item.connect('activate', self.graph_timezoom_popup) crop_item = gtk.CheckMenuItem('Toggle _Crop Base Graph') items.append(crop_item) crop_item.set_active(self.t.crop) crop_item.connect('activate', self.toggle_crop) self.menu_group_item = gtk.ImageMenuItem('_Group All Families') img = gtk.image_new_from_stock('group', gtk.ICON_SIZE_MENU) self.menu_group_item.set_image(img) items.append(self.menu_group_item) self.menu_group_item.connect('activate', self.group_all, True) self.menu_ungroup_item = gtk.ImageMenuItem('_UnGroup All Families') img = gtk.image_new_from_stock('ungroup', gtk.ICON_SIZE_MENU) self.menu_ungroup_item.set_image(img) items.append(self.menu_ungroup_item) self.menu_ungroup_item.connect('activate', self.group_all, False) menu_left_to_right_item = gtk.CheckMenuItem( '_Transpose Graph') items.append(menu_left_to_right_item) menu_left_to_right_item.set_active(self.t.orientation == "LR") menu_left_to_right_item.connect('activate', self.toggle_left_to_right_mode) self.menu_subgraphs_item = gtk.CheckMenuItem( '_Organise by Cycle Point') items.append(self.menu_subgraphs_item) self.menu_subgraphs_item.set_active(self.t.subgraphs_on) self.menu_subgraphs_item.connect('activate', self.toggle_cycle_point_subgraphs) igsui_item = gtk.CheckMenuItem('_Ignore Suicide Triggers') items.append(igsui_item) igsui_item.set_active(self.t.ignore_suicide) igsui_item.connect('activate', self.toggle_ignore_suicide_triggers) self.menu_frames_item = gtk.CheckMenuItem('_Write Graph Frames') items.append(self.menu_frames_item) self.menu_frames_item.set_active(self.t.write_dot_frames) self.menu_frames_item.connect( 'activate', self.toggle_write_dot_frames) return items def _set_tooltip(self, widget, tip_text): tip = gtk.Tooltips() tip.enable() tip.set_tip(widget, tip_text) def get_toolitems(self): """Return the tool bar items specific to this view.""" items = [] for child in self.xdot.vbox.get_children(): if isinstance(child, gtk.HButtonBox): self.xdot.vbox.remove(child) self.group_toolbutton = gtk.ToolButton() g_image = gtk.image_new_from_stock('group', gtk.ICON_SIZE_SMALL_TOOLBAR) self.group_toolbutton.set_icon_widget(g_image) self.group_toolbutton.set_label("Group") self.group_toolbutton.connect('clicked', self.group_all, True) self._set_tooltip(self.group_toolbutton, "Graph View - Click to group all task families") items.append(self.group_toolbutton) self.ungroup_toolbutton = gtk.ToolButton() g_image = gtk.image_new_from_stock('ungroup', gtk.ICON_SIZE_SMALL_TOOLBAR) self.ungroup_toolbutton.set_icon_widget(g_image) self.ungroup_toolbutton.set_label("Ungroup") self.ungroup_toolbutton.connect('clicked', self.group_all, False) self._set_tooltip(self.ungroup_toolbutton, "Graph View - Click to ungroup all task families") items.append(self.ungroup_toolbutton) self.transpose_toolbutton = gtk.ToggleToolButton() self.transpose_toolbutton.set_active(False) g_image = gtk.image_new_from_stock('transpose', gtk.ICON_SIZE_SMALL_TOOLBAR) self.transpose_toolbutton.set_icon_widget(g_image) self.transpose_toolbutton.set_label("Transpose") self.transpose_toolbutton.connect('clicked', self.toggle_left_to_right_mode) self._set_tooltip(self.transpose_toolbutton, "Graph View - Click to transpose graph") items.append(self.transpose_toolbutton) self.subgraphs_button = gtk.ToggleToolButton() image = gtk.image_new_from_stock(gtk.STOCK_LEAVE_FULLSCREEN, gtk.ICON_SIZE_SMALL_TOOLBAR) self.subgraphs_button.set_icon_widget(image) self.subgraphs_button.set_label("Subgraphs") self.subgraphs_button.connect( 'clicked', self.toggle_cycle_point_subgraphs) self._set_tooltip(self.subgraphs_button, "Graph View - Click to organise by cycle point") items.append(self.subgraphs_button) zoomin_button = gtk.ToolButton(gtk.STOCK_ZOOM_IN) zoomin_button.connect('clicked', self.xdot.widget.on_zoom_in) zoomin_button.set_label(None) self._set_tooltip(zoomin_button, "Graph View - Zoom In") items.append(zoomin_button) zoomout_button = gtk.ToolButton(gtk.STOCK_ZOOM_OUT) zoomout_button.connect('clicked', self.xdot.widget.on_zoom_out) zoomout_button.set_label(None) self._set_tooltip(zoomout_button, "Graph View - Zoom Out") items.append(zoomout_button) zoomfit_button = gtk.ToolButton(gtk.STOCK_ZOOM_FIT) zoomfit_button.connect('clicked', self.xdot.widget.on_zoom_fit) zoomfit_button.set_label(None) self._set_tooltip(zoomfit_button, "Graph View - Best Fit") items.append(zoomfit_button) zoom100_button = gtk.ToolButton(gtk.STOCK_ZOOM_100) zoom100_button.connect('clicked', self.xdot.widget.on_zoom_100) zoom100_button.set_label(None) self._set_tooltip(zoom100_button, "Graph View - Normal Size") items.append(zoom100_button) connect_button = gtk.ToggleToolButton() image = gtk.image_new_from_stock(gtk.STOCK_DISCONNECT, gtk.ICON_SIZE_SMALL_TOOLBAR) connect_button.set_icon_widget(image) connect_button.set_label("Disconnect") self._set_tooltip(connect_button, "Graph View - Click to disconnect") items.append(connect_button) update_button = gtk.ToolButton(gtk.STOCK_REFRESH) update_button.connect('clicked', self.graph_update) update_button.set_label(None) update_button.set_sensitive(False) self._set_tooltip(update_button, "Graph View - Update graph") items.append(update_button) connect_button.connect('clicked', self.toggle_graph_disconnect, update_button) return items def group_all(self, w, group): if group: self.t.group_all = True self.t.ungroup_all = False if "graph" in self.cfg.ungrouped_views: self.cfg.ungrouped_views.remove("graph") else: self.t.ungroup_all = True self.t.group_all = False if "graph" not in self.cfg.ungrouped_views: self.cfg.ungrouped_views.append("graph") self.t.action_required = True self.t.best_fit = True def toggle_crop(self, w): self.t.crop = not self.t.crop self.t.action_required = True def toggle_write_dot_frames(self, w): self.t.toggle_write_dot_frames() def toggle_cycle_point_subgraphs(self, toggle_item): subgraphs_on = toggle_item.get_active() if subgraphs_on == self.t.subgraphs_on: return self.t.subgraphs_on = subgraphs_on if isinstance(toggle_item, gtk.ToggleToolButton): self.menu_subgraphs_item.set_active(self.t.subgraphs_on) else: self.subgraphs_button.set_active(self.t.subgraphs_on) self.t.action_required = True def toggle_left_to_right_mode(self, w): """Change the graph to left-to-right or top-to-bottom.""" if self.t.orientation == "TB": # Top -> bottom ordering self.t.orientation = "LR" # Left -> right ordering elif self.t.orientation == "LR": self.t.orientation = "TB" self.t.best_fit = True self.t.action_required = True def toggle_ignore_suicide_triggers(self, w): self.t.ignore_suicide = not self.t.ignore_suicide self.t.action_required = True # def focused_timezoom_popup(self, w, id): # window = gtk.Window() # window.modify_bg(gtk.STATE_NORMAL, # gtk.gdk.color_parse(self.log_colors.get_color())) # window.set_border_width(5) # window.set_title("Cycle Point Zoom") # parent_window = self.xdot.widget.get_toplevel() # if isinstance(parent_window, gtk.Window): # window.set_transient_for(parent_window) # window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) # vbox = gtk.VBox() # # name, point_string = TaskID.split(id) # # TODO - do we need to check that oldest_point_string is defined yet? # # #diff_pre = ctime - self.t.oldest_ctime.hours # #diff_post = self.t.newest_ctime - ctime.hours # # ... # #start_entry.set_text(str(diff_pre)) # #stop_entry.set_text(str(diff_post)) # # # TODO - error checking on date range given # box = gtk.HBox() # label = gtk.Label('Pre (hours)') # box.pack_start(label, True) # start_entry = gtk.Entry() # box.pack_start (start_entry, True) # vbox.pack_start(box) # # box = gtk.HBox() # label = gtk.Label('Post (hours)') # box.pack_start(label, True) # stop_entry = gtk.Entry() # box.pack_start (stop_entry, True) # vbox.pack_start(box) # # cancel_button = gtk.Button("_Close") # cancel_button.connect("clicked", lambda x: window.destroy()) # # reset_button = gtk.Button("_Reset (No Zoom)") # reset_button.connect("clicked", self.focused_timezoom_direct, None) # # apply_button = gtk.Button("_Apply") # apply_button.connect("clicked", self.focused_timezoom, # point_string, start_entry, stop_entry) # # hbox = gtk.HBox() # hbox.pack_start(apply_button, False) # hbox.pack_start(reset_button, False) # hbox.pack_end(cancel_button, False) # #hbox.pack_end(help_button, False) # vbox.pack_start(hbox) # # window.add(vbox) # window.show_all() def focused_timezoom_direct(self, w, point_string): self.t.focus_start_point_string = point_string self.t.focus_stop_point_string = point_string self.t.action_required = True self.t.best_fit = True def graph_timezoom_popup(self, w): window = gtk.Window() window.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.log_colors.get_color())) window.set_border_width(5) window.set_title("Time Zoom") parent_window = self.xdot.widget.get_toplevel() if isinstance(parent_window, gtk.Window): window.set_transient_for(parent_window) window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) vbox = gtk.VBox() # TODO - error checking on date range given box = gtk.HBox() label = gtk.Label('Start CYCLE') box.pack_start(label, True) start_entry = gtk.Entry() start_entry.set_max_length(14) if self.t.oldest_point_string: start_entry.set_text(self.t.oldest_point_string) box.pack_start(start_entry, True) vbox.pack_start(box) box = gtk.HBox() label = gtk.Label('Stop CYCLE') box.pack_start(label, True) stop_entry = gtk.Entry() stop_entry.set_max_length(14) if self.t.newest_point_string: stop_entry.set_text(self.t.newest_point_string) box.pack_start(stop_entry, True) vbox.pack_start(box) cancel_button = gtk.Button("_Close") cancel_button.connect("clicked", lambda x: window.destroy()) reset_button = gtk.Button("_Reset (No Zoom)") reset_button.connect("clicked", self.focused_timezoom_direct, None) apply_button = gtk.Button("_Apply") apply_button.connect("clicked", self.graph_timezoom, start_entry, stop_entry) hbox = gtk.HBox() hbox.pack_start(apply_button, False) hbox.pack_start(reset_button, False) hbox.pack_end(cancel_button, False) vbox.pack_start(hbox) window.add(vbox) window.show_all() def graph_timezoom(self, w, start_e, stop_e): self.t.focus_start_point_string = start_e.get_text() self.t.focus_stop_point_string = stop_e.get_text() self.t.best_fit = True self.t.action_required = True