Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/networkx/drawing/nx_agraph.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 # Copyright (C) 2004-2019 by | |
2 # Aric Hagberg <hagberg@lanl.gov> | |
3 # Dan Schult <dschult@colgate.edu> | |
4 # Pieter Swart <swart@lanl.gov> | |
5 # All rights reserved. | |
6 # BSD license. | |
7 # | |
8 # Author: Aric Hagberg (hagberg@lanl.gov) | |
9 """ | |
10 *************** | |
11 Graphviz AGraph | |
12 *************** | |
13 | |
14 Interface to pygraphviz AGraph class. | |
15 | |
16 Examples | |
17 -------- | |
18 >>> G = nx.complete_graph(5) | |
19 >>> A = nx.nx_agraph.to_agraph(G) | |
20 >>> H = nx.nx_agraph.from_agraph(A) | |
21 | |
22 See Also | |
23 -------- | |
24 Pygraphviz: http://pygraphviz.github.io/ | |
25 """ | |
26 import os | |
27 import tempfile | |
28 import networkx as nx | |
29 | |
30 __all__ = ['from_agraph', 'to_agraph', | |
31 'write_dot', 'read_dot', | |
32 'graphviz_layout', | |
33 'pygraphviz_layout', | |
34 'view_pygraphviz'] | |
35 | |
36 | |
37 def from_agraph(A, create_using=None): | |
38 """Returns a NetworkX Graph or DiGraph from a PyGraphviz graph. | |
39 | |
40 Parameters | |
41 ---------- | |
42 A : PyGraphviz AGraph | |
43 A graph created with PyGraphviz | |
44 | |
45 create_using : NetworkX graph constructor, optional (default=nx.Graph) | |
46 Graph type to create. If graph instance, then cleared before populated. | |
47 | |
48 Examples | |
49 -------- | |
50 >>> K5 = nx.complete_graph(5) | |
51 >>> A = nx.nx_agraph.to_agraph(K5) | |
52 >>> G = nx.nx_agraph.from_agraph(A) | |
53 | |
54 Notes | |
55 ----- | |
56 The Graph G will have a dictionary G.graph_attr containing | |
57 the default graphviz attributes for graphs, nodes and edges. | |
58 | |
59 Default node attributes will be in the dictionary G.node_attr | |
60 which is keyed by node. | |
61 | |
62 Edge attributes will be returned as edge data in G. With | |
63 edge_attr=False the edge data will be the Graphviz edge weight | |
64 attribute or the value 1 if no edge weight attribute is found. | |
65 | |
66 """ | |
67 if create_using is None: | |
68 if A.is_directed(): | |
69 if A.is_strict(): | |
70 create_using = nx.DiGraph | |
71 else: | |
72 create_using = nx.MultiDiGraph | |
73 else: | |
74 if A.is_strict(): | |
75 create_using = nx.Graph | |
76 else: | |
77 create_using = nx.MultiGraph | |
78 | |
79 # assign defaults | |
80 N = nx.empty_graph(0, create_using) | |
81 if A.name is not None: | |
82 N.name = A.name | |
83 | |
84 # add graph attributes | |
85 N.graph.update(A.graph_attr) | |
86 | |
87 # add nodes, attributes to N.node_attr | |
88 for n in A.nodes(): | |
89 str_attr = {str(k): v for k, v in n.attr.items()} | |
90 N.add_node(str(n), **str_attr) | |
91 | |
92 # add edges, assign edge data as dictionary of attributes | |
93 for e in A.edges(): | |
94 u, v = str(e[0]), str(e[1]) | |
95 attr = dict(e.attr) | |
96 str_attr = {str(k): v for k, v in attr.items()} | |
97 if not N.is_multigraph(): | |
98 if e.name is not None: | |
99 str_attr['key'] = e.name | |
100 N.add_edge(u, v, **str_attr) | |
101 else: | |
102 N.add_edge(u, v, key=e.name, **str_attr) | |
103 | |
104 # add default attributes for graph, nodes, and edges | |
105 # hang them on N.graph_attr | |
106 N.graph['graph'] = dict(A.graph_attr) | |
107 N.graph['node'] = dict(A.node_attr) | |
108 N.graph['edge'] = dict(A.edge_attr) | |
109 return N | |
110 | |
111 | |
112 def to_agraph(N): | |
113 """Returns a pygraphviz graph from a NetworkX graph N. | |
114 | |
115 Parameters | |
116 ---------- | |
117 N : NetworkX graph | |
118 A graph created with NetworkX | |
119 | |
120 Examples | |
121 -------- | |
122 >>> K5 = nx.complete_graph(5) | |
123 >>> A = nx.nx_agraph.to_agraph(K5) | |
124 | |
125 Notes | |
126 ----- | |
127 If N has an dict N.graph_attr an attempt will be made first | |
128 to copy properties attached to the graph (see from_agraph) | |
129 and then updated with the calling arguments if any. | |
130 | |
131 """ | |
132 try: | |
133 import pygraphviz | |
134 except ImportError: | |
135 raise ImportError('requires pygraphviz ', | |
136 'http://pygraphviz.github.io/') | |
137 directed = N.is_directed() | |
138 strict = nx.number_of_selfloops(N) == 0 and not N.is_multigraph() | |
139 A = pygraphviz.AGraph(name=N.name, strict=strict, directed=directed) | |
140 | |
141 # default graph attributes | |
142 A.graph_attr.update(N.graph.get('graph', {})) | |
143 A.node_attr.update(N.graph.get('node', {})) | |
144 A.edge_attr.update(N.graph.get('edge', {})) | |
145 | |
146 A.graph_attr.update((k, v) for k, v in N.graph.items() | |
147 if k not in ('graph', 'node', 'edge')) | |
148 | |
149 # add nodes | |
150 for n, nodedata in N.nodes(data=True): | |
151 A.add_node(n) | |
152 if nodedata is not None: | |
153 a = A.get_node(n) | |
154 a.attr.update({k: str(v) for k, v in nodedata.items()}) | |
155 | |
156 # loop over edges | |
157 if N.is_multigraph(): | |
158 for u, v, key, edgedata in N.edges(data=True, keys=True): | |
159 str_edgedata = {k: str(v) for k, v in edgedata.items() | |
160 if k != 'key'} | |
161 A.add_edge(u, v, key=str(key)) | |
162 if edgedata is not None: | |
163 a = A.get_edge(u, v) | |
164 a.attr.update(str_edgedata) | |
165 | |
166 else: | |
167 for u, v, edgedata in N.edges(data=True): | |
168 str_edgedata = {k: str(v) for k, v in edgedata.items()} | |
169 A.add_edge(u, v) | |
170 if edgedata is not None: | |
171 a = A.get_edge(u, v) | |
172 a.attr.update(str_edgedata) | |
173 | |
174 return A | |
175 | |
176 | |
177 def write_dot(G, path): | |
178 """Write NetworkX graph G to Graphviz dot format on path. | |
179 | |
180 Parameters | |
181 ---------- | |
182 G : graph | |
183 A networkx graph | |
184 path : filename | |
185 Filename or file handle to write | |
186 """ | |
187 try: | |
188 import pygraphviz | |
189 except ImportError: | |
190 raise ImportError('requires pygraphviz ', | |
191 'http://pygraphviz.github.io/') | |
192 A = to_agraph(G) | |
193 A.write(path) | |
194 A.clear() | |
195 return | |
196 | |
197 | |
198 def read_dot(path): | |
199 """Returns a NetworkX graph from a dot file on path. | |
200 | |
201 Parameters | |
202 ---------- | |
203 path : file or string | |
204 File name or file handle to read. | |
205 """ | |
206 try: | |
207 import pygraphviz | |
208 except ImportError: | |
209 raise ImportError('read_dot() requires pygraphviz ', | |
210 'http://pygraphviz.github.io/') | |
211 A = pygraphviz.AGraph(file=path) | |
212 return from_agraph(A) | |
213 | |
214 | |
215 def graphviz_layout(G, prog='neato', root=None, args=''): | |
216 """Create node positions for G using Graphviz. | |
217 | |
218 Parameters | |
219 ---------- | |
220 G : NetworkX graph | |
221 A graph created with NetworkX | |
222 prog : string | |
223 Name of Graphviz layout program | |
224 root : string, optional | |
225 Root node for twopi layout | |
226 args : string, optional | |
227 Extra arguments to Graphviz layout program | |
228 | |
229 Returns | |
230 ------- | |
231 Dictionary of x, y, positions keyed by node. | |
232 | |
233 Examples | |
234 -------- | |
235 >>> G = nx.petersen_graph() | |
236 >>> pos = nx.nx_agraph.graphviz_layout(G) | |
237 >>> pos = nx.nx_agraph.graphviz_layout(G, prog='dot') | |
238 | |
239 Notes | |
240 ----- | |
241 This is a wrapper for pygraphviz_layout. | |
242 """ | |
243 return pygraphviz_layout(G, prog=prog, root=root, args=args) | |
244 | |
245 | |
246 def pygraphviz_layout(G, prog='neato', root=None, args=''): | |
247 """Create node positions for G using Graphviz. | |
248 | |
249 Parameters | |
250 ---------- | |
251 G : NetworkX graph | |
252 A graph created with NetworkX | |
253 prog : string | |
254 Name of Graphviz layout program | |
255 root : string, optional | |
256 Root node for twopi layout | |
257 args : string, optional | |
258 Extra arguments to Graphviz layout program | |
259 | |
260 Returns : dictionary | |
261 Dictionary of x, y, positions keyed by node. | |
262 | |
263 Examples | |
264 -------- | |
265 >>> G = nx.petersen_graph() | |
266 >>> pos = nx.nx_agraph.graphviz_layout(G) | |
267 >>> pos = nx.nx_agraph.graphviz_layout(G, prog='dot') | |
268 | |
269 Notes | |
270 ----- | |
271 If you use complex node objects, they may have the same string | |
272 representation and GraphViz could treat them as the same node. | |
273 The layout may assign both nodes a single location. See Issue #1568 | |
274 If this occurs in your case, consider relabeling the nodes just | |
275 for the layout computation using something similar to: | |
276 | |
277 H = nx.convert_node_labels_to_integers(G, label_attribute='node_label') | |
278 H_layout = nx.nx_agraph.pygraphviz_layout(G, prog='dot') | |
279 G_layout = {H.nodes[n]['node_label']: p for n, p in H_layout.items()} | |
280 | |
281 """ | |
282 try: | |
283 import pygraphviz | |
284 except ImportError: | |
285 raise ImportError('requires pygraphviz ', | |
286 'http://pygraphviz.github.io/') | |
287 if root is not None: | |
288 args += "-Groot=%s" % root | |
289 A = to_agraph(G) | |
290 A.layout(prog=prog, args=args) | |
291 node_pos = {} | |
292 for n in G: | |
293 node = pygraphviz.Node(A, n) | |
294 try: | |
295 xs = node.attr["pos"].split(',') | |
296 node_pos[n] = tuple(float(x) for x in xs) | |
297 except: | |
298 print("no position for node", n) | |
299 node_pos[n] = (0.0, 0.0) | |
300 return node_pos | |
301 | |
302 | |
303 @nx.utils.open_file(5, 'w+b') | |
304 def view_pygraphviz(G, edgelabel=None, prog='dot', args='', | |
305 suffix='', path=None): | |
306 """Views the graph G using the specified layout algorithm. | |
307 | |
308 Parameters | |
309 ---------- | |
310 G : NetworkX graph | |
311 The machine to draw. | |
312 edgelabel : str, callable, None | |
313 If a string, then it specifes the edge attribute to be displayed | |
314 on the edge labels. If a callable, then it is called for each | |
315 edge and it should return the string to be displayed on the edges. | |
316 The function signature of `edgelabel` should be edgelabel(data), | |
317 where `data` is the edge attribute dictionary. | |
318 prog : string | |
319 Name of Graphviz layout program. | |
320 args : str | |
321 Additional arguments to pass to the Graphviz layout program. | |
322 suffix : str | |
323 If `filename` is None, we save to a temporary file. The value of | |
324 `suffix` will appear at the tail end of the temporary filename. | |
325 path : str, None | |
326 The filename used to save the image. If None, save to a temporary | |
327 file. File formats are the same as those from pygraphviz.agraph.draw. | |
328 | |
329 Returns | |
330 ------- | |
331 path : str | |
332 The filename of the generated image. | |
333 A : PyGraphviz graph | |
334 The PyGraphviz graph instance used to generate the image. | |
335 | |
336 Notes | |
337 ----- | |
338 If this function is called in succession too quickly, sometimes the | |
339 image is not displayed. So you might consider time.sleep(.5) between | |
340 calls if you experience problems. | |
341 | |
342 """ | |
343 if not len(G): | |
344 raise nx.NetworkXException("An empty graph cannot be drawn.") | |
345 | |
346 import pygraphviz | |
347 | |
348 # If we are providing default values for graphviz, these must be set | |
349 # before any nodes or edges are added to the PyGraphviz graph object. | |
350 # The reason for this is that default values only affect incoming objects. | |
351 # If you change the default values after the objects have been added, | |
352 # then they inherit no value and are set only if explicitly set. | |
353 | |
354 # to_agraph() uses these values. | |
355 attrs = ['edge', 'node', 'graph'] | |
356 for attr in attrs: | |
357 if attr not in G.graph: | |
358 G.graph[attr] = {} | |
359 | |
360 # These are the default values. | |
361 edge_attrs = {'fontsize': '10'} | |
362 node_attrs = {'style': 'filled', | |
363 'fillcolor': '#0000FF40', | |
364 'height': '0.75', | |
365 'width': '0.75', | |
366 'shape': 'circle'} | |
367 graph_attrs = {} | |
368 | |
369 def update_attrs(which, attrs): | |
370 # Update graph attributes. Return list of those which were added. | |
371 added = [] | |
372 for k, v in attrs.items(): | |
373 if k not in G.graph[which]: | |
374 G.graph[which][k] = v | |
375 added.append(k) | |
376 | |
377 def clean_attrs(which, added): | |
378 # Remove added attributes | |
379 for attr in added: | |
380 del G.graph[which][attr] | |
381 if not G.graph[which]: | |
382 del G.graph[which] | |
383 | |
384 # Update all default values | |
385 update_attrs('edge', edge_attrs) | |
386 update_attrs('node', node_attrs) | |
387 update_attrs('graph', graph_attrs) | |
388 | |
389 # Convert to agraph, so we inherit default values | |
390 A = to_agraph(G) | |
391 | |
392 # Remove the default values we added to the original graph. | |
393 clean_attrs('edge', edge_attrs) | |
394 clean_attrs('node', node_attrs) | |
395 clean_attrs('graph', graph_attrs) | |
396 | |
397 # If the user passed in an edgelabel, we update the labels for all edges. | |
398 if edgelabel is not None: | |
399 if not hasattr(edgelabel, '__call__'): | |
400 def func(data): | |
401 return ''.join([" ", str(data[edgelabel]), " "]) | |
402 else: | |
403 func = edgelabel | |
404 | |
405 # update all the edge labels | |
406 if G.is_multigraph(): | |
407 for u, v, key, data in G.edges(keys=True, data=True): | |
408 # PyGraphviz doesn't convert the key to a string. See #339 | |
409 edge = A.get_edge(u, v, str(key)) | |
410 edge.attr['label'] = str(func(data)) | |
411 else: | |
412 for u, v, data in G.edges(data=True): | |
413 edge = A.get_edge(u, v) | |
414 edge.attr['label'] = str(func(data)) | |
415 | |
416 if path is None: | |
417 ext = 'png' | |
418 if suffix: | |
419 suffix = '_%s.%s' % (suffix, ext) | |
420 else: | |
421 suffix = '.%s' % (ext,) | |
422 path = tempfile.NamedTemporaryFile(suffix=suffix, delete=False) | |
423 else: | |
424 # Assume the decorator worked and it is a file-object. | |
425 pass | |
426 | |
427 display_pygraphviz(A, path=path, prog=prog, args=args) | |
428 | |
429 return path.name, A | |
430 | |
431 | |
432 def display_pygraphviz(graph, path, format=None, prog=None, args=''): | |
433 """Internal function to display a graph in OS dependent manner. | |
434 | |
435 Parameters | |
436 ---------- | |
437 graph : PyGraphviz graph | |
438 A PyGraphviz AGraph instance. | |
439 path : file object | |
440 An already opened file object that will be closed. | |
441 format : str, None | |
442 An attempt is made to guess the output format based on the extension | |
443 of the filename. If that fails, the value of `format` is used. | |
444 prog : string | |
445 Name of Graphviz layout program. | |
446 args : str | |
447 Additional arguments to pass to the Graphviz layout program. | |
448 | |
449 Notes | |
450 ----- | |
451 If this function is called in succession too quickly, sometimes the | |
452 image is not displayed. So you might consider time.sleep(.5) between | |
453 calls if you experience problems. | |
454 | |
455 """ | |
456 if format is None: | |
457 filename = path.name | |
458 format = os.path.splitext(filename)[1].lower()[1:] | |
459 if not format: | |
460 # Let the draw() function use its default | |
461 format = None | |
462 | |
463 # Save to a file and display in the default viewer. | |
464 # We must close the file before viewing it. | |
465 graph.draw(path, format, prog, args) | |
466 path.close() | |
467 nx.utils.default_opener(filename) | |
468 | |
469 | |
470 # fixture for pytest | |
471 def setup_module(module): | |
472 import pytest | |
473 pygraphviz = pytest.importorskip('pygraphviz') |