예제 #1
0
    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())
예제 #2
0
 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
예제 #3
0
    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())
예제 #4
0
    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
예제 #5
0
 def test_gtk_rgb_to_hex(self):
     self.assertEqual(gtk_rgb_to_hex(fake_gtk_color()), '#ffffff')