Coverage for src/mlstatpy/graph/graphviz_helper.py: 90%
62 statements
« prev ^ index » next coverage.py v7.1.0, created at 2023-02-27 05:59 +0100
« prev ^ index » next coverage.py v7.1.0, created at 2023-02-27 05:59 +0100
1"""
2@file
3@brief graphviz helper
4"""
5import os
6import sys
7from pyquickhelper.loghelper import run_cmd
8from pyquickhelper.helpgen.conf_path_tools import find_graphviz_dot
11def run_graphviz(filename, image, engine="dot"):
12 """
13 Run :epkg:`GraphViz`.
15 @param filename filename which contains the graph definition
16 @param image output image
17 @param engine *dot* or *neato*
18 @return output of graphviz
19 """
20 ext = os.path.splitext(image)[-1]
21 if ext != ".png":
22 raise RuntimeError("extension should be .png not " + str(ext))
23 if sys.platform.startswith("win"):
24 bin_ = os.path.dirname(find_graphviz_dot())
25 # if bin not in os.environ["PATH"]:
26 # os.environ["PATH"] = os.environ["PATH"] + ";" + bin
27 cmd = f'"{bin_}\\{engine}" -Tpng "{filename}" -o "{image}"'
28 else:
29 cmd = f'"{engine}" -Tpng "{filename}" -o "{image}"'
30 out, err = run_cmd(cmd, wait=True)
31 if len(err) > 0:
32 raise RuntimeError(
33 f"Unable to run Graphviz\nCMD:\n{cmd}\nOUT:\n{out}\nERR:\n{err}")
34 return out
37def edges2gv(vertices, edges):
38 """
39 Converts a graph into a :epkg:`GraphViz` file format.
41 @param edges see below
42 @param vertices see below
43 @return gv format
45 The function creates a file ``<image>.gv``.
47 .. runpython::
48 :showcode:
50 from mlstatpy.graph.graphviz_helper import edges2gv
51 gv = edges2gv([(1, "eee", "red")],
52 [(1, 2, "blue"), (3, 4), (1, 3)])
53 print(gv)
55 """
56 memovertex = {}
57 for v in vertices:
58 if isinstance(v, tuple):
59 if len(v) == 1:
60 memovertex[v[0]] = None
61 else:
62 memovertex[v[0]] = v[1:]
63 else:
64 memovertex[v] = None
65 for edge in edges:
66 i, j = edge[:2]
67 if i not in memovertex:
68 memovertex[i] = None
69 if j not in memovertex:
70 memovertex[j] = None
72 li = ["digraph{"]
73 for k, v in memovertex.items():
74 if v is None:
75 li.append(f"{k} ;")
76 elif len(v) == 1:
77 li.append(f"\"{k}\" [label=\"{v[0]}\"];")
78 elif len(v) == 2:
79 li.append(
80 f"\"{k}\" [label=\"{v[0]}\",fillcolor={v[1]},color={v[1]}];")
81 else:
82 raise ValueError("unable to understand " + str(v))
84 for edge in edges:
85 i, j = edge[:2]
86 if len(edge) == 2:
87 li.append(f"\"{i}\" -> \"{j}\";")
88 elif len(edge) == 3:
89 li.append(f"\"{i}\" -> \"{j}\" [label=\"{edge[2]}\"];")
90 elif len(edge) == 4:
91 li.append(
92 f"\"{i}\" -> \"{j}\" [label=\"{edge[2]}\",color={edge[3]}];")
93 else:
94 raise ValueError("unable to understand " + str(edge))
95 li.append("}")
97 text = "\n".join(li)
98 return text
101def draw_graph_graphviz(vertices, edges, image=None, engine="dot"):
102 """
103 Draws a graph using :epkg:`Graphviz`.
105 @param edges see below
106 @param vertices see below
107 @param image output image, None, just returns the output
108 @param engine *dot* or *neato*
109 @return :epkg:`Graphviz` output or
110 the dot text if *image* is None
112 The function creates a file ``<image>.gv`` if *image* is not None.
113 ::
115 edges = [ (1,2, label, color), (3,4), (1,3), ... ] , liste d'arcs
116 vertices = [ (1, label, color), (2), ... ] , liste de noeuds
117 image = nom d'image (format png)
119 """
120 text = edges2gv(vertices, edges)
121 if image is None:
122 return text
123 filename = image + ".gv"
124 with open(filename, "w", encoding="utf-8") as f:
125 f.write(text)
127 out = run_graphviz(filename, image, engine=engine)
128 if not os.path.exists(image):
129 raise FileNotFoundError(
130 f"GraphViz failed with no reason. '{image}' not found.")
131 return out