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')