Visualizer¶
Overview¶
The visualizer shows a structure in four forms: plain text for a terminal or a doctest, Mermaid for documentation pages, DOT for Graphviz, and an image file. It is a set of free functions: presentation stays out of the structure classes, which keep no drawing code and no dependency.
Function |
Produces |
Accepts |
Dependency |
|---|---|---|---|
Plain text, one line or one node per line |
every supported structure |
none |
|
Mermaid flowchart source |
every supported structure |
none |
|
Graphviz DOT source |
graphs only |
none (Graphviz only to render the source) |
|
One of the three text formats, chosen by name |
as the chosen format |
none |
|
PNG, SVG, PDF or any format matplotlib writes |
every supported structure |
|
graph LR
S[structure] --> V[view<br/>nodes, edges, labels]
V --> T[to_text]
V --> M[to_mermaid]
V --> D[to_dot]
V --> L[layout] --> I[save_image]
style V fill:#3498db,color:#fff
Every function first reads the structure into the same intermediate view, through public accessors only, then writes that view in its format. Two formats of the same structure therefore always show the same nodes in the same order.
Note
Output is deterministic. Linear structures are read in iteration order, trees from the root, graphs in node and edge insertion order. The same structure always renders to the same text, which makes the output safe to compare in tests and to commit in documentation.
Supported Structures¶
Family |
Classes |
Rendered as |
|---|---|---|
Linear |
|
A chain, with |
Binary trees |
|
A tree whose children are tagged |
General trees |
|
A tree, children in insertion order |
B-trees |
|
A tree of nodes, each showing its keys |
Graphs |
every graph of |
Nodes and edges, with direction and weights |
Heaps, tries, segment trees and the structures of sds.advanced and
sds.probabilistic are not supported yet: passing one raises
StructureTypeError.
Labels¶
Every function takes an optional keyword argument label: a callable
that returns the text to show for one element. What it receives depends on
the structure:
Structure |
|
Default |
|---|---|---|
Linear structures |
each item |
|
Binary and general trees, graphs |
each node |
|
B-trees |
each key |
|
# Show the node ids of a graph instead of their data
print(to_text(graph, label=lambda node: node.id))
Formats escape labels themselves: a label may hold quotes, pipes or line breaks without breaking the Mermaid or DOT source.
Text Formats¶
to_text¶
- sds.utils.visualizer.to_text(structure, *, label=None)[source]¶
Render a structure as plain text.
Linear structures render on one line, with named ends where they have a meaning (
topof a stack,front/rearof a queue). Trees render one node per line with box-drawing branches; children of a binary tree are taggedL/R. Graphs render as an adjacency list in insertion order, with->for directed and--for undirected graphs, and the weight of each edge in parentheses.- Parameters:
structure (Any) – A supported structure (see the module documentation).
label (Callable[[Any], str], optional) – Labelling function; see the module documentation.
- Returns:
The rendering,
(empty)for an empty structure.- Return type:
- Raises:
StructureTypeError – If the structure is not supported.
Examples
>>> from sds.linear import Stack >>> stack = Stack() >>> for item in (1, 2, 3): ... stack.push(item) >>> print(to_text(stack)) top: 3 -> 2 -> 1
>>> from sds.tree import BinarySearchTree >>> tree = BinarySearchTree() >>> for key in (50, 30, 70, 20, 40): ... tree.insert(key) >>> print(to_text(tree)) 50 ├── L: 30 │ ├── L: 20 │ └── R: 40 └── R: 70
The three families render as follows. A doubly linked list shows its two-way links:
1 <-> 2 <-> 3
A general tree uses box-drawing branches:
root
├── src
│ └── main.py
└── README
A B-tree of minimum degree 2 after inserting 1 to 7 shows the keys of each node:
[2 | 4]
├── [1]
├── [3]
└── [5 | 6 | 7]
A weighted directed graph is an adjacency list, one line per node:
A -> B (4.0), C (1.0)
B
C -> B (2.0)
to_mermaid¶
- sds.utils.visualizer.to_mermaid(structure, *, label=None)[source]¶
Render a structure as a Mermaid flowchart.
Linear structures and graphs are drawn left to right (
flowchart LR), trees top-down (flowchart TD). Directed graphs use-->, undirected graphs---; weights and the L/R side of binary-tree children become edge labels. Named ends of stacks and queues become rounded nodes. Node ids are generated (n0,n1…) and labels are escaped, so any label is safe to embed in a Markdown or reST page.- Parameters:
structure (Any) – A supported structure (see the module documentation).
label (Callable[[Any], str], optional) – Labelling function; see the module documentation.
- Returns:
The Mermaid source, without the surrounding code fence. An empty structure gives the
flowchartline alone.- Return type:
- Raises:
StructureTypeError – If the structure is not supported.
Examples
>>> from sds.tree import BinarySearchTree >>> tree = BinarySearchTree() >>> for key in (2, 1, 3): ... tree.insert(key) >>> print(to_mermaid(tree)) flowchart TD n0["2"] n1["1"] n0 -->|L| n1 n2["3"] n0 -->|R| n2
The source can be pasted into any Mermaid renderer, including the
.. mermaid:: directive of this documentation. The weighted directed
graph above gives:
flowchart LR
n0["A"]
n1["B"]
n2["C"]
n0 -->|4.0| n1
n0 -->|1.0| n2
n2 -->|2.0| n1
which renders as:
flowchart LR
n0["A"]
n1["B"]
n2["C"]
n0 -->|4.0| n1
n0 -->|1.0| n2
n2 -->|2.0| n1
to_dot¶
- sds.utils.visualizer.to_dot(graph, *, label=None)[source]¶
Render a graph as Graphviz DOT source.
Directed graphs become a
digraphwith->edges, the others agraphwith--edges. Nodes are identified by theiridand labelled withlabel; weights become edge labels. Producing the source needs no Graphviz install; rendering it does.- Parameters:
graph (AbstractGraph) – The graph to render.
label (Callable[[Any], str], optional) – Labelling function; it receives each node.
- Returns:
The DOT source.
- Return type:
- Raises:
StructureTypeError – If
graphis not a graph: DOT is offered for graphs only.
Examples
>>> from sds.graph import DirectedEdge, DirectedGraph, GraphNode >>> graph = DirectedGraph() >>> a, b = GraphNode("A", "n1"), GraphNode("B", "n2") >>> graph.add_node(a) >>> graph.add_node(b) >>> graph.add_edge(DirectedEdge(a, b)) >>> print(to_dot(graph)) digraph { "n1" [label="A"]; "n2" [label="B"]; "n1" -> "n2"; }
The DOT source is plain text; turning it into an image is Graphviz’s job,
for instance dot -Tpng graph.dot -o graph.png. The same graph gives:
digraph {
"A" [label="A"];
"B" [label="B"];
"C" [label="C"];
"A" -> "B" [label="4.0"];
"A" -> "C" [label="1.0"];
"C" -> "B" [label="2.0"];
}
render¶
- sds.utils.visualizer.render(structure, fmt='text', *, label=None)[source]¶
Render a structure in one of the text formats.
Dispatches to
to_text(),to_mermaid()orto_dot(); the command line uses it to honour its format option.- Parameters:
- Returns:
The rendering.
- Return type:
- Raises:
InvalidParameterError – If
fmtis not one ofFORMATS.StructureTypeError – If the structure is not supported, or
fmtis"dot"and the structure is not a graph.
Examples
>>> from sds.linear import Queue >>> queue = Queue() >>> for item in (1, 2): ... queue.enqueue(item) >>> print(render(queue)) front: 1 -> 2 :rear >>> render(queue, "svg") Traceback (most recent call last): ... sds.core.exceptions.InvalidParameterError: Unknown format 'svg': use one of text, mermaid, dot
FORMATS is the tuple ("text", "mermaid", "dot").
Image Output¶
save_image¶
- sds.utils.visualizer.save_image(structure, path, *, label=None)[source]¶
Draw a structure and save the image to
path.Needs the
imageextra:pip install "pysds-tools[image]". The format follows the file extension (.png,.svg,.pdf, or any other format matplotlib writes). Nodes are placed by the visualiser: a row for linear structures, in-order columns for binary trees, children-centred parents for other trees, a circle for graphs.- Parameters:
structure (Any) – A supported structure (see the module documentation).
path (str or os.PathLike) – Destination file; its extension selects the format.
label (Callable[[Any], str], optional) – Labelling function; see the module documentation.
- Raises:
StructureTypeError – If the structure is not supported.
OptionalDependencyError – If matplotlib is not installed.
InvalidParameterError – If the extension names no format matplotlib can write.
Notes
The figure is built with matplotlib’s object API: no window opens and the global pyplot state is not touched.
The visualizer places the nodes itself, so the drawing needs nothing but matplotlib:
Structure |
Layout |
|---|---|
Linear structures |
One row, left to right |
Binary trees |
One column per node in in-order position, one row per level |
General trees and B-trees |
Each parent centred above its children |
Graphs |
Nodes on a circle, from the top, clockwise in insertion order; two opposite edges are bent so both stay visible |
Usage Example: Documenting a Graph¶
from sds.graph import GraphNode, WeightedDirectedEdge, WeightedDirectedGraph
from sds.utils.visualizer import render, save_image, to_text
a, b, c = (GraphNode(name, name) for name in "ABC")
graph = WeightedDirectedGraph()
for node in (a, b, c):
graph.add_node(node)
graph.add_edge(WeightedDirectedEdge(a, b, 4))
graph.add_edge(WeightedDirectedEdge(a, c, 1))
graph.add_edge(WeightedDirectedEdge(c, b, 2))
print(to_text(graph)) # quick look in a terminal
with open("graph.mmd", "w") as stream: # for a Markdown page
stream.write(render(graph, "mermaid"))
save_image(graph, "graph.svg") # needs the image extra
Best Practices¶
✅ Use to_text in doctests and assertions: its output is short,
deterministic and dependency-free.
✅ Use to_mermaid for documentation pages, and to_dot when Graphviz
is already part of the toolchain or the graph is large.
✅ Pass a label callable rather than changing the data stored in the
structure to get a readable drawing.
❌ Do not call to_dot on a tree or a list: DOT output is offered for
graphs only and raises StructureTypeError otherwise.
❌ Do not import matplotlib.pyplot just to save an image:
save_image builds its figure with matplotlib’s object API and leaves
the pyplot state untouched.
References¶
Mermaid, Flowchart syntax — https://mermaid.js.org/syntax/flowchart.html
Graphviz, The DOT Language — https://graphviz.org/doc/info/lang.html
matplotlib, Figure.savefig (output formats) — https://matplotlib.org/stable/api/_as_gen/matplotlib.figure.Figure.savefig.html