Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/networkx/drawing/nx_pydot.py @ 1:56ad4e20f292 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
| author | guerler |
|---|---|
| date | Fri, 31 Jul 2020 00:32:28 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 0:d30785e31577 | 1:56ad4e20f292 |
|---|---|
| 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_agraph can be used to interface with graphviz. | |
| 9 | |
| 10 See Also | |
| 11 -------- | |
| 12 pydot: https://github.com/erocarrera/pydot | |
| 13 Graphviz: https://www.graphviz.org | |
| 14 DOT Language: http://www.graphviz.org/doc/info/lang.html | |
| 15 """ | |
| 16 # Author: Aric Hagberg (aric.hagberg@gmail.com) | |
| 17 | |
| 18 # Copyright (C) 2004-2019 by | |
| 19 # Aric Hagberg <hagberg@lanl.gov> | |
| 20 # Dan Schult <dschult@colgate.edu> | |
| 21 # Pieter Swart <swart@lanl.gov> | |
| 22 # Cecil Curry <leycec@gmail.com> | |
| 23 # All rights reserved. | |
| 24 # BSD license. | |
| 25 from locale import getpreferredencoding | |
| 26 from networkx.utils import open_file, make_str | |
| 27 import networkx as nx | |
| 28 | |
| 29 __all__ = ['write_dot', 'read_dot', 'graphviz_layout', 'pydot_layout', | |
| 30 'to_pydot', 'from_pydot'] | |
| 31 | |
| 32 # 2.x/3.x compatibility | |
| 33 try: | |
| 34 basestring | |
| 35 except NameError: | |
| 36 basestring = str | |
| 37 unicode = str | |
| 38 | |
| 39 | |
| 40 @open_file(1, mode='w') | |
| 41 def write_dot(G, path): | |
| 42 """Write NetworkX graph G to Graphviz dot format on path. | |
| 43 | |
| 44 Path can be a string or a file handle. | |
| 45 """ | |
| 46 P = to_pydot(G) | |
| 47 path.write(P.to_string()) | |
| 48 return | |
| 49 | |
| 50 | |
| 51 @open_file(0, mode='r') | |
| 52 def read_dot(path): | |
| 53 """Returns a NetworkX :class:`MultiGraph` or :class:`MultiDiGraph` from the | |
| 54 dot file with the passed path. | |
| 55 | |
| 56 If this file contains multiple graphs, only the first such graph is | |
| 57 returned. All graphs _except_ the first are silently ignored. | |
| 58 | |
| 59 Parameters | |
| 60 ---------- | |
| 61 path : str or file | |
| 62 Filename or file handle. | |
| 63 | |
| 64 Returns | |
| 65 ------- | |
| 66 G : MultiGraph or MultiDiGraph | |
| 67 A :class:`MultiGraph` or :class:`MultiDiGraph`. | |
| 68 | |
| 69 Notes | |
| 70 ----- | |
| 71 Use `G = nx.Graph(read_dot(path))` to return a :class:`Graph` instead of a | |
| 72 :class:`MultiGraph`. | |
| 73 """ | |
| 74 import pydot | |
| 75 data = path.read() | |
| 76 | |
| 77 # List of one or more "pydot.Dot" instances deserialized from this file. | |
| 78 P_list = pydot.graph_from_dot_data(data) | |
| 79 | |
| 80 # Convert only the first such instance into a NetworkX graph. | |
| 81 return from_pydot(P_list[0]) | |
| 82 | |
| 83 | |
| 84 def from_pydot(P): | |
| 85 """Returns a NetworkX graph from a Pydot graph. | |
| 86 | |
| 87 Parameters | |
| 88 ---------- | |
| 89 P : Pydot graph | |
| 90 A graph created with Pydot | |
| 91 | |
| 92 Returns | |
| 93 ------- | |
| 94 G : NetworkX multigraph | |
| 95 A MultiGraph or MultiDiGraph. | |
| 96 | |
| 97 Examples | |
| 98 -------- | |
| 99 >>> K5 = nx.complete_graph(5) | |
| 100 >>> A = nx.nx_pydot.to_pydot(K5) | |
| 101 >>> G = nx.nx_pydot.from_pydot(A) # return MultiGraph | |
| 102 | |
| 103 # make a Graph instead of MultiGraph | |
| 104 >>> G = nx.Graph(nx.nx_pydot.from_pydot(A)) | |
| 105 | |
| 106 """ | |
| 107 if P.get_strict(None): # pydot bug: get_strict() shouldn't take argument | |
| 108 multiedges = False | |
| 109 else: | |
| 110 multiedges = True | |
| 111 | |
| 112 if P.get_type() == 'graph': # undirected | |
| 113 if multiedges: | |
| 114 N = nx.MultiGraph() | |
| 115 else: | |
| 116 N = nx.Graph() | |
| 117 else: | |
| 118 if multiedges: | |
| 119 N = nx.MultiDiGraph() | |
| 120 else: | |
| 121 N = nx.DiGraph() | |
| 122 | |
| 123 # assign defaults | |
| 124 name = P.get_name().strip('"') | |
| 125 if name != '': | |
| 126 N.name = name | |
| 127 | |
| 128 # add nodes, attributes to N.node_attr | |
| 129 for p in P.get_node_list(): | |
| 130 n = p.get_name().strip('"') | |
| 131 if n in ('node', 'graph', 'edge'): | |
| 132 continue | |
| 133 N.add_node(n, **p.get_attributes()) | |
| 134 | |
| 135 # add edges | |
| 136 for e in P.get_edge_list(): | |
| 137 u = e.get_source() | |
| 138 v = e.get_destination() | |
| 139 attr = e.get_attributes() | |
| 140 s = [] | |
| 141 d = [] | |
| 142 | |
| 143 if isinstance(u, basestring): | |
| 144 s.append(u.strip('"')) | |
| 145 else: | |
| 146 for unodes in u['nodes']: | |
| 147 s.append(unodes.strip('"')) | |
| 148 | |
| 149 if isinstance(v, basestring): | |
| 150 d.append(v.strip('"')) | |
| 151 else: | |
| 152 for vnodes in v['nodes']: | |
| 153 d.append(vnodes.strip('"')) | |
| 154 | |
| 155 for source_node in s: | |
| 156 for destination_node in d: | |
| 157 N.add_edge(source_node, destination_node, **attr) | |
| 158 | |
| 159 # add default attributes for graph, nodes, edges | |
| 160 pattr = P.get_attributes() | |
| 161 if pattr: | |
| 162 N.graph['graph'] = pattr | |
| 163 try: | |
| 164 N.graph['node'] = P.get_node_defaults()[0] | |
| 165 except (IndexError, TypeError): | |
| 166 pass # N.graph['node']={} | |
| 167 try: | |
| 168 N.graph['edge'] = P.get_edge_defaults()[0] | |
| 169 except (IndexError, TypeError): | |
| 170 pass # N.graph['edge']={} | |
| 171 return N | |
| 172 | |
| 173 | |
| 174 def to_pydot(N): | |
| 175 """Returns a pydot graph from a NetworkX graph N. | |
| 176 | |
| 177 Parameters | |
| 178 ---------- | |
| 179 N : NetworkX graph | |
| 180 A graph created with NetworkX | |
| 181 | |
| 182 Examples | |
| 183 -------- | |
| 184 >>> K5 = nx.complete_graph(5) | |
| 185 >>> P = nx.nx_pydot.to_pydot(K5) | |
| 186 | |
| 187 Notes | |
| 188 ----- | |
| 189 | |
| 190 """ | |
| 191 import pydot | |
| 192 | |
| 193 # set Graphviz graph type | |
| 194 if N.is_directed(): | |
| 195 graph_type = 'digraph' | |
| 196 else: | |
| 197 graph_type = 'graph' | |
| 198 strict = nx.number_of_selfloops(N) == 0 and not N.is_multigraph() | |
| 199 | |
| 200 name = N.name | |
| 201 graph_defaults = N.graph.get('graph', {}) | |
| 202 if name == '': | |
| 203 P = pydot.Dot('', graph_type=graph_type, strict=strict, | |
| 204 **graph_defaults) | |
| 205 else: | |
| 206 P = pydot.Dot('"%s"' % name, graph_type=graph_type, strict=strict, | |
| 207 **graph_defaults) | |
| 208 try: | |
| 209 P.set_node_defaults(**N.graph['node']) | |
| 210 except KeyError: | |
| 211 pass | |
| 212 try: | |
| 213 P.set_edge_defaults(**N.graph['edge']) | |
| 214 except KeyError: | |
| 215 pass | |
| 216 | |
| 217 for n, nodedata in N.nodes(data=True): | |
| 218 str_nodedata = dict((k, make_str(v)) for k, v in nodedata.items()) | |
| 219 p = pydot.Node(make_str(n), **str_nodedata) | |
| 220 P.add_node(p) | |
| 221 | |
| 222 if N.is_multigraph(): | |
| 223 for u, v, key, edgedata in N.edges(data=True, keys=True): | |
| 224 str_edgedata = dict((k, make_str(v)) for k, v in edgedata.items() | |
| 225 if k != 'key') | |
| 226 edge = pydot.Edge(make_str(u), make_str(v), | |
| 227 key=make_str(key), **str_edgedata) | |
| 228 P.add_edge(edge) | |
| 229 | |
| 230 else: | |
| 231 for u, v, edgedata in N.edges(data=True): | |
| 232 str_edgedata = dict((k, make_str(v)) for k, v in edgedata.items()) | |
| 233 edge = pydot.Edge(make_str(u), make_str(v), **str_edgedata) | |
| 234 P.add_edge(edge) | |
| 235 return P | |
| 236 | |
| 237 | |
| 238 def graphviz_layout(G, prog='neato', root=None): | |
| 239 """Create node positions using Pydot and Graphviz. | |
| 240 | |
| 241 Returns a dictionary of positions keyed by node. | |
| 242 | |
| 243 Parameters | |
| 244 ---------- | |
| 245 G : NetworkX Graph | |
| 246 The graph for which the layout is computed. | |
| 247 prog : string (default: 'neato') | |
| 248 The name of the GraphViz program to use for layout. | |
| 249 Options depend on GraphViz version but may include: | |
| 250 'dot', 'twopi', 'fdp', 'sfdp', 'circo' | |
| 251 root : Node from G or None (default: None) | |
| 252 The node of G from which to start some layout algorithms. | |
| 253 | |
| 254 Returns | |
| 255 ------- | |
| 256 Dictionary of (x, y) positions keyed by node. | |
| 257 | |
| 258 Examples | |
| 259 -------- | |
| 260 >>> G = nx.complete_graph(4) | |
| 261 >>> pos = nx.nx_pydot.graphviz_layout(G) | |
| 262 >>> pos = nx.nx_pydot.graphviz_layout(G, prog='dot') | |
| 263 | |
| 264 Notes | |
| 265 ----- | |
| 266 This is a wrapper for pydot_layout. | |
| 267 """ | |
| 268 return pydot_layout(G=G, prog=prog, root=root) | |
| 269 | |
| 270 | |
| 271 def pydot_layout(G, prog='neato', root=None): | |
| 272 """Create node positions using :mod:`pydot` and Graphviz. | |
| 273 | |
| 274 Parameters | |
| 275 -------- | |
| 276 G : Graph | |
| 277 NetworkX graph to be laid out. | |
| 278 prog : string (default: 'neato') | |
| 279 Name of the GraphViz command to use for layout. | |
| 280 Options depend on GraphViz version but may include: | |
| 281 'dot', 'twopi', 'fdp', 'sfdp', 'circo' | |
| 282 root : Node from G or None (default: None) | |
| 283 The node of G from which to start some layout algorithms. | |
| 284 | |
| 285 Returns | |
| 286 -------- | |
| 287 dict | |
| 288 Dictionary of positions keyed by node. | |
| 289 | |
| 290 Examples | |
| 291 -------- | |
| 292 >>> G = nx.complete_graph(4) | |
| 293 >>> pos = nx.nx_pydot.pydot_layout(G) | |
| 294 >>> pos = nx.nx_pydot.pydot_layout(G, prog='dot') | |
| 295 | |
| 296 Notes | |
| 297 ----- | |
| 298 If you use complex node objects, they may have the same string | |
| 299 representation and GraphViz could treat them as the same node. | |
| 300 The layout may assign both nodes a single location. See Issue #1568 | |
| 301 If this occurs in your case, consider relabeling the nodes just | |
| 302 for the layout computation using something similar to: | |
| 303 | |
| 304 H = nx.convert_node_labels_to_integers(G, label_attribute='node_label') | |
| 305 H_layout = nx.nx_pydot.pydot_layout(G, prog='dot') | |
| 306 G_layout = {H.nodes[n]['node_label']: p for n, p in H_layout.items()} | |
| 307 | |
| 308 """ | |
| 309 import pydot | |
| 310 P = to_pydot(G) | |
| 311 if root is not None: | |
| 312 P.set("root", make_str(root)) | |
| 313 | |
| 314 # List of low-level bytes comprising a string in the dot language converted | |
| 315 # from the passed graph with the passed external GraphViz command. | |
| 316 D_bytes = P.create_dot(prog=prog) | |
| 317 | |
| 318 # Unique string decoded from these bytes with the preferred locale encoding | |
| 319 D = unicode(D_bytes, encoding=getpreferredencoding()) | |
| 320 | |
| 321 if D == "": # no data returned | |
| 322 print("Graphviz layout with %s failed" % (prog)) | |
| 323 print() | |
| 324 print("To debug what happened try:") | |
| 325 print("P = nx.nx_pydot.to_pydot(G)") | |
| 326 print("P.write_dot(\"file.dot\")") | |
| 327 print("And then run %s on file.dot" % (prog)) | |
| 328 return | |
| 329 | |
| 330 # List of one or more "pydot.Dot" instances deserialized from this string. | |
| 331 Q_list = pydot.graph_from_dot_data(D) | |
| 332 assert len(Q_list) == 1 | |
| 333 | |
| 334 # The first and only such instance, as guaranteed by the above assertion. | |
| 335 Q = Q_list[0] | |
| 336 | |
| 337 node_pos = {} | |
| 338 for n in G.nodes(): | |
| 339 pydot_node = pydot.Node(make_str(n)).get_name() | |
| 340 node = Q.get_node(pydot_node) | |
| 341 | |
| 342 if isinstance(node, list): | |
| 343 node = node[0] | |
| 344 pos = node.get_pos()[1:-1] # strip leading and trailing double quotes | |
| 345 if pos is not None: | |
| 346 xx, yy = pos.split(",") | |
| 347 node_pos[n] = (float(xx), float(yy)) | |
| 348 return node_pos | |
| 349 | |
| 350 | |
| 351 # fixture for pytest | |
| 352 def setup_module(module): | |
| 353 import pytest | |
| 354 pytdot = pytest.importorskip('pytdot') |
