Package networkx :: Package drawing :: Module nx_pygraphviz
[hide private]
[frames] | no frames]

Source Code for Module networkx.drawing.nx_pygraphviz

  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  #    Copyright (C) 2004,2005 by  
 37  #    Aric Hagberg <hagberg@lanl.gov> 
 38  #    Dan Schult <dschult@colgate.edu> 
 39  #    Pieter Swart <swart@lanl.gov> 
 40  #    Distributed under the terms of the GNU Lesser General Public License 
 41  #    http://www.gnu.org/copyleft/lesser.html 
 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   
51 -def write_dot(G,path):
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
61 -def read_dot(path):
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 # handle attributes, user dictionary or function 132 if hasattr(graph_attr,"__getitem__"): # if we are a dict 133 get_graph_attr=graph_attr.copy # call dict copy 134 else: 135 get_graph_attr=graph_attr # call user function 136 137 # nodes 138 if hasattr(node_attr,"__getitem__"): # if we are a dict 139 get_node_attr=node_attr.__getitem__ # call as a function 140 else: 141 get_node_attr=node_attr # call user function 142 143 # edges 144 if hasattr(edge_attr,"__getitem__"): 145 def get_edge_attr(e): 146 return edge_attr[e]
147 else: # elif hasattr(edge_attr,"__call__"): 148 def get_edge_attr(e): 149 return edge_attr(N,e) 150 151 # graph attributes 152 try: 153 attr=get_graph_attr() 154 A.set_attr(attr) 155 except: 156 pass 157 158 # loop over nodes 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 # loop over edges 169 for e in N.edges_iter(): 170 name=None 171 if len(e)==2: 172 (u,v)=e 173 elif len(e)==3: # XGraph or XDigraph 174 (u,v,x)=e 175 if x is not None: 176 if is_string_like(x): # use data as edge name 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 # set graph type or user defined 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 # handle graph attributes, 265 if hasattr(graph_attr,"__setitem__"): # if we are a dict 266 add_graph=graph_attr.update # call update as a function 267 # user function 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 # add node function with optional attributes 276 if hasattr(node_attr,"__setitem__"): 277 # update user dictionary 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 # call user function 284 def add_node(n,a): 285 node_attr(N,n,a) 286 return 287 else: 288 # just add node 289 def add_node(n,a): 290 N.add_node(n) 291 return 292 293 294 # add edge function with optional attributes 295 if hasattr(edge_attr,"__setitem__"): 296 # update user dictionary 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 # call user function 303 def add_edge(e,a): 304 return edge_attr(N,e,a) 305 else: 306 # just add edge 307 def add_edge(e,a): 308 N.add_edge(e[0],e[1]) 309 310 # graph 311 add_graph(dict(A.get_all_attr())) 312 # loop through nodes 313 for node in A.nodes(): 314 name=pygraphviz.graphviz.agnameof(node.anode) 315 add_node(name,A.get_all_attr(node=node)) 316 # loop through edges 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
329 -def xgraph_from_pygraphviz(A):
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
355 -def to_pygraphviz_from_xgraph(G):
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
369 -def graphviz_layout(G,prog='neato',root=None, args=''):
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
384 -def pygraphviz_layout(G,prog='neato',root=None, args=''):
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: # user must pick one of the graphviz programs... 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
444 -def _which(name):
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
455 -def _test_suite():
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 # directory of networkx package (relative to this) 471 nxbase=sys.path[0]+os.sep+os.pardir 472 sys.path.insert(0,nxbase) # prepend to search path 473 unittest.TextTestRunner().run(_test_suite()) 474