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