1 """
2 Import and export networkx networks to dot format using pygraphviz.
3
4 Provides:
5
6 - write_dot()
7 - read_dot()
8 - graphviz_layout()
9 - pygraphviz_layout()
10
11 - to_pygraphviz()
12 - from_pygraphviz()
13
14 - xgraph_to_pygraphviz()
15 - xgraph_from_pygraphviz()
16
17 and the graph layout methods:
18
19 - graphviz_layout()
20 - pygraphviz_layout()
21
22 Does not currently handle graphviz subgraphs.
23
24 Either this module or nx_pydot can be used to interface with graphviz.
25
26 References:
27 - pygraphviz : http://networkx.lanl.gov/pygraphviz/
28 - Graphviz: http://www.research.att.com/sw/tools/graphviz/
29 - DOT Language: http://www.research.att.com/~erg/graphviz/info/lang.html
30
31 """
32 __author__ = """Aric Hagberg (hagberg@lanl.gov)"""
33 __date__ = "$Date: 2005-06-15 08:55:33 -0600 (Wed, 15 Jun 2005) $"
34 __credits__ = """"""
35 __revision__ = "$Revision: 1034 $"
36
37
38
39
40
41
42 import os
43 import sys
44 from networkx.utils import is_string_like,_get_fh
45 try:
46 import pygraphviz
47 except ImportError:
48 raise
49
50
52 """Write NetworkX graph G to Graphviz dot format on path.
53
54 Path can be a string or a file handle.
55 """
56 A=to_pygraphviz(G)
57 fh=_get_fh(path,mode='w')
58 A.write(fh)
59 return
60
62 """Return a NetworkX Graph from a dot file on path.
63
64 Path can be a string or a file handle.
65 """
66 fh=_get_fh(path,mode='r')
67 A=pygraphviz.Agraph()
68 A.read(fh)
69 return from_pygraphviz(A)
70
71 -def to_pygraphviz(N,
72 graph_attr=None,
73 node_attr=None,
74 edge_attr=None):
75 """Creates a pygraphviz graph from an networkx graph N.
76
77 This simplest use creates a pygraphviz graph with no attributes,
78 just the nodes and edges. Attributes can be added by modifying
79 the resulting pygraphviz graph.
80
81 Optional arguments graph_attr, node_attr, edge_attr are used
82 to specify Graphviz graph properties to applied during the conversion.
83 http://www.graphviz.org/doc/info/attrs.html
84
85 The graph_attr, node_attr, and edge_attr arguments can be either
86 a dictionary or function as follows:
87
88 graph_attr:
89 dictionary with keys as attribute names and values as attribute values
90 or
91 function that returns a dictionary of attribute names and values
92 The function takes one argument - the graph name::
93
94 def graph_attr(G):
95 a={'color':'red','label'='my graph'}
96 return a
97
98 node_attr:
99 dictionary keyed by node name that has as a value a dictionary
100 of attribute names and values.
101 or
102 function that returns a dictionary of attribute names and values
103 The function takes two arguments - the graph name and node name::
104
105 def node_attr(G,n):
106 a={'color':'red','shape'='diamond'}
107 return a
108
109 edge_attr:
110 dictionary keyed by edge tuple (u,v) that has as a value a dictionary
111 of attribute names and values.
112 or
113 function that returns a dictionary of attribute names and values
114 The function takes two arguments - the graph name and edge tuple e::
115
116 def edge_attr(G,e):
117 a={'color':'red','shape'='diamond'}
118 return a
119
120 """
121
122 if N.is_directed():
123 A = pygraphviz.Agraph(name=N.name,
124 type=pygraphviz.graphviz.cvar.Agdirected)
125 digraph=True
126 else:
127 A = pygraphviz.Agraph(name=N.name,
128 type=pygraphviz.graphviz.cvar.Agundirected)
129 digraph=False
130
131
132 if hasattr(graph_attr,"__getitem__"):
133 get_graph_attr=graph_attr.copy
134 else:
135 get_graph_attr=graph_attr
136
137
138 if hasattr(node_attr,"__getitem__"):
139 get_node_attr=node_attr.__getitem__
140 else:
141 get_node_attr=node_attr
142
143
144 if hasattr(edge_attr,"__getitem__"):
145 def get_edge_attr(e):
146 return edge_attr[e]
147 else:
148 def get_edge_attr(e):
149 return edge_attr(N,e)
150
151
152 try:
153 attr=get_graph_attr()
154 A.set_attr(attr)
155 except:
156 pass
157
158
159 for n in N.nodes_iter():
160 node=A.add_node(str(n))
161 try:
162 attr=get_node_attr(n)
163 A.set_node_attr(node,attr)
164 except:
165 pass
166
167
168
169 for e in N.edges_iter():
170 name=None
171 if len(e)==2:
172 (u,v)=e
173 elif len(e)==3:
174 (u,v,x)=e
175 if x is not None:
176 if is_string_like(x):
177 name=x
178 edge=A.add_edge(str(u),str(v),name)
179 try:
180 attr=get_edge_attr(e)
181 A.set_edge_attr((u,v),attr)
182 except:
183 pass
184 if not digraph:
185 try:
186 if len(e)==2:
187 er=(v,u)
188 else:
189 er=(v,u,x)
190 attr=get_edge_attr(er)
191 A.set_edge_attr((v,u),attr)
192 except:
193 pass
194 return A
195
196 -def from_pygraphviz(A,
197 create_using=None,
198 graph_attr=None,
199 node_attr=None,
200 edge_attr=None):
201 """Creates an networkx graph from an pygraphviz graph A.
202
203 This simplest use creates a netwrkx graph and ignores graphviz
204 attributes.
205
206 Optional arguments graph_attr, node_attr, edge_attr are used
207 to get graph properties.
208 http://www.graphviz.org/doc/info/attrs.html
209
210 The graph_attr, node_attr, and edge_attr arguments can be either
211 a dictionary or function as follows:
212
213 graph_attr:
214 empty dictionary will be filled with keys as attribute names and
215 values as attribute values
216 or
217 A function that takes two arguments -
218 the NetworkX graph and attributes::
219
220 def graph_attr(G,a):
221 print a # just print attributes
222 return
223
224 node_attr:
225 empty dictionary will be filled with keys as nodes with
226 value set to a dictionary of attribute names and values.
227 or
228 A function that takes three arguments - the graph name, node name
229 and attributes::
230
231 def node_attr(G,n,a):
232 print n,a # print node and attributes
233 return a
234
235 edge_attr:
236 empty dictionary will be filled with keys as edge tuples (u,v)
237 with a value set to a dictionary of attribute names and values.
238 or
239 A function that takes three arguments - the graph name, edge tuple,
240 and attributes::
241
242 def edge_attr(G,n,e):
243 print e,a # print node and attributes
244 return a
245
246
247 create_using is an optional networkx graph type.
248 The default is to use a Graph or DiGraph depending on the
249 type of the pygraphviz graph A
250
251 """
252 import networkx
253
254
255 if create_using is None:
256 if A.is_undirected():
257 create_using=networkx.Graph()
258 else:
259 create_using=networkx.DiGraph()
260
261 N=networkx.empty_graph(0,create_using)
262 N.name=str(A)
263
264
265 if hasattr(graph_attr,"__setitem__"):
266 add_graph=graph_attr.update
267
268 elif hasattr(graph_attr,"__call__"):
269 def add_graph(a):
270 return graph_attr(N,a)
271 else:
272 def add_graph(a):
273 return
274
275
276 if hasattr(node_attr,"__setitem__"):
277
278 def add_node(n,a):
279 N.add_node(n)
280 node_attr[n]=a
281 return
282 elif hasattr(node_attr,"__call__"):
283
284 def add_node(n,a):
285 node_attr(N,n,a)
286 return
287 else:
288
289 def add_node(n,a):
290 N.add_node(n)
291 return
292
293
294
295 if hasattr(edge_attr,"__setitem__"):
296
297 def add_edge(e,a):
298 N.add_edge(e[0],e[1])
299 edge_attr[e]=a
300 return
301 elif hasattr(edge_attr,"__call__"):
302
303 def add_edge(e,a):
304 return edge_attr(N,e,a)
305 else:
306
307 def add_edge(e,a):
308 N.add_edge(e[0],e[1])
309
310
311 add_graph(dict(A.get_all_attr()))
312
313 for node in A.nodes():
314 name=pygraphviz.graphviz.agnameof(node.anode)
315 add_node(name,A.get_all_attr(node=node))
316
317 edges_seen = {}
318 for edge in A.edges():
319 if edge in edges_seen: continue
320 source=pygraphviz.graphviz.agnameof(edge.source().anode)
321 target=pygraphviz.graphviz.agnameof(edge.target().anode)
322 edges_seen[edge]=1
323 add_edge((source,target),A.get_all_attr(edge=(source,target)))
324 return N
325
326 networkx_from_pygraphviz=from_pygraphviz
327 pygraphviz_from_networkx=to_pygraphviz
328
330 """Convert from a pygraphviz graph to a NetworkX XGraph.
331
332 A NetworkX XGraph XG is returned with
333
334 XG.attr - a dictionary set to the graphviz graph attributes
335 XG.nattr - a dictionary keyed by node with graphviz graph attributes
336
337 and the edge data set to a dictionary of graphviz edge attributes.
338 """
339 graph_attr={}
340 node_attr={}
341 def set_edge_attr(N,e,attr):
342 N.add_edge(e[0],e[1],attr)
343 return
344 XG=XGraph(multiedges=True,selfloops=True)
345 G=from_pygraphviz(A,
346 graph_attr=graph_attr,
347 node_attr=node_attr,
348 edge_attr=set_edge_attr,
349 create_using=G)
350
351 G.attr=graph_attr
352 G.nattr=node_attr
353 return G
354
356 """Convert from a NetworkX XGraph with attributes to a pygraphviz graph.
357 See from_pygraphviz_to_xgraph for the special XGraph format.
358 """
359 def get_edge_attr(N,e):
360 return e[2]
361 A=to_pygraphviz(G,
362 graph_attr=G.attr,
363 node_attr=G.nattr,
364 edge_attr=get_edge_attr)
365
366 return A
367
368
370 """
371 Create layout using graphviz.
372 Returns a dictionary of positions keyed by node.
373
374 >>> from networkx import *
375 >>> G=petersen_graph()
376 >>> pos=graphviz_layout(G)
377 >>> pos=graphviz_layout(G,prog='dot')
378
379 This is a wrapper for pygraphviz_layout.
380
381 """
382 return pygraphviz_layout(G,prog=prog,root=root,args=args)
383
385 """
386 Create layout using pygraphviz and graphviz.
387 Returns a dictionary of positions keyed by node.
388
389 >>> from networkx import *
390 >>> G=petersen_graph()
391 >>> pos=pygraphviz_layout(G)
392 >>> pos=pygraphviz_layout(G,prog='dot')
393
394 """
395 import tempfile
396 try:
397 import pygraphviz
398 except:
399 print "Import Error: not able to import pygraphviz."
400 raise
401
402 if root is not None :
403 args+=" -Groot=%s"%str(root)
404
405 gprogs=dict.fromkeys(['neato','dot','twopi','circo','fdp','circa'],True)
406 try:
407 gprogs[prog]==True
408 except:
409 raise "program %s not from graphviz"%prog
410
411 try:
412 runprog = _which(prog)
413 except:
414 raise "program %s not found in path"%prog
415
416 tmp_fd, tmp_name = tempfile.mkstemp()
417 write_dot(G, tmp_name)
418 os.close(tmp_fd)
419 cmd=' '.join([runprog,args,"-Tdot",tmp_name])
420 stdin,stdout,stderr=os.popen3(cmd, 'b')
421 try:
422 A=pygraphviz.Agraph()
423 A.read(stdout)
424 stdin.close(); stdout.close(); stderr.close()
425 except:
426 print "Graphviz layout with %s failed"%(prog)
427 print "the file %s might have useful information"%tmp_name
428 print stderr.read()
429 stdin.close(); stdout.close(); stderr.close()
430 return
431
432 os.unlink(tmp_name)
433 node_pos={}
434 for n in G.nodes():
435 node=A.get_node(str(n))
436 try:
437 xx,yy=node.get_attr("pos").split(',')
438 node_pos[n]=(float(xx),float(yy))
439 except:
440 print "no position for node",n
441 node_pos[n]=(0.0,0.0)
442 return node_pos
443
445 """searches for executable in path """
446 import os
447 import glob
448 paths = os.environ["PATH"]
449 for path in paths.split(os.pathsep):
450 match=glob.glob(os.path.join(path, name))
451 if match:
452 return match[0]
453 raise "no prog %s in path"%name
454
456 import doctest
457 suite = doctest.DocFileSuite('tests/drawing/nx_pygraphviz.txt',package='networkx')
458 return suite
459
460
461
462
463 if __name__ == "__main__":
464 import os
465 import sys
466 import unittest
467 if sys.version_info[:2] < (2, 4):
468 print "Python version 2.4 or later required for tests (%d.%d detected)." % sys.version_info[:2]
469 sys.exit(-1)
470
471 nxbase=sys.path[0]+os.sep+os.pardir
472 sys.path.insert(0,nxbase)
473 unittest.TextTestRunner().run(_test_suite())
474