def get_graph(self, group_nodes=None, ungroup_nodes=None, ungroup_recursive=False, ungroup_all=False, group_all=False): if not self.suiterc: return family_nodes = self.suiterc.get_first_parent_descendants() # Note this is used by "cylc graph" but not gcylc. # self.start_ and self.stop_point_string come from CLI. bg_color = gtk_rgb_to_hex( getattr(self.style, 'bg', None)[gtk.STATE_NORMAL]) fg_color = gtk_rgb_to_hex( getattr(self.style, 'fg', None)[gtk.STATE_NORMAL]) graph = CGraph.get_graph(self.suiterc, group_nodes=group_nodes, ungroup_nodes=ungroup_nodes, ungroup_recursive=ungroup_recursive, group_all=group_all, ungroup_all=ungroup_all, ignore_suicide=self.ignore_suicide, subgraphs_on=self.subgraphs_on, bgcolor=bg_color, fgcolor=fg_color) graph.graph_attr['rankdir'] = self.orientation # Style nodes. cache = {} # For caching is_on_sequence() calls. fg_ghost = "%s%s" % (fg_color, GHOST_TRANSP_HEX) for node in graph.iternodes(): name, point = TaskID.split(node.get_name()) if name.startswith('@'): # Style action trigger nodes. node.attr['shape'] = 'none' elif name in family_nodes: # Style family nodes. node.attr['shape'] = 'doubleoctagon' # Detecting ghost families would involve analysing triggers # in the suite's graphing. elif self.is_off_sequence(name, point, cache=cache): node.attr['style'] = 'dotted' node.attr['color'] = fg_ghost node.attr['fontcolor'] = fg_ghost self.graph = graph self.filter_graph() self.set_dotcode(graph.string())
def update_xdot(self, no_zoom=False): self.graphw.set_def_style( gtk_rgb_to_hex( getattr(self.xdot.widget.style, 'fg', None)[gtk.STATE_NORMAL]), gtk_rgb_to_hex( getattr(self.xdot.widget.style, 'bg', None)[gtk.STATE_NORMAL])) self.xdot.set_dotcode(self.graphw.to_string(), no_zoom=no_zoom) if self.first_update: self.xdot.widget.zoom_to_fit() self.first_update = False elif self.best_fit: self.xdot.widget.zoom_to_fit() self.best_fit = False elif self.normal_fit: self.xdot.widget.zoom_image(1.0, center=True) self.normal_fit = False
def get_graph(self): title = self.suite + ': runtime inheritance graph' graph = CGraphPlain(title) graph.set_def_style( gtk_rgb_to_hex(getattr(self.style, 'fg', None)[gtk.STATE_NORMAL]), gtk_rgb_to_hex(getattr(self.style, 'bg', None)[gtk.STATE_NORMAL])) graph.graph_attr['rankdir'] = self.orientation for ns in self.inherit: for p in self.inherit[ns]: graph.add_edge(p, ns) graph.get_node(p).attr['shape'] = 'box' graph.get_node(ns).attr['shape'] = 'box' self.graph = graph self.filter_graph() self.set_dotcode(graph.string())
def update_gui(self): # TODO - check edges against resolved ones # (adding new ones, and nodes, if necessary) self.action_required = False if not self.global_summary: return self.oldest_point_string = ( self.global_summary['oldest cycle point string']) self.newest_point_string = ( self.global_summary['newest cycle point string']) if TASK_STATUS_RUNAHEAD not in self.updater.filter_states_excl: # Get a graph out to the max runahead point. self.newest_point_string = ( self.global_summary['newest runahead cycle point string']) if self.focus_start_point_string: oldest = self.focus_start_point_string newest = self.focus_stop_point_string else: oldest = self.oldest_point_string newest = self.newest_point_string group_for_server = self.group if self.group == []: group_for_server = None ungroup_for_server = self.ungroup if self.ungroup == []: ungroup_for_server = None try: res = self.updater.client.get_info( 'get_graph_raw', start_point_string=oldest, stop_point_string=newest, group_nodes=group_for_server, ungroup_nodes=ungroup_for_server, ungroup_recursive=self.ungroup_recursive, group_all=self.group_all, ungroup_all=self.ungroup_all) except ClientError: if cylc.flags.debug: try: traceback.print_exc() except IOError: pass # Cannot print to terminal (session may be closed). return False self.have_leaves_and_feet = True gr_edges, suite_polling_tasks, self.leaves, self.feet = res gr_edges = [tuple(edge) for edge in gr_edges] fgcolor = gtk_rgb_to_hex( getattr(self.xdot.widget.style, 'fg', None)[gtk.STATE_NORMAL]) current_id = self.get_graph_id(gr_edges) if current_id != self.prev_graph_id: self.graphw = CGraphPlain(self.cfg.suite, suite_polling_tasks) self.graphw.add_edges(gr_edges, ignore_suicide=self.ignore_suicide) nodes_to_remove = set() # Remove nodes representing filtered-out tasks. if (self.updater.filter_name_string or self.updater.filter_states_excl): for node in self.graphw.nodes(): id_ = node.get_name() # Don't need to guard against special nodes here (yet). name, point_string = TaskID.split(id_) if name not in self.all_families: # This node is a task, not a family. if id_ in self.updater.filt_task_ids: nodes_to_remove.add(node) elif id_ not in self.updater.kept_task_ids: # A base node - these only appear in the graph. filter_string = self.updater.filter_name_string if (filter_string and filter_string not in name and not re.search(filter_string, name)): # A base node that fails the name filter. nodes_to_remove.add(node) elif id_ in self.fam_state_summary: # Remove family nodes if all members filtered out. remove = True for mem in self.descendants[name]: mem_id = TaskID.get(mem, point_string) if mem_id in self.updater.kept_task_ids: remove = False break if remove: nodes_to_remove.add(node) elif id_ in self.updater.full_fam_state_summary: # An updater-filtered-out family. nodes_to_remove.add(node) # Base node cropping. if self.crop: # Remove all base nodes. for node in (set(self.graphw.nodes()) - nodes_to_remove): if node.get_name() not in self.state_summary: nodes_to_remove.add(node) else: # Remove cycle points containing only base nodes. non_base_point_strings = set() point_string_nodes = {} for node in set(self.graphw.nodes()) - nodes_to_remove: node_id = node.get_name() name, point_string = TaskID.split(node_id) point_string_nodes.setdefault(point_string, []) point_string_nodes[point_string].append(node) if (node_id in self.state_summary or node_id in self.fam_state_summary): non_base_point_strings.add(point_string) pure_base_point_strings = (set(point_string_nodes) - non_base_point_strings) for point_string in pure_base_point_strings: for node in point_string_nodes[point_string]: nodes_to_remove.add(node) self.graphw.cylc_remove_nodes_from(list(nodes_to_remove)) # TODO - remove base nodes only connected to other base nodes? # Should these even exist any more? # Make family nodes octagons. for node in self.graphw.nodes(): node_id = node.get_name() try: name, point_string = TaskID.split(node_id) except ValueError: # Special node. continue if name in self.all_families: node.attr['shape'] = 'doubleoctagon' elif name.startswith('@'): node.attr['shape'] = 'none' if self.subgraphs_on: self.graphw.add_cycle_point_subgraphs(gr_edges, fgcolor) # Set base node style defaults fg_ghost = "%s%s" % (fgcolor, GHOST_TRANSP_HEX) for node in self.graphw.nodes(): node.attr['style'] = 'dotted' node.attr['color'] = fg_ghost node.attr['fontcolor'] = fg_ghost if not node.attr['URL'].startswith(self.PREFIX_BASE): node.attr['URL'] = self.PREFIX_BASE + node.attr['URL'] for id_ in self.state_summary: try: node = self.graphw.get_node(id_) except KeyError: continue self.set_live_node_attr(node, id_) for id_ in self.fam_state_summary: try: node = self.graphw.get_node(id_) except KeyError: # Node not in graph. continue self.set_live_node_attr(node, id_) self.graphw.graph_attr['rankdir'] = self.orientation if self.write_dot_frames: arg = os.path.join( self.suite_share_dir, 'frame' + '-' + str(self.graph_frame_count) + '.dot') self.graphw.write(arg) self.graph_frame_count += 1 self.update_xdot(no_zoom=(current_id == self.prev_graph_id)) self.prev_graph_id = current_id
def test_gtk_rgb_to_hex(self): self.assertEqual(gtk_rgb_to_hex(fake_gtk_color()), '#ffffff')