Skip to content

Commit b62105b

Browse files
committed
Use pydot.
1 parent b83a73d commit b62105b

File tree

11 files changed

+174
-149
lines changed

11 files changed

+174
-149
lines changed

.github/workflows/continuous-integration-quality-unit-tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ jobs:
5151
if: matrix.os == 'macOS-latest'
5252
run: |
5353
poetry run python -m pip install --upgrade pip
54-
poetry install --without graphviz
54+
poetry install
5555
- name: Install Package Dependencies (Ubuntu)
5656
if: matrix.os == 'ubuntu-latest'
5757
run: |
@@ -63,7 +63,7 @@ jobs:
6363
if: matrix.os == 'windows-latest'
6464
run: |
6565
poetry run python -m pip install --upgrade pip
66-
poetry install --without graphviz
66+
poetry install
6767
poetry run python -c "import imageio;imageio.plugins.freeimage.download()"
6868
shell: bash
6969
- name: Install OpenImageIO (macOs)

colour/plotting/graph.py

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
from __future__ import annotations
1111

12+
import os
13+
1214
import colour
1315
from colour.graph import (
1416
CONVERSION_GRAPH_NODE_LABELS,
@@ -29,13 +31,12 @@
2931
]
3032

3133

32-
@required("Graphviz")
34+
@required("Pydot")
3335
@required("NetworkX")
3436
def plot_automatic_colour_conversion_graph(
3537
filename: str,
36-
prog: (Literal["circo", "dot", "fdp", "neato", "nop", "twopi"] | str) = "fdp",
37-
args: str = "",
38-
) -> AGraph: # pyright: ignore # noqa: F821 # pragma: no cover
38+
prog: Literal["circo", "dot", "fdp", "neato", "nop", "twopi"] | str = "fdp",
39+
) -> Dot: # pyright: ignore # noqa: F821 # pragma: no cover
3940
"""
4041
Plot *Colour* automatic colour conversion graph using
4142
`Graphviz <https://www.graphviz.org>`__ and
@@ -47,13 +48,11 @@ def plot_automatic_colour_conversion_graph(
4748
Filename to use to save the image.
4849
prog
4950
*Graphviz* layout method.
50-
args
51-
Additional arguments for *Graphviz*.
5251
5352
Returns
5453
-------
55-
:class:`AGraph`
56-
*Pyraphviz* graph.
54+
:class:`pydot.Dot`
55+
*Pydot* graph.
5756
5857
Notes
5958
-----
@@ -87,28 +86,31 @@ def plot_automatic_colour_conversion_graph(
8786
# TODO: Investigate API to trigger the conversion graph build.
8887
describe_conversion_path("RGB", "RGB", print_callable=lambda x: x)
8988

90-
agraph = nx.nx_agraph.to_agraph(cast(nx.DiGraph, colour.graph.CONVERSION_GRAPH))
89+
dot = nx.drawing.nx_pydot.to_pydot(cast(nx.DiGraph, colour.graph.CONVERSION_GRAPH))
9190

92-
for node in agraph.nodes():
93-
node.attr.update(label=CONVERSION_GRAPH_NODE_LABELS[node.name])
91+
for node in dot.get_nodes():
92+
label = CONVERSION_GRAPH_NODE_LABELS.get(node.get_name())
9493

95-
agraph.node_attr.update(
96-
style="filled",
97-
shape="circle",
98-
color="#2196F3FF",
99-
fillcolor="#2196F370",
100-
fontname="Helvetica",
101-
fontcolor="#263238",
102-
)
103-
agraph.edge_attr.update(color="#26323870")
104-
for node in ("CIE XYZ", "RGB", "Spectral Distribution"):
105-
agraph.get_node(node.lower()).attr.update(
106-
shape="doublecircle",
107-
color="#673AB7FF",
108-
fillcolor="#673AB770",
109-
fontsize=30,
110-
)
111-
for node in (
94+
if label is None:
95+
continue
96+
97+
node.set_label(label)
98+
node.set_style("filled")
99+
node.set_shape("circle")
100+
node.set_color("#2196F3FF")
101+
node.set_fillcolor("#2196F370")
102+
node.set_fontname("Helvetica")
103+
node.set_fontcolor("#263238")
104+
105+
for name in ("CIE XYZ", "RGB", "Spectral Distribution"):
106+
node = next(iter(dot.get_node(name.lower())))
107+
108+
node.set_shape("doublecircle")
109+
node.set_color("#673AB7FF")
110+
node.set_fillcolor("#673AB770")
111+
node.set_fontsize(30)
112+
113+
for name in (
112114
"ATD95",
113115
"CAM16",
114116
"CIECAM02",
@@ -120,10 +122,16 @@ def plot_automatic_colour_conversion_graph(
120122
"RLAB",
121123
"ZCAM",
122124
):
123-
agraph.get_node(node.lower()).attr.update(
124-
color="#00BCD4FF", fillcolor="#00BCD470"
125-
)
125+
node = next(iter(dot.get_node(name.lower())))
126+
127+
node.set_color("#00BCD4FF")
128+
node.set_fillcolor("#00BCD470")
129+
130+
for edge in dot.get_edges():
131+
edge.set_color("#26323870")
126132

127-
agraph.draw(filename, prog=prog, args=args)
133+
file_format = os.path.splitext(filename)[-1][1:]
134+
write_method = getattr(dot, f"write_{file_format}")
135+
write_method(filename, prog=prog, f=file_format)
128136

129-
return agraph
137+
return dot

colour/plotting/tests/test_graph.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import tempfile
33

44
from colour.plotting import plot_automatic_colour_conversion_graph
5-
from colour.utilities import is_graphviz_installed, is_networkx_installed
5+
from colour.utilities import is_networkx_installed, is_pydot_installed
66

77
__author__ = "Colour Developers"
88
__copyright__ = "Copyright 2013 Colour Developers"
@@ -28,9 +28,7 @@ def test_plot_automatic_colour_conversion_graph(self):
2828
plot_automatic_colour_conversion_graph` definition.
2929
"""
3030

31-
if (
32-
not is_graphviz_installed() or not is_networkx_installed()
33-
): # pragma: no cover
31+
if not is_pydot_installed() or not is_networkx_installed(): # pragma: no cover
3432
return
3533

3634
plot_automatic_colour_conversion_graph( # pragma: no cover

colour/utilities/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,12 @@
3535
)
3636
from .requirements import (
3737
is_ctlrender_installed,
38-
is_graphviz_installed,
3938
is_matplotlib_installed,
4039
is_networkx_installed,
4140
is_opencolorio_installed,
4241
is_openimageio_installed,
4342
is_pandas_installed,
43+
is_pydot_installed,
4444
is_tqdm_installed,
4545
is_trimesh_installed,
4646
is_xxhash_installed,
@@ -176,12 +176,12 @@
176176
]
177177
__all__ += [
178178
"is_ctlrender_installed",
179-
"is_graphviz_installed",
180179
"is_matplotlib_installed",
181180
"is_networkx_installed",
182181
"is_opencolorio_installed",
183182
"is_openimageio_installed",
184183
"is_pandas_installed",
184+
"is_pydot_installed",
185185
"is_tqdm_installed",
186186
"is_trimesh_installed",
187187
"is_xxhash_installed",

colour/utilities/network.py

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1919,16 +1919,15 @@ def process(self, **kwargs: Dict) -> None:
19191919

19201920
node.process()
19211921

1922-
@required("Graphviz")
1923-
def to_graphviz(self) -> AGraph: # noqa: F821 # pyright: ignore
1922+
@required("Pydot")
1923+
def to_graphviz(self) -> Dot: # noqa: F821 # pyright: ignore
19241924
"""
19251925
Return a visualisation node-graph for *Graphviz*.
19261926
19271927
Returns
19281928
-------
1929-
:class:`pygraphviz.AGraph`
1930-
String representation for visualisation of the node-graph with
1931-
*Graphviz*.
1929+
:class:`pydot.Dot`
1930+
*Pydot* graph.
19321931
19331932
Examples
19341933
--------
@@ -1941,17 +1940,17 @@ def to_graphviz(self) -> AGraph: # noqa: F821 # pyright: ignore
19411940
>>> graph.add_node(node_2)
19421941
>>> node_1.connect("output", node_2, "a")
19431942
>>> graph.to_graphviz() # doctest: +SKIP
1944-
<AGraph <Swig Object of type 'Agraph_t *' at 0x...>>
1943+
<pydot.core.Dot object at 0x...>
19451944
"""
19461945

19471946
if self._parent is not None:
19481947
return PortNode.to_graphviz(self)
19491948

1950-
from pygraphviz import AGraph
1949+
import pydot
19511950

1952-
agraph = AGraph(strict=False)
1953-
agraph.graph_attr["rankdir"] = "LR"
1954-
agraph.graph_attr["splines"] = "polyline"
1951+
dot = pydot.Dot(
1952+
"digraph", graph_type="digraph", rankdir="LR", splines="polyline"
1953+
)
19551954

19561955
graphs = [node for node in self.walk_ports() if isinstance(node, PortGraph)]
19571956

@@ -1961,8 +1960,12 @@ def is_graph_member(node: PortNode) -> bool:
19611960
return any(node in graph.nodes.values() for graph in graphs)
19621961

19631962
for node in self.walk_ports():
1964-
agraph.add_node(
1965-
f"{node.name} (#{node.id})", label=node.to_graphviz(), shape="record"
1963+
dot.add_node(
1964+
pydot.Node(
1965+
f"{node.name} (#{node.id})",
1966+
label=node.to_graphviz(),
1967+
shape="record",
1968+
)
19661969
)
19671970
input_edges, output_edges = node.edges
19681971

@@ -1971,16 +1974,18 @@ def is_graph_member(node: PortNode) -> bool:
19711974
if is_graph_member(edge[0].node) or is_graph_member(edge[1].node):
19721975
continue
19731976

1974-
agraph.add_edge(
1975-
f"{edge[1].node.name} (#{edge[1].node.id})",
1976-
f"{edge[0].node.name} (#{edge[0].node.id})",
1977-
tailport=edge[1].name,
1978-
headport=edge[0].name,
1979-
key=f"{edge[1]} => {edge[0]}",
1980-
dir="forward",
1977+
dot.add_edge(
1978+
pydot.Edge(
1979+
f"{edge[1].node.name} (#{edge[1].node.id})",
1980+
f"{edge[0].node.name} (#{edge[0].node.id})",
1981+
tailport=edge[1].name,
1982+
headport=edge[0].name,
1983+
key=f"{edge[1]} => {edge[0]}",
1984+
dir="forward",
1985+
)
19811986
)
19821987

1983-
return agraph
1988+
return dot
19841989

19851990

19861991
class ExecutionPort(Port):

0 commit comments

Comments
 (0)