1 """
2 Import and export NetworkX graphs in Graphviz dot format using pydot.
3
4 Provides:
5
6 - write_dot()
7 - read_dot()
8 - graphviz_layout()
9 - pydot_layout()
10
11 - to_pydot()
12 - from_pydot(0
13
14 Either this module or nx_pygraphviz can be used to interface with graphviz.
15
16 References:
17 - pydot Homepage: http://www.dkbza.org/pydot.html
18 - Graphviz: http://www.research.att.com/sw/tools/graphviz/
19 - DOT Language: http://www.research.att.com/~erg/graphviz/info/lang.html
20
21 """
22 __author__ = """Aric Hagberg (hagberg@lanl.gov)"""
23
24
25
26
27
28
29 import sys
30 from networkx.utils import _get_fh
31 try:
32 import pydot
33 except ImportError:
34 raise
35
36
38 """Write NetworkX graph G to Graphviz dot format on path.
39
40 Path can be a string or a file handle.
41 """
42 fh=_get_fh(path,'w')
43 P=to_pydot(G)
44 fh.write(P.to_string())
45 fh.flush()
46 return
47
49 """Return a NetworkX Graph or DiGraph from a dot file on path.
50
51 Path can be a string or a file handle.
52
53 """
54 fh=_get_fh(path,'r')
55 data=fh.read()
56 P=pydot.graph_from_dot_data(data)
57 return from_pydot(P)
58
59
61 """Return a NetworkX XGraph or XDiGraph from a pydot graph.
62
63 >>> X=from_pydot(P)
64
65 The XGraph X will have a dictionary X.graph_attr containing
66 the default graphviz attributes for graphs, nodes and edges.
67
68 Default node attributes will be in the dictionary X.node_attr
69 which is keyed by node.
70
71 Edge attributes will be returned as edge data in the graph X.
72
73 If you want a Graph with no attributes attached instead of an XGraph
74 with attributes use
75
76 >>> G=Graph(X)
77
78 Similarly to make a DiGraph from an XDiGraph
79
80 >>> D=DiGraph(X)
81
82 """
83 import networkx
84
85 if P.get_strict(0):
86 multiedges=False
87 selfloops=False
88 else:
89 multiedges=True
90 selfloops=True
91
92 if P.get_type()=='graph':
93 create_using=networkx.XGraph(multiedges=multiedges,
94 selfloops=selfloops)
95 else:
96 create_using=networkx.XDiGraph(multiedges=multiedges,
97 selfloops=selfloops)
98
99
100 N=networkx.empty_graph(0,create_using)
101 N.name=P.get_name()
102 node_attr={}
103
104
105 for p in P.get_node_list():
106 n=p.get_name()
107 if n.startswith('"'):
108 n=n[1:-1]
109 if n in ('node','graph','edge'):
110 continue
111 N.add_node(n)
112 node_attr[n]=p.get_attributes()
113
114
115 for e in P.get_edge_list():
116 source=e.get_source()
117 dest=e.get_destination()
118 if hasattr(N,'allow_multiedges')==True:
119 N.add_edge(source,dest,e.get_attributes())
120 else:
121 N.add_edge(source,dest)
122
123
124
125 if hasattr(N,'allow_multiedges')==True:
126 N.graph_attr={}
127 N.graph_attr['graph']=P.get_attributes()
128
129
130 if 'node' in P.obj_dict['nodes']:
131 N.graph_attr['node']=P.obj_dict['nodes']['node'][0]['attributes']
132
133 if 'edge' in P.obj_dict['nodes']:
134 N.graph_attr['edge']=P.obj_dict['nodes']['edge'][0]['attributes']
135 N.node_attr=node_attr
136
137 return N
138
139 -def to_pydot(N, graph_attr=None, node_attr=None, edge_attr=None,
140 strict=True):
141 """Return a pydot graph from a NetworkX graph N.
142
143 If N is a Graph or DiGraph, graphviz attributes can
144 be supplied through the keyword arguments
145
146 graph_attr: dictionary with default attributes for graph, nodes, and edges
147 keyed by 'graph', 'node', and 'edge' to attribute dictionaries
148
149 node_attr: dictionary keyed by node to node attribute dictionary
150
151 edge_attr: dictionary keyed by edge tuple to edge attribute dictionary
152
153 If N is an XGraph or XDiGraph an attempt will be made first
154 to copy properties attached to the graph (see from_pydot)
155 and then updated with the calling arguments, if any.
156
157 """
158 if hasattr(N,'graph_attr'):
159 graph_attributes=N.graph_attr
160 else:
161 graph_attributes={}
162 if graph_attr is not None:
163 graph_attributes.update(graph_attr)
164
165 directed=N.is_directed()
166 if N.is_directed():
167 graph_type='digraph'
168 else:
169 graph_type='graph'
170 if hasattr(N,'allow_multiedges'):
171 if N.multiedges:
172 strict=False
173 if hasattr(N,'allow_selfloops'):
174 if N.selfloops:
175 strict=False
176
177 try:
178 node_a=N.node_attr
179 except:
180 node_a={}
181 if node_attr is not None:
182 node_a.update(node_attr)
183
184 P = pydot.Dot(graph_type=graph_type,strict=strict)
185
186 for n in N.nodes_iter():
187 if n in node_a:
188 attr=node_a[n]
189 else:
190 attr={}
191 p=pydot.Node(str(n),**attr)
192 P.add_node(p)
193
194 for e in N.edges_iter():
195 if len(e)==2:
196 (u,v)=e
197 edge=pydot.Edge(str(u),str(v))
198 P.add_edge(edge)
199 if len(e)==3:
200 (u,v,x)=e
201 try:
202 N.allow_multiedges()==True
203 dlist=N.get_edge(u,v)
204 except:
205 dlist=[N.get_edge(u,v)]
206 for d in dlist:
207 if hasattr(d,"__getitem__"):
208 attr=d
209 else:
210 attr={'label':d}
211 try:
212 attr.update(edge_attr[(u,v)])
213 except:
214 pass
215 edge=pydot.Edge(str(u),str(v),**attr)
216 P.add_edge(edge)
217
218 try:
219 P.obj_dict['attributes'].update(graph_attributes['graph'])
220 except:
221 pass
222 try:
223 P.obj_dict['nodes']['node'][0]['attributes'].update(graph_attributes['node'])
224 except:
225 pass
226 try:
227 P.obj_dict['nodes']['edge'][0]['attributes'].update(graph_attributes['edge'])
228 except:
229 pass
230
231 return P
232
233
235 """Creates a pydot graph from an networkx graph N"""
236 from warnings import warn
237 warn('pydot_from_networkx is replaced by to_pydot', DeprecationWarning)
238 return to_pydot(N)
239
241 """Creates an networkx graph from an pydot graph D"""
242 from warnings import warn
243 warn('networkx_from_pydot is replaced by from_pydot',
244 DeprecationWarning)
245 return from_pydot(D)
246
248 """Create layout using pydot and graphviz.
249 Returns a dictionary of positions keyed by node.
250
251 >>> pos=graphviz_layout(G)
252 >>> pos=graphviz_layout(G,prog='dot')
253
254 This is a wrapper for pydot_layout.
255
256 """
257 return pydot_layout(G=G,prog=prog,root=root,**kwds)
258
259
261 """
262 Create layout using pydot and graphviz.
263 Returns a dictionary of positions keyed by node.
264
265 >>> pos=pydot_layout(G)
266 >>> pos=pydot_layout(G,prog='dot')
267
268 """
269 from networkx.drawing.nx_pydot import pydot_from_networkx
270 try:
271 import pydot
272 except:
273 print "Import Error: not able to import pydot."
274 raise
275 P=to_pydot(G)
276 if root is not None :
277 P.set("root",str(root))
278
279 D=P.create_dot(prog=prog)
280
281 if D=="":
282 print "Graphviz layout with %s failed"%(prog)
283 print
284 print "To debug what happened try:"
285 print "P=pydot_from_networkx(G)"
286 print "P.write_dot(\"file.dot\")"
287 print "And then run %s on file.dot"%(prog)
288 return
289
290 Q=pydot.graph_from_dot_data(D)
291
292 node_pos={}
293 for n in G.nodes():
294 node=Q.get_node(str(n))
295 pos=node.get_pos()[1:-1]
296 if pos != None:
297 xx,yy=pos.split(",")
298 node_pos[n]=(float(xx),float(yy))
299 return node_pos
300
302 import doctest
303 suite = doctest.DocFileSuite('tests/drawing/nx_pydot.txt',package='networkx')
304 return suite
305
306
307 if __name__ == "__main__":
308 import os
309 import sys
310 import unittest
311 if sys.version_info[:2] < (2, 4):
312 print "Python version 2.4 or later required for tests (%d.%d detected)." % sys.version_info[:2]
313 sys.exit(-1)
314
315 nxbase=sys.path[0]+os.sep+os.pardir
316 sys.path.insert(0,nxbase)
317 unittest.TextTestRunner().run(_test_suite())
318