1 """ 2 ***** 3 Pydot 4 ***** 5 6 Import and export NetworkX graphs in Graphviz dot format using pydot. 7 8 Either this module or nx_pygraphviz can be used to interface with graphviz. 9 10 See Also 11 -------- 12 Pydot: http://code.google.com/p/pydot/ 13 Graphviz: http://www.research.att.com/sw/tools/graphviz/ 14 DOT Language: http://www.graphviz.org/doc/info/lang.html 15 """ 16 # Copyright (C) 2004-2013 by 17 # Aric Hagberg <hagberg (at] lanl.gov> 18 # Dan Schult <dschult (at] colgate.edu> 19 # Pieter Swart <swart (at] lanl.gov> 20 # All rights reserved. 21 # BSD license. 22 from networkx.utils import open_file, make_str 23 import networkx as nx 24 __author__ = """Aric Hagberg (aric.hagberg (at] gmail.com)""" 25 __all__ = ['write_dot', 'read_dot', 'graphviz_layout', 'pydot_layout', 26 'to_pydot', 'from_pydot'] 27 28 @open_file(1,mode='w') 29 def write_dot(G,path): 30 """Write NetworkX graph G to Graphviz dot format on path. 31 32 Path can be a string or a file handle. 33 """ 34 try: 35 import pydot 36 except ImportError: 37 raise ImportError("write_dot() requires pydot", 38 "http://code.google.com/p/pydot/") 39 P=to_pydot(G) 40 path.write(P.to_string()) 41 return 42 43 @open_file(0,mode='r') 44 def read_dot(path): 45 """Return a NetworkX MultiGraph or MultiDiGraph from a dot file on path. 46 47 Parameters 48 ---------- 49 path : filename or file handle 50 51 Returns 52 ------- 53 G : NetworkX multigraph 54 A MultiGraph or MultiDiGraph. 55 56 Notes 57 ----- 58 Use G=nx.Graph(nx.read_dot(path)) to return a Graph instead of a MultiGraph. 59 """ 60 try: 61 import pydot 62 except ImportError: 63 raise ImportError("read_dot() requires pydot", 64 "http://code.google.com/p/pydot/") 65 66 data=path.read() 67 P=pydot.graph_from_dot_data(data) 68 return from_pydot(P) 69 70 def from_pydot(P): 71 """Return a NetworkX graph from a Pydot graph. 72 73 Parameters 74 ---------- 75 P : Pydot graph 76 A graph created with Pydot 77 78 Returns 79 ------- 80 G : NetworkX multigraph 81 A MultiGraph or MultiDiGraph. 82 83 Examples 84 -------- 85 >>> K5=nx.complete_graph(5) 86 >>> A=nx.to_pydot(K5) 87 >>> G=nx.from_pydot(A) # return MultiGraph 88 >>> G=nx.Graph(nx.from_pydot(A)) # make a Graph instead of MultiGraph 89 90 """ 91 if P.get_strict(None): # pydot bug: get_strict() shouldn't take argument 92 multiedges=False 93 else: 94 multiedges=True 95 96 if P.get_type()=='graph': # undirected 97 if multiedges: 98 create_using=nx.MultiGraph() 99 else: 100 create_using=nx.Graph() 101 else: 102 if multiedges: 103 create_using=nx.MultiDiGraph() 104 else: 105 create_using=nx.DiGraph() 106 107 # assign defaults 108 N=nx.empty_graph(0,create_using) 109 N.name=P.get_name() 110 111 # add nodes, attributes to N.node_attr 112 for p in P.get_node_list(): 113 n=p.get_name().strip('"') 114 if n in ('node','graph','edge'): 115 continue 116 N.add_node(n,**p.get_attributes()) 117 118 # add edges 119 for e in P.get_edge_list(): 120 u=e.get_source().strip('"') 121 v=e.get_destination().strip('"') 122 attr=e.get_attributes() 123 N.add_edge(u,v,**attr) 124 125 # add default attributes for graph, nodes, edges 126 N.graph['graph']=P.get_attributes() 127 try: 128 N.graph['node']=P.get_node_defaults()[0] 129 except:# IndexError,TypeError: 130 N.graph['node']={} 131 try: 132 N.graph['edge']=P.get_edge_defaults()[0] 133 except:# IndexError,TypeError: 134 N.graph['edge']={} 135 return N 136 137 def to_pydot(N, strict=True): 138 """Return a pydot graph from a NetworkX graph N. 139 140 Parameters 141 ---------- 142 N : NetworkX graph 143 A graph created with NetworkX 144 145 Examples 146 -------- 147 >>> K5=nx.complete_graph(5) 148 >>> P=nx.to_pydot(K5) 149 150 Notes 151 ----- 152 153 """ 154 try: 155 import pydot 156 except ImportError: 157 raise ImportError('to_pydot() requires pydot: ' 158 'http://code.google.com/p/pydot/') 159 160 # set Graphviz graph type 161 if N.is_directed(): 162 graph_type='digraph' 163 else: 164 graph_type='graph' 165 strict=N.number_of_selfloops()==0 and not N.is_multigraph() 166 167 name = N.graph.get('name') 168 graph_defaults=N.graph.get('graph',{}) 169 if name is None: 170 P = pydot.Dot(graph_type=graph_type,strict=strict,**graph_defaults) 171 else: 172 P = pydot.Dot('"%s"'%name,graph_type=graph_type,strict=strict, 173 **graph_defaults) 174 try: 175 P.set_node_defaults(**N.graph['node']) 176 except KeyError: 177 pass 178 try: 179 P.set_edge_defaults(**N.graph['edge']) 180 except KeyError: 181 pass 182 183 for n,nodedata in N.nodes_iter(data=True): 184 str_nodedata=dict((k,make_str(v)) for k,v in nodedata.items()) 185 p=pydot.Node(make_str(n),**str_nodedata) 186 P.add_node(p) 187 188 if N.is_multigraph(): 189 for u,v,key,edgedata in N.edges_iter(data=True,keys=True): 190 str_edgedata=dict((k,make_str(v)) for k,v in edgedata.items()) 191 edge=pydot.Edge(make_str(u),make_str(v),key=make_str(key),**str_edgedata) 192 P.add_edge(edge) 193 194 else: 195 for u,v,edgedata in N.edges_iter(data=True): 196 str_edgedata=dict((k,make_str(v)) for k,v in edgedata.items()) 197 edge=pydot.Edge(make_str(u),make_str(v),**str_edgedata) 198 P.add_edge(edge) 199 return P 200 201 202 def pydot_from_networkx(N): 203 """Create a Pydot graph from a NetworkX graph.""" 204 from warnings import warn 205 warn('pydot_from_networkx is replaced by to_pydot', DeprecationWarning) 206 return to_pydot(N) 207 208 def networkx_from_pydot(D, create_using=None): 209 """Create a NetworkX graph from a Pydot graph.""" 210 from warnings import warn 211 warn('networkx_from_pydot is replaced by from_pydot', 212 DeprecationWarning) 213 return from_pydot(D) 214 215 def graphviz_layout(G,prog='neato',root=None, **kwds): 216 """Create node positions using Pydot and Graphviz. 217 218 Returns a dictionary of positions keyed by node. 219 220 Examples 221 -------- 222 >>> G=nx.complete_graph(4) 223 >>> pos=nx.graphviz_layout(G) 224 >>> pos=nx.graphviz_layout(G,prog='dot') 225 226 Notes 227 ----- 228 This is a wrapper for pydot_layout. 229 """ 230 return pydot_layout(G=G,prog=prog,root=root,**kwds) 231 232 233 def pydot_layout(G,prog='neato',root=None, **kwds): 234 """Create node positions using Pydot and Graphviz. 235 236 Returns a dictionary of positions keyed by node. 237 238 Examples 239 -------- 240 >>> G=nx.complete_graph(4) 241 >>> pos=nx.pydot_layout(G) 242 >>> pos=nx.pydot_layout(G,prog='dot') 243 """ 244 try: 245 import pydot 246 except ImportError: 247 raise ImportError('pydot_layout() requires pydot ', 248 'http://code.google.com/p/pydot/') 249 250 P=to_pydot(G) 251 if root is not None : 252 P.set("root",make_str(root)) 253 254 D=P.create_dot(prog=prog) 255 256 if D=="": # no data returned 257 print("Graphviz layout with %s failed"%(prog)) 258 print() 259 print("To debug what happened try:") 260 print("P=pydot_from_networkx(G)") 261 print("P.write_dot(\"file.dot\")") 262 print("And then run %s on file.dot"%(prog)) 263 return 264 265 Q=pydot.graph_from_dot_data(D) 266 267 node_pos={} 268 for n in G.nodes(): 269 pydot_node = pydot.Node(make_str(n)).get_name().encode('utf-8') 270 node=Q.get_node(pydot_node) 271 272 if isinstance(node,list): 273 node=node[0] 274 pos=node.get_pos()[1:-1] # strip leading and trailing double quotes 275 if pos != None: 276 xx,yy=pos.split(",") 277 node_pos[n]=(float(xx),float(yy)) 278 return node_pos 279 280 # fixture for nose tests 281 def setup_module(module): 282 from nose import SkipTest 283 try: 284 import pydot 285 import dot_parser 286 except: 287 raise SkipTest("pydot not available") 288