Skip to content

Commit

Permalink
Use pydot.
Browse files Browse the repository at this point in the history
  • Loading branch information
KelSolaar committed Oct 8, 2024
1 parent b83a73d commit b62105b
Show file tree
Hide file tree
Showing 11 changed files with 174 additions and 149 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
if: matrix.os == 'macOS-latest'
run: |
poetry run python -m pip install --upgrade pip
poetry install --without graphviz
poetry install
- name: Install Package Dependencies (Ubuntu)
if: matrix.os == 'ubuntu-latest'
run: |
Expand All @@ -63,7 +63,7 @@ jobs:
if: matrix.os == 'windows-latest'
run: |
poetry run python -m pip install --upgrade pip
poetry install --without graphviz
poetry install
poetry run python -c "import imageio;imageio.plugins.freeimage.download()"
shell: bash
- name: Install OpenImageIO (macOs)
Expand Down
74 changes: 41 additions & 33 deletions colour/plotting/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

from __future__ import annotations

import os

import colour
from colour.graph import (
CONVERSION_GRAPH_NODE_LABELS,
Expand All @@ -29,13 +31,12 @@
]


@required("Graphviz")
@required("Pydot")
@required("NetworkX")
def plot_automatic_colour_conversion_graph(
filename: str,
prog: (Literal["circo", "dot", "fdp", "neato", "nop", "twopi"] | str) = "fdp",
args: str = "",
) -> AGraph: # pyright: ignore # noqa: F821 # pragma: no cover
prog: Literal["circo", "dot", "fdp", "neato", "nop", "twopi"] | str = "fdp",
) -> Dot: # pyright: ignore # noqa: F821 # pragma: no cover
"""
Plot *Colour* automatic colour conversion graph using
`Graphviz <https://www.graphviz.org>`__ and
Expand All @@ -47,13 +48,11 @@ def plot_automatic_colour_conversion_graph(
Filename to use to save the image.
prog
*Graphviz* layout method.
args
Additional arguments for *Graphviz*.
Returns
-------
:class:`AGraph`
*Pyraphviz* graph.
:class:`pydot.Dot`
*Pydot* graph.
Notes
-----
Expand Down Expand Up @@ -87,28 +86,31 @@ def plot_automatic_colour_conversion_graph(
# TODO: Investigate API to trigger the conversion graph build.
describe_conversion_path("RGB", "RGB", print_callable=lambda x: x)

agraph = nx.nx_agraph.to_agraph(cast(nx.DiGraph, colour.graph.CONVERSION_GRAPH))
dot = nx.drawing.nx_pydot.to_pydot(cast(nx.DiGraph, colour.graph.CONVERSION_GRAPH))

for node in agraph.nodes():
node.attr.update(label=CONVERSION_GRAPH_NODE_LABELS[node.name])
for node in dot.get_nodes():
label = CONVERSION_GRAPH_NODE_LABELS.get(node.get_name())

agraph.node_attr.update(
style="filled",
shape="circle",
color="#2196F3FF",
fillcolor="#2196F370",
fontname="Helvetica",
fontcolor="#263238",
)
agraph.edge_attr.update(color="#26323870")
for node in ("CIE XYZ", "RGB", "Spectral Distribution"):
agraph.get_node(node.lower()).attr.update(
shape="doublecircle",
color="#673AB7FF",
fillcolor="#673AB770",
fontsize=30,
)
for node in (
if label is None:
continue

node.set_label(label)
node.set_style("filled")
node.set_shape("circle")
node.set_color("#2196F3FF")
node.set_fillcolor("#2196F370")
node.set_fontname("Helvetica")
node.set_fontcolor("#263238")

for name in ("CIE XYZ", "RGB", "Spectral Distribution"):
node = next(iter(dot.get_node(name.lower())))

node.set_shape("doublecircle")
node.set_color("#673AB7FF")
node.set_fillcolor("#673AB770")
node.set_fontsize(30)

for name in (
"ATD95",
"CAM16",
"CIECAM02",
Expand All @@ -120,10 +122,16 @@ def plot_automatic_colour_conversion_graph(
"RLAB",
"ZCAM",
):
agraph.get_node(node.lower()).attr.update(
color="#00BCD4FF", fillcolor="#00BCD470"
)
node = next(iter(dot.get_node(name.lower())))

node.set_color("#00BCD4FF")
node.set_fillcolor("#00BCD470")

for edge in dot.get_edges():
edge.set_color("#26323870")

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

return agraph
return dot
6 changes: 2 additions & 4 deletions colour/plotting/tests/test_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import tempfile

from colour.plotting import plot_automatic_colour_conversion_graph
from colour.utilities import is_graphviz_installed, is_networkx_installed
from colour.utilities import is_networkx_installed, is_pydot_installed

__author__ = "Colour Developers"
__copyright__ = "Copyright 2013 Colour Developers"
Expand All @@ -28,9 +28,7 @@ def test_plot_automatic_colour_conversion_graph(self):
plot_automatic_colour_conversion_graph` definition.
"""

if (
not is_graphviz_installed() or not is_networkx_installed()
): # pragma: no cover
if not is_pydot_installed() or not is_networkx_installed(): # pragma: no cover
return

plot_automatic_colour_conversion_graph( # pragma: no cover
Expand Down
4 changes: 2 additions & 2 deletions colour/utilities/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@
)
from .requirements import (
is_ctlrender_installed,
is_graphviz_installed,
is_matplotlib_installed,
is_networkx_installed,
is_opencolorio_installed,
is_openimageio_installed,
is_pandas_installed,
is_pydot_installed,
is_tqdm_installed,
is_trimesh_installed,
is_xxhash_installed,
Expand Down Expand Up @@ -176,12 +176,12 @@
]
__all__ += [
"is_ctlrender_installed",
"is_graphviz_installed",
"is_matplotlib_installed",
"is_networkx_installed",
"is_opencolorio_installed",
"is_openimageio_installed",
"is_pandas_installed",
"is_pydot_installed",
"is_tqdm_installed",
"is_trimesh_installed",
"is_xxhash_installed",
Expand Down
45 changes: 25 additions & 20 deletions colour/utilities/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -1919,16 +1919,15 @@ def process(self, **kwargs: Dict) -> None:

node.process()

@required("Graphviz")
def to_graphviz(self) -> AGraph: # noqa: F821 # pyright: ignore
@required("Pydot")
def to_graphviz(self) -> Dot: # noqa: F821 # pyright: ignore
"""
Return a visualisation node-graph for *Graphviz*.
Returns
-------
:class:`pygraphviz.AGraph`
String representation for visualisation of the node-graph with
*Graphviz*.
:class:`pydot.Dot`
*Pydot* graph.
Examples
--------
Expand All @@ -1941,17 +1940,17 @@ def to_graphviz(self) -> AGraph: # noqa: F821 # pyright: ignore
>>> graph.add_node(node_2)
>>> node_1.connect("output", node_2, "a")
>>> graph.to_graphviz() # doctest: +SKIP
<AGraph <Swig Object of type 'Agraph_t *' at 0x...>>
<pydot.core.Dot object at 0x...>
"""

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

from pygraphviz import AGraph
import pydot

agraph = AGraph(strict=False)
agraph.graph_attr["rankdir"] = "LR"
agraph.graph_attr["splines"] = "polyline"
dot = pydot.Dot(
"digraph", graph_type="digraph", rankdir="LR", splines="polyline"
)

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

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

for node in self.walk_ports():
agraph.add_node(
f"{node.name} (#{node.id})", label=node.to_graphviz(), shape="record"
dot.add_node(
pydot.Node(
f"{node.name} (#{node.id})",
label=node.to_graphviz(),
shape="record",
)
)
input_edges, output_edges = node.edges

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

agraph.add_edge(
f"{edge[1].node.name} (#{edge[1].node.id})",
f"{edge[0].node.name} (#{edge[0].node.id})",
tailport=edge[1].name,
headport=edge[0].name,
key=f"{edge[1]} => {edge[0]}",
dir="forward",
dot.add_edge(
pydot.Edge(
f"{edge[1].node.name} (#{edge[1].node.id})",
f"{edge[0].node.name} (#{edge[0].node.id})",
tailport=edge[1].name,
headport=edge[0].name,
key=f"{edge[1]} => {edge[0]}",
dir="forward",
)
)

return agraph
return dot


class ExecutionPort(Port):
Expand Down
Loading

0 comments on commit b62105b

Please sign in to comment.