1cef7893435aa41160dd1255c43cb8498279738ccChris Craik'''
2cef7893435aa41160dd1255c43cb8498279738ccChris Craikaltgraph.Dot - Interface to the dot language
3cef7893435aa41160dd1255c43cb8498279738ccChris Craik============================================
4cef7893435aa41160dd1255c43cb8498279738ccChris Craik
5cef7893435aa41160dd1255c43cb8498279738ccChris CraikThe :py:mod:`~altgraph.Dot` module provides a simple interface to the
6cef7893435aa41160dd1255c43cb8498279738ccChris Craikfile format used in the `graphviz <http://www.research.att.com/sw/tools/graphviz/>`_
7cef7893435aa41160dd1255c43cb8498279738ccChris Craikprogram. The module is intended to offload the most tedious part of the process
8cef7893435aa41160dd1255c43cb8498279738ccChris Craik(the **dot** file generation) while transparently exposing most of its features.
9cef7893435aa41160dd1255c43cb8498279738ccChris Craik
10cef7893435aa41160dd1255c43cb8498279738ccChris CraikTo display the graphs or to generate image files the `graphviz <http://www.research.att.com/sw/tools/graphviz/>`_
11cef7893435aa41160dd1255c43cb8498279738ccChris Craikpackage needs to be installed on the system, moreover the :command:`dot` and :command:`dotty` programs must
12cef7893435aa41160dd1255c43cb8498279738ccChris Craikbe accesible in the program path so that they can be ran from processes spawned
13cef7893435aa41160dd1255c43cb8498279738ccChris Craikwithin the module.
14cef7893435aa41160dd1255c43cb8498279738ccChris Craik
15cef7893435aa41160dd1255c43cb8498279738ccChris CraikExample usage
16cef7893435aa41160dd1255c43cb8498279738ccChris Craik-------------
17cef7893435aa41160dd1255c43cb8498279738ccChris Craik
18cef7893435aa41160dd1255c43cb8498279738ccChris CraikHere is a typical usage::
19cef7893435aa41160dd1255c43cb8498279738ccChris Craik
20cef7893435aa41160dd1255c43cb8498279738ccChris Craik    from altgraph import Graph, Dot
21cef7893435aa41160dd1255c43cb8498279738ccChris Craik
22cef7893435aa41160dd1255c43cb8498279738ccChris Craik    # create a graph
23cef7893435aa41160dd1255c43cb8498279738ccChris Craik    edges = [ (1,2), (1,3), (3,4), (3,5), (4,5), (5,4) ]
24cef7893435aa41160dd1255c43cb8498279738ccChris Craik    graph = Graph.Graph(edges)
25cef7893435aa41160dd1255c43cb8498279738ccChris Craik
26cef7893435aa41160dd1255c43cb8498279738ccChris Craik    # create a dot representation of the graph
27cef7893435aa41160dd1255c43cb8498279738ccChris Craik    dot = Dot.Dot(graph)
28cef7893435aa41160dd1255c43cb8498279738ccChris Craik
29cef7893435aa41160dd1255c43cb8498279738ccChris Craik    # display the graph
30cef7893435aa41160dd1255c43cb8498279738ccChris Craik    dot.display()
31cef7893435aa41160dd1255c43cb8498279738ccChris Craik
32cef7893435aa41160dd1255c43cb8498279738ccChris Craik    # save the dot representation into the mydot.dot file
33cef7893435aa41160dd1255c43cb8498279738ccChris Craik    dot.save_dot(file_name='mydot.dot')
34cef7893435aa41160dd1255c43cb8498279738ccChris Craik
35cef7893435aa41160dd1255c43cb8498279738ccChris Craik    # save dot file as gif image into the graph.gif file
36cef7893435aa41160dd1255c43cb8498279738ccChris Craik    dot.save_img(file_name='graph', file_type='gif')
37cef7893435aa41160dd1255c43cb8498279738ccChris Craik
38cef7893435aa41160dd1255c43cb8498279738ccChris CraikDirected graph and non-directed graph
39cef7893435aa41160dd1255c43cb8498279738ccChris Craik-------------------------------------
40cef7893435aa41160dd1255c43cb8498279738ccChris Craik
41cef7893435aa41160dd1255c43cb8498279738ccChris CraikDot class can use for both directed graph and non-directed graph
42cef7893435aa41160dd1255c43cb8498279738ccChris Craikby passing ``graphtype`` parameter.
43cef7893435aa41160dd1255c43cb8498279738ccChris Craik
44cef7893435aa41160dd1255c43cb8498279738ccChris CraikExample::
45cef7893435aa41160dd1255c43cb8498279738ccChris Craik
46cef7893435aa41160dd1255c43cb8498279738ccChris Craik    # create directed graph(default)
47cef7893435aa41160dd1255c43cb8498279738ccChris Craik    dot = Dot.Dot(graph, graphtype="digraph")
48cef7893435aa41160dd1255c43cb8498279738ccChris Craik
49cef7893435aa41160dd1255c43cb8498279738ccChris Craik    # create non-directed graph
50cef7893435aa41160dd1255c43cb8498279738ccChris Craik    dot = Dot.Dot(graph, graphtype="graph")
51cef7893435aa41160dd1255c43cb8498279738ccChris Craik
52cef7893435aa41160dd1255c43cb8498279738ccChris CraikCustomizing the output
53cef7893435aa41160dd1255c43cb8498279738ccChris Craik----------------------
54cef7893435aa41160dd1255c43cb8498279738ccChris Craik
55cef7893435aa41160dd1255c43cb8498279738ccChris CraikThe graph drawing process may be customized by passing
56cef7893435aa41160dd1255c43cb8498279738ccChris Craikvalid :command:`dot` parameters for the nodes and edges. For a list of all
57cef7893435aa41160dd1255c43cb8498279738ccChris Craikparameters see the `graphviz <http://www.research.att.com/sw/tools/graphviz/>`_
58cef7893435aa41160dd1255c43cb8498279738ccChris Craikdocumentation.
59cef7893435aa41160dd1255c43cb8498279738ccChris Craik
60cef7893435aa41160dd1255c43cb8498279738ccChris CraikExample::
61cef7893435aa41160dd1255c43cb8498279738ccChris Craik
62cef7893435aa41160dd1255c43cb8498279738ccChris Craik    # customizing the way the overall graph is drawn
63cef7893435aa41160dd1255c43cb8498279738ccChris Craik    dot.style(size='10,10', rankdir='RL', page='5, 5' , ranksep=0.75)
64cef7893435aa41160dd1255c43cb8498279738ccChris Craik
65cef7893435aa41160dd1255c43cb8498279738ccChris Craik    # customizing node drawing
66cef7893435aa41160dd1255c43cb8498279738ccChris Craik    dot.node_style(1, label='BASE_NODE',shape='box', color='blue' )
67cef7893435aa41160dd1255c43cb8498279738ccChris Craik    dot.node_style(2, style='filled', fillcolor='red')
68cef7893435aa41160dd1255c43cb8498279738ccChris Craik
69cef7893435aa41160dd1255c43cb8498279738ccChris Craik    # customizing edge drawing
70cef7893435aa41160dd1255c43cb8498279738ccChris Craik    dot.edge_style(1, 2, style='dotted')
71cef7893435aa41160dd1255c43cb8498279738ccChris Craik    dot.edge_style(3, 5, arrowhead='dot', label='binds', labelangle='90')
72cef7893435aa41160dd1255c43cb8498279738ccChris Craik    dot.edge_style(4, 5, arrowsize=2, style='bold')
73cef7893435aa41160dd1255c43cb8498279738ccChris Craik
74cef7893435aa41160dd1255c43cb8498279738ccChris Craik
75cef7893435aa41160dd1255c43cb8498279738ccChris Craik.. note::
76cef7893435aa41160dd1255c43cb8498279738ccChris Craik
77cef7893435aa41160dd1255c43cb8498279738ccChris Craik   dotty (invoked via :py:func:`~altgraph.Dot.display`) may not be able to
78cef7893435aa41160dd1255c43cb8498279738ccChris Craik   display all graphics styles. To verify the output save it to an image file
79cef7893435aa41160dd1255c43cb8498279738ccChris Craik   and look at it that way.
80cef7893435aa41160dd1255c43cb8498279738ccChris Craik
81cef7893435aa41160dd1255c43cb8498279738ccChris CraikValid attributes
82cef7893435aa41160dd1255c43cb8498279738ccChris Craik----------------
83cef7893435aa41160dd1255c43cb8498279738ccChris Craik
84cef7893435aa41160dd1255c43cb8498279738ccChris Craik    - dot styles, passed via the :py:meth:`Dot.style` method::
85cef7893435aa41160dd1255c43cb8498279738ccChris Craik
86cef7893435aa41160dd1255c43cb8498279738ccChris Craik        rankdir = 'LR'   (draws the graph horizontally, left to right)
87cef7893435aa41160dd1255c43cb8498279738ccChris Craik        ranksep = number (rank separation in inches)
88cef7893435aa41160dd1255c43cb8498279738ccChris Craik
89cef7893435aa41160dd1255c43cb8498279738ccChris Craik    - node attributes, passed via the :py:meth:`Dot.node_style` method::
90cef7893435aa41160dd1255c43cb8498279738ccChris Craik
91cef7893435aa41160dd1255c43cb8498279738ccChris Craik        style = 'filled' | 'invisible' | 'diagonals' | 'rounded'
92cef7893435aa41160dd1255c43cb8498279738ccChris Craik        shape = 'box' | 'ellipse' | 'circle' | 'point' | 'triangle'
93cef7893435aa41160dd1255c43cb8498279738ccChris Craik
94cef7893435aa41160dd1255c43cb8498279738ccChris Craik    - edge attributes, passed via the :py:meth:`Dot.edge_style` method::
95cef7893435aa41160dd1255c43cb8498279738ccChris Craik
96cef7893435aa41160dd1255c43cb8498279738ccChris Craik        style     = 'dashed' | 'dotted' | 'solid' | 'invis' | 'bold'
97cef7893435aa41160dd1255c43cb8498279738ccChris Craik        arrowhead = 'box' | 'crow' | 'diamond' | 'dot' | 'inv' | 'none' | 'tee' | 'vee'
98cef7893435aa41160dd1255c43cb8498279738ccChris Craik        weight    = number (the larger the number the closer the nodes will be)
99cef7893435aa41160dd1255c43cb8498279738ccChris Craik
100cef7893435aa41160dd1255c43cb8498279738ccChris Craik    - valid `graphviz colors <http://www.research.att.com/~erg/graphviz/info/colors.html>`_
101cef7893435aa41160dd1255c43cb8498279738ccChris Craik
102cef7893435aa41160dd1255c43cb8498279738ccChris Craik    - for more details on how to control the graph drawing process see the
103cef7893435aa41160dd1255c43cb8498279738ccChris Craik      `graphviz reference <http://www.research.att.com/sw/tools/graphviz/refs.html>`_.
104cef7893435aa41160dd1255c43cb8498279738ccChris Craik'''
105cef7893435aa41160dd1255c43cb8498279738ccChris Craikimport os
106cef7893435aa41160dd1255c43cb8498279738ccChris Craikimport warnings
107cef7893435aa41160dd1255c43cb8498279738ccChris Craik
108cef7893435aa41160dd1255c43cb8498279738ccChris Craikfrom altgraph import GraphError
109cef7893435aa41160dd1255c43cb8498279738ccChris Craik
110cef7893435aa41160dd1255c43cb8498279738ccChris Craik
111cef7893435aa41160dd1255c43cb8498279738ccChris Craikclass Dot(object):
112cef7893435aa41160dd1255c43cb8498279738ccChris Craik    '''
113cef7893435aa41160dd1255c43cb8498279738ccChris Craik    A  class providing a **graphviz** (dot language) representation
114cef7893435aa41160dd1255c43cb8498279738ccChris Craik    allowing a fine grained control over how the graph is being
115cef7893435aa41160dd1255c43cb8498279738ccChris Craik    displayed.
116cef7893435aa41160dd1255c43cb8498279738ccChris Craik
117cef7893435aa41160dd1255c43cb8498279738ccChris Craik    If the :command:`dot` and :command:`dotty` programs are not in the current system path
118cef7893435aa41160dd1255c43cb8498279738ccChris Craik    their location needs to be specified in the contructor.
119cef7893435aa41160dd1255c43cb8498279738ccChris Craik    '''
120cef7893435aa41160dd1255c43cb8498279738ccChris Craik
121cef7893435aa41160dd1255c43cb8498279738ccChris Craik    def __init__(self, graph=None, nodes=None, edgefn=None, nodevisitor=None, edgevisitor=None, name="G", dot='dot', dotty='dotty', neato='neato', graphtype="digraph"):
122cef7893435aa41160dd1255c43cb8498279738ccChris Craik        '''
123cef7893435aa41160dd1255c43cb8498279738ccChris Craik        Initialization.
124cef7893435aa41160dd1255c43cb8498279738ccChris Craik        '''
125cef7893435aa41160dd1255c43cb8498279738ccChris Craik        self.name, self.attr = name, {}
126cef7893435aa41160dd1255c43cb8498279738ccChris Craik
127cef7893435aa41160dd1255c43cb8498279738ccChris Craik        assert graphtype in ['graph', 'digraph']
128cef7893435aa41160dd1255c43cb8498279738ccChris Craik        self.type = graphtype
129cef7893435aa41160dd1255c43cb8498279738ccChris Craik
130cef7893435aa41160dd1255c43cb8498279738ccChris Craik        self.temp_dot = "tmp_dot.dot"
131cef7893435aa41160dd1255c43cb8498279738ccChris Craik        self.temp_neo = "tmp_neo.dot"
132cef7893435aa41160dd1255c43cb8498279738ccChris Craik
133cef7893435aa41160dd1255c43cb8498279738ccChris Craik        self.dot, self.dotty, self.neato = dot, dotty, neato
134cef7893435aa41160dd1255c43cb8498279738ccChris Craik
135cef7893435aa41160dd1255c43cb8498279738ccChris Craik        # self.nodes: node styles
136cef7893435aa41160dd1255c43cb8498279738ccChris Craik        # self.edges: edge styles
137cef7893435aa41160dd1255c43cb8498279738ccChris Craik        self.nodes, self.edges = {}, {}
138cef7893435aa41160dd1255c43cb8498279738ccChris Craik
139cef7893435aa41160dd1255c43cb8498279738ccChris Craik        if graph is not None and nodes is None:
140cef7893435aa41160dd1255c43cb8498279738ccChris Craik            nodes = graph
141cef7893435aa41160dd1255c43cb8498279738ccChris Craik        if graph is not None and edgefn is None:
142cef7893435aa41160dd1255c43cb8498279738ccChris Craik            def edgefn(node, graph=graph):
143cef7893435aa41160dd1255c43cb8498279738ccChris Craik                return graph.out_nbrs(node)
144cef7893435aa41160dd1255c43cb8498279738ccChris Craik        if nodes is None:
145cef7893435aa41160dd1255c43cb8498279738ccChris Craik            nodes = ()
146cef7893435aa41160dd1255c43cb8498279738ccChris Craik
147cef7893435aa41160dd1255c43cb8498279738ccChris Craik        seen = set()
148cef7893435aa41160dd1255c43cb8498279738ccChris Craik        for node in nodes:
149cef7893435aa41160dd1255c43cb8498279738ccChris Craik            if nodevisitor is None:
150cef7893435aa41160dd1255c43cb8498279738ccChris Craik                style = {}
151cef7893435aa41160dd1255c43cb8498279738ccChris Craik            else:
152cef7893435aa41160dd1255c43cb8498279738ccChris Craik                style = nodevisitor(node)
153cef7893435aa41160dd1255c43cb8498279738ccChris Craik            if style is not None:
154cef7893435aa41160dd1255c43cb8498279738ccChris Craik                self.nodes[node] = {}
155cef7893435aa41160dd1255c43cb8498279738ccChris Craik                self.node_style(node, **style)
156cef7893435aa41160dd1255c43cb8498279738ccChris Craik                seen.add(node)
157cef7893435aa41160dd1255c43cb8498279738ccChris Craik        if edgefn is not None:
158cef7893435aa41160dd1255c43cb8498279738ccChris Craik            for head in seen:
159cef7893435aa41160dd1255c43cb8498279738ccChris Craik                for tail in (n for n in edgefn(head) if n in seen):
160cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    if edgevisitor is None:
161cef7893435aa41160dd1255c43cb8498279738ccChris Craik                        edgestyle = {}
162cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    else:
163cef7893435aa41160dd1255c43cb8498279738ccChris Craik                        edgestyle = edgevisitor(head, tail)
164cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    if edgestyle is not None:
165cef7893435aa41160dd1255c43cb8498279738ccChris Craik                        if head not in self.edges:
166cef7893435aa41160dd1255c43cb8498279738ccChris Craik                            self.edges[head] = {}
167cef7893435aa41160dd1255c43cb8498279738ccChris Craik                        self.edges[head][tail] = {}
168cef7893435aa41160dd1255c43cb8498279738ccChris Craik                        self.edge_style(head, tail, **edgestyle)
169cef7893435aa41160dd1255c43cb8498279738ccChris Craik
170cef7893435aa41160dd1255c43cb8498279738ccChris Craik    def style(self, **attr):
171cef7893435aa41160dd1255c43cb8498279738ccChris Craik        '''
172cef7893435aa41160dd1255c43cb8498279738ccChris Craik        Changes the overall style
173cef7893435aa41160dd1255c43cb8498279738ccChris Craik        '''
174cef7893435aa41160dd1255c43cb8498279738ccChris Craik        self.attr = attr
175cef7893435aa41160dd1255c43cb8498279738ccChris Craik
176cef7893435aa41160dd1255c43cb8498279738ccChris Craik    def display(self, mode='dot'):
177cef7893435aa41160dd1255c43cb8498279738ccChris Craik        '''
178cef7893435aa41160dd1255c43cb8498279738ccChris Craik        Displays the current graph via dotty
179cef7893435aa41160dd1255c43cb8498279738ccChris Craik        '''
180cef7893435aa41160dd1255c43cb8498279738ccChris Craik
181cef7893435aa41160dd1255c43cb8498279738ccChris Craik        if  mode == 'neato':
182cef7893435aa41160dd1255c43cb8498279738ccChris Craik            self.save_dot(self.temp_neo)
183cef7893435aa41160dd1255c43cb8498279738ccChris Craik            neato_cmd = "%s -o %s %s" % (self.neato, self.temp_dot, self.temp_neo)
184cef7893435aa41160dd1255c43cb8498279738ccChris Craik            os.system(neato_cmd)
185cef7893435aa41160dd1255c43cb8498279738ccChris Craik        else:
186cef7893435aa41160dd1255c43cb8498279738ccChris Craik            self.save_dot(self.temp_dot)
187cef7893435aa41160dd1255c43cb8498279738ccChris Craik
188cef7893435aa41160dd1255c43cb8498279738ccChris Craik        plot_cmd = "%s %s" % (self.dotty, self.temp_dot)
189cef7893435aa41160dd1255c43cb8498279738ccChris Craik        os.system(plot_cmd)
190cef7893435aa41160dd1255c43cb8498279738ccChris Craik
191cef7893435aa41160dd1255c43cb8498279738ccChris Craik    def node_style(self, node, **kwargs):
192cef7893435aa41160dd1255c43cb8498279738ccChris Craik        '''
193cef7893435aa41160dd1255c43cb8498279738ccChris Craik        Modifies a node style to the dot representation.
194cef7893435aa41160dd1255c43cb8498279738ccChris Craik        '''
195cef7893435aa41160dd1255c43cb8498279738ccChris Craik        if node not in self.edges:
196cef7893435aa41160dd1255c43cb8498279738ccChris Craik            self.edges[node] = {}
197cef7893435aa41160dd1255c43cb8498279738ccChris Craik        self.nodes[node] = kwargs
198cef7893435aa41160dd1255c43cb8498279738ccChris Craik
199cef7893435aa41160dd1255c43cb8498279738ccChris Craik    def all_node_style(self, **kwargs):
200cef7893435aa41160dd1255c43cb8498279738ccChris Craik        '''
201cef7893435aa41160dd1255c43cb8498279738ccChris Craik        Modifies all node styles
202cef7893435aa41160dd1255c43cb8498279738ccChris Craik        '''
203cef7893435aa41160dd1255c43cb8498279738ccChris Craik        for node in self.nodes:
204cef7893435aa41160dd1255c43cb8498279738ccChris Craik            self.node_style(node, **kwargs)
205cef7893435aa41160dd1255c43cb8498279738ccChris Craik
206cef7893435aa41160dd1255c43cb8498279738ccChris Craik    def edge_style(self, head, tail, **kwargs):
207cef7893435aa41160dd1255c43cb8498279738ccChris Craik        '''
208cef7893435aa41160dd1255c43cb8498279738ccChris Craik        Modifies an edge style to the dot representation.
209cef7893435aa41160dd1255c43cb8498279738ccChris Craik        '''
210cef7893435aa41160dd1255c43cb8498279738ccChris Craik        if tail not in self.nodes:
211cef7893435aa41160dd1255c43cb8498279738ccChris Craik            raise GraphError("invalid node %s" % (tail,))
212cef7893435aa41160dd1255c43cb8498279738ccChris Craik
213cef7893435aa41160dd1255c43cb8498279738ccChris Craik        try:
214cef7893435aa41160dd1255c43cb8498279738ccChris Craik            if tail not in self.edges[head]:
215cef7893435aa41160dd1255c43cb8498279738ccChris Craik                self.edges[head][tail]= {}
216cef7893435aa41160dd1255c43cb8498279738ccChris Craik            self.edges[head][tail] = kwargs
217cef7893435aa41160dd1255c43cb8498279738ccChris Craik        except KeyError:
218cef7893435aa41160dd1255c43cb8498279738ccChris Craik            raise GraphError("invalid edge  %s -> %s " % (head, tail) )
219cef7893435aa41160dd1255c43cb8498279738ccChris Craik
220cef7893435aa41160dd1255c43cb8498279738ccChris Craik    def iterdot(self):
221cef7893435aa41160dd1255c43cb8498279738ccChris Craik        # write graph title
222cef7893435aa41160dd1255c43cb8498279738ccChris Craik        if self.type == 'digraph':
223cef7893435aa41160dd1255c43cb8498279738ccChris Craik            yield 'digraph %s {\n' % (self.name,)
224cef7893435aa41160dd1255c43cb8498279738ccChris Craik        elif self.type == 'graph':
225cef7893435aa41160dd1255c43cb8498279738ccChris Craik            yield 'graph %s {\n' % (self.name,)
226cef7893435aa41160dd1255c43cb8498279738ccChris Craik
227cef7893435aa41160dd1255c43cb8498279738ccChris Craik        else:
228cef7893435aa41160dd1255c43cb8498279738ccChris Craik            raise GraphError("unsupported graphtype %s" % (self.type,))
229cef7893435aa41160dd1255c43cb8498279738ccChris Craik
230cef7893435aa41160dd1255c43cb8498279738ccChris Craik        # write overall graph attributes
231cef7893435aa41160dd1255c43cb8498279738ccChris Craik        for attr_name, attr_value in sorted(self.attr.items()):
232cef7893435aa41160dd1255c43cb8498279738ccChris Craik            yield '%s="%s";' % (attr_name, attr_value)
233cef7893435aa41160dd1255c43cb8498279738ccChris Craik        yield '\n'
234cef7893435aa41160dd1255c43cb8498279738ccChris Craik
235cef7893435aa41160dd1255c43cb8498279738ccChris Craik        # some reusable patterns
236cef7893435aa41160dd1255c43cb8498279738ccChris Craik        cpatt  = '%s="%s",'      # to separate attributes
237cef7893435aa41160dd1255c43cb8498279738ccChris Craik        epatt  = '];\n'          # to end attributes
238cef7893435aa41160dd1255c43cb8498279738ccChris Craik
239cef7893435aa41160dd1255c43cb8498279738ccChris Craik        # write node attributes
240cef7893435aa41160dd1255c43cb8498279738ccChris Craik        for node_name, node_attr in sorted(self.nodes.items()):
241cef7893435aa41160dd1255c43cb8498279738ccChris Craik            yield '\t"%s" [' % (node_name,)
242cef7893435aa41160dd1255c43cb8498279738ccChris Craik            for attr_name, attr_value in sorted(node_attr.items()):
243cef7893435aa41160dd1255c43cb8498279738ccChris Craik                yield cpatt % (attr_name, attr_value)
244cef7893435aa41160dd1255c43cb8498279738ccChris Craik            yield epatt
245cef7893435aa41160dd1255c43cb8498279738ccChris Craik
246cef7893435aa41160dd1255c43cb8498279738ccChris Craik        # write edge attributes
247cef7893435aa41160dd1255c43cb8498279738ccChris Craik        for head in sorted(self.edges):
248cef7893435aa41160dd1255c43cb8498279738ccChris Craik            for tail in sorted(self.edges[head]):
249cef7893435aa41160dd1255c43cb8498279738ccChris Craik                if self.type == 'digraph':
250cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    yield '\t"%s" -> "%s" [' % (head, tail)
251cef7893435aa41160dd1255c43cb8498279738ccChris Craik                else:
252cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    yield '\t"%s" -- "%s" [' % (head, tail)
253cef7893435aa41160dd1255c43cb8498279738ccChris Craik                for attr_name, attr_value in sorted(self.edges[head][tail].items()):
254cef7893435aa41160dd1255c43cb8498279738ccChris Craik                    yield cpatt % (attr_name, attr_value)
255cef7893435aa41160dd1255c43cb8498279738ccChris Craik                yield epatt
256cef7893435aa41160dd1255c43cb8498279738ccChris Craik
257cef7893435aa41160dd1255c43cb8498279738ccChris Craik        # finish file
258cef7893435aa41160dd1255c43cb8498279738ccChris Craik        yield '}\n'
259cef7893435aa41160dd1255c43cb8498279738ccChris Craik
260cef7893435aa41160dd1255c43cb8498279738ccChris Craik    def __iter__(self):
261cef7893435aa41160dd1255c43cb8498279738ccChris Craik        return self.iterdot()
262cef7893435aa41160dd1255c43cb8498279738ccChris Craik
263cef7893435aa41160dd1255c43cb8498279738ccChris Craik    def save_dot(self, file_name=None):
264cef7893435aa41160dd1255c43cb8498279738ccChris Craik        '''
265cef7893435aa41160dd1255c43cb8498279738ccChris Craik        Saves the current graph representation into a file
266cef7893435aa41160dd1255c43cb8498279738ccChris Craik        '''
267cef7893435aa41160dd1255c43cb8498279738ccChris Craik
268cef7893435aa41160dd1255c43cb8498279738ccChris Craik        if not file_name:
269cef7893435aa41160dd1255c43cb8498279738ccChris Craik            warnings.warn(DeprecationWarning, "always pass a file_name")
270cef7893435aa41160dd1255c43cb8498279738ccChris Craik            file_name = self.temp_dot
271cef7893435aa41160dd1255c43cb8498279738ccChris Craik
272cef7893435aa41160dd1255c43cb8498279738ccChris Craik        fp   = open(file_name, "w")
273cef7893435aa41160dd1255c43cb8498279738ccChris Craik        try:
274cef7893435aa41160dd1255c43cb8498279738ccChris Craik            for chunk in self.iterdot():
275cef7893435aa41160dd1255c43cb8498279738ccChris Craik                fp.write(chunk)
276cef7893435aa41160dd1255c43cb8498279738ccChris Craik        finally:
277cef7893435aa41160dd1255c43cb8498279738ccChris Craik            fp.close()
278cef7893435aa41160dd1255c43cb8498279738ccChris Craik
279cef7893435aa41160dd1255c43cb8498279738ccChris Craik    def save_img(self, file_name=None, file_type="gif", mode='dot'):
280cef7893435aa41160dd1255c43cb8498279738ccChris Craik        '''
281cef7893435aa41160dd1255c43cb8498279738ccChris Craik        Saves the dot file as an image file
282cef7893435aa41160dd1255c43cb8498279738ccChris Craik        '''
283cef7893435aa41160dd1255c43cb8498279738ccChris Craik
284cef7893435aa41160dd1255c43cb8498279738ccChris Craik        if not file_name:
285cef7893435aa41160dd1255c43cb8498279738ccChris Craik            warnings.warn(DeprecationWarning, "always pass a file_name")
286cef7893435aa41160dd1255c43cb8498279738ccChris Craik            file_name = "out"
287cef7893435aa41160dd1255c43cb8498279738ccChris Craik
288cef7893435aa41160dd1255c43cb8498279738ccChris Craik        if  mode == 'neato':
289cef7893435aa41160dd1255c43cb8498279738ccChris Craik            self.save_dot(self.temp_neo)
290cef7893435aa41160dd1255c43cb8498279738ccChris Craik            neato_cmd = "%s -o %s %s" % (self.neato, self.temp_dot, self.temp_neo)
291cef7893435aa41160dd1255c43cb8498279738ccChris Craik            os.system(neato_cmd)
292cef7893435aa41160dd1255c43cb8498279738ccChris Craik            plot_cmd = self.dot
293cef7893435aa41160dd1255c43cb8498279738ccChris Craik        else:
294cef7893435aa41160dd1255c43cb8498279738ccChris Craik            self.save_dot(self.temp_dot)
295cef7893435aa41160dd1255c43cb8498279738ccChris Craik            plot_cmd = self.dot
296cef7893435aa41160dd1255c43cb8498279738ccChris Craik
297cef7893435aa41160dd1255c43cb8498279738ccChris Craik        file_name  = "%s.%s" % (file_name, file_type)
298cef7893435aa41160dd1255c43cb8498279738ccChris Craik        create_cmd = "%s -T%s %s -o %s" % (plot_cmd, file_type, self.temp_dot, file_name)
299cef7893435aa41160dd1255c43cb8498279738ccChris Craik        os.system(create_cmd)
300