OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/python |
| 2 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. |
| 5 |
| 6 """Helper functions for building graphs with dot.""" |
| 7 |
| 8 import subprocess |
| 9 |
| 10 class Subgraph(object): |
| 11 """A subgraph in dot. Contains nodes, arcs, and other subgraphs.""" |
| 12 |
| 13 _valid_ranks = set(['source', 'sink', 'same', 'min', 'max', None]) |
| 14 |
| 15 def __init__(self, rank=None): |
| 16 self.SetRank(rank) |
| 17 self._nodes = [] |
| 18 self._subgraphs = [] |
| 19 self._arcs = set() |
| 20 |
| 21 def SetRank(self, rank): |
| 22 """Sets the rank for the nodes in the graph. |
| 23 |
| 24 Can be one of 'source', 'sink', 'same', 'min', 'max' or None. See dot |
| 25 documentation at http://www.graphviz.org/Documentation.php for exact |
| 26 semantics.""" |
| 27 assert(rank in self._valid_ranks) |
| 28 self._rank = rank |
| 29 |
| 30 def AddNode(self, id, name=None, color=None, href=None): |
| 31 """Adds a node to the subgraph.""" |
| 32 tags = {} |
| 33 if name: |
| 34 tags['label'] = name |
| 35 if color: |
| 36 tags['color'] = color |
| 37 tags['fontcolor'] = color |
| 38 if href: |
| 39 tags['href'] = href |
| 40 self._nodes.append({'id': id, 'tags': tags}) |
| 41 |
| 42 def AddSubgraph(self, subgraph): |
| 43 """Adds a subgraph to the subgraph.""" |
| 44 self._subgraphs.append(subgraph) |
| 45 |
| 46 def AddNewSubgraph(self, rank=None): |
| 47 """Adds a new subgraph to the subgraph. The new subgraph is returned.""" |
| 48 subgraph = Subgraph(rank) |
| 49 self.AddSubgraph(subgraph) |
| 50 return subgraph |
| 51 |
| 52 def AddArc(self, node_from, node_to): |
| 53 """Adds an arc between two nodes.""" |
| 54 self._arcs.add((node_from, node_to)) |
| 55 |
| 56 def _GenNodes(self): |
| 57 """Generates the code for all the nodes.""" |
| 58 lines = [] |
| 59 for node in self._nodes: |
| 60 tags = ['%s="%s"' % (k, v) for (k, v) in node['tags'].iteritems()] |
| 61 lines.append('"%s" [%s];' % (node['id'], ', '.join(tags))) |
| 62 return lines |
| 63 |
| 64 def _GenSubgraphs(self): |
| 65 """Generates the code for all the subgraphs contained in this subgraph.""" |
| 66 lines = [] |
| 67 for subgraph in self._subgraphs: |
| 68 lines += subgraph.Gen(); |
| 69 return lines |
| 70 |
| 71 def _GenArcs(self): |
| 72 """Generates the code for all the arcs.""" |
| 73 lines = [] |
| 74 for node_from, node_to in self._arcs: |
| 75 lines.append('"%s" -> "%s";' % (node_from, node_to)) |
| 76 return lines |
| 77 |
| 78 def _GenInner(self): |
| 79 """Generates the code for the inner contents of the subgraph.""" |
| 80 lines = [] |
| 81 if self._rank: |
| 82 lines.append('rank=%s;' % self._rank) |
| 83 lines += self._GenSubgraphs() |
| 84 lines += self._GenNodes() |
| 85 lines += self._GenArcs() |
| 86 return lines |
| 87 |
| 88 def Gen(self): |
| 89 """Generates the code for the subgraph.""" |
| 90 return ['subgraph {'] + self._GenInner() + ['}'] |
| 91 |
| 92 |
| 93 class Graph(Subgraph): |
| 94 """A top-level graph in dot. It's basically a subgraph with a name.""" |
| 95 |
| 96 def __init__(self, name): |
| 97 Subgraph.__init__(self) |
| 98 self._name = name |
| 99 |
| 100 def Gen(self): |
| 101 """Generates the code for the graph.""" |
| 102 return ['digraph "%s" {' % self._name, |
| 103 'graph [name="%s"];' % self._name] + self._GenInner() + ['}'] |
| 104 |
| 105 |
| 106 def GenerateImage(lines, filename, format='svg', save_dot_filename=None): |
| 107 """Generates the image by calling dot on the input lines.""" |
| 108 data = '\n'.join(lines) |
| 109 proc = subprocess.Popen(['dot', '-T' + format, '-o' + filename], |
| 110 stdin=subprocess.PIPE) |
| 111 proc.communicate(data) |
| 112 |
| 113 if save_dot_filename: |
| 114 file = open(save_dot_filename, 'w') |
| 115 file.write(data) |
| 116 file.close() |
OLD | NEW |