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