Python interface to Graphviz's Dot language for creating, reading, editing, and visualizing graphs
—
Advanced graph structuring with subgraphs and clusters for complex layouts, visual organization, and hierarchical graph representations. These features enable sophisticated graph designs with logical grouping and nested structures.
Subgraphs provide logical grouping of nodes and edges within a parent graph, enabling hierarchical organization and layout control.
class Subgraph:
def __init__(self, graph_name='', obj_dict=None, suppress_disconnected=False,
simplify=False, **attrs):
"""
Create a subgraph for logical grouping and layout control.
Parameters:
- graph_name (str): Name of the subgraph
- obj_dict (dict): Object dictionary for internal use
- suppress_disconnected (bool): Hide disconnected nodes in output
- simplify (bool): Remove redundant elements
- **attrs: Subgraph attributes
"""
def add_node(self, node):
"""Add a node to the subgraph."""
def add_edge(self, edge):
"""Add an edge to the subgraph."""
def get_nodes(self):
"""Get all nodes in the subgraph."""
def get_edges(self):
"""Get all edges in the subgraph."""Clusters are special subgraphs that provide visual grouping with borders and styling options. Cluster names must begin with 'cluster' to be recognized by Graphviz.
class Cluster:
def __init__(self, graph_name='subG', obj_dict=None, suppress_disconnected=False,
simplify=False, **attrs):
"""
Create a cluster for visual grouping with borders.
Parameters:
- graph_name (str): Name of the cluster (should start with 'cluster')
- obj_dict (dict): Object dictionary for internal use
- suppress_disconnected (bool): Hide disconnected nodes in output
- simplify (bool): Remove redundant elements
- **attrs: Cluster attributes (bgcolor, style, etc.)
"""Clusters support dynamically generated attribute methods for visual styling:
# Visual styling methods (22 attributes)
def get_bgcolor(self): """Get background color."""
def set_bgcolor(self, value): """Set background color."""
def get_color(self): """Get border color."""
def set_color(self, value): """Set border color."""
def get_style(self): """Get cluster style."""
def set_style(self, value): """Set cluster style ('filled', 'rounded', etc.)."""
def get_label(self): """Get cluster label."""
def set_label(self, value): """Set cluster label."""
def get_fontname(self): """Get font family."""
def set_fontname(self, value): """Set font family."""
def get_fontsize(self): """Get font size."""
def set_fontsize(self, value): """Set font size."""
def get_penwidth(self): """Get border line width."""
def set_penwidth(self, value): """Set border line width."""Both subgraphs and clusters integrate seamlessly with parent graphs:
# In Dot/Graph classes:
def add_subgraph(self, subgraph):
"""Add a subgraph to the parent graph."""
def get_subgraphs(self):
"""Get all subgraphs in the graph."""import pydot
# Create main graph
main_graph = pydot.Dot("hierarchical", graph_type="digraph")
# Create subgraph for input nodes
input_subgraph = pydot.Subgraph("inputs")
input_subgraph.set_rank("same") # Align nodes horizontally
input_a = pydot.Node("input_a", label="Input A", shape="ellipse")
input_b = pydot.Node("input_b", label="Input B", shape="ellipse")
input_subgraph.add_node(input_a)
input_subgraph.add_node(input_b)
# Create subgraph for processing nodes
processing_subgraph = pydot.Subgraph("processing")
processing_subgraph.set_rank("same")
process_x = pydot.Node("process_x", label="Process X", shape="box")
process_y = pydot.Node("process_y", label="Process Y", shape="box")
processing_subgraph.add_node(process_x)
processing_subgraph.add_node(process_y)
# Add subgraphs to main graph
main_graph.add_subgraph(input_subgraph)
main_graph.add_subgraph(processing_subgraph)
# Add cross-subgraph edges
main_graph.add_edge(pydot.Edge("input_a", "process_x"))
main_graph.add_edge(pydot.Edge("input_b", "process_y"))
main_graph.write_png("hierarchical_layout.png")import pydot
# Create main graph
system_graph = pydot.Dot("system_architecture", graph_type="digraph")
# Create cluster for database layer
db_cluster = pydot.Cluster("cluster_database")
db_cluster.set_label("Database Layer")
db_cluster.set_bgcolor("lightblue")
db_cluster.set_style("filled")
db_cluster.set_color("blue")
# Add database components
db_cluster.add_node(pydot.Node("mysql", label="MySQL", shape="cylinder"))
db_cluster.add_node(pydot.Node("redis", label="Redis Cache", shape="cylinder"))
# Create cluster for application layer
app_cluster = pydot.Cluster("cluster_application")
app_cluster.set_label("Application Layer")
app_cluster.set_bgcolor("lightgreen")
app_cluster.set_style("filled")
app_cluster.set_color("green")
app_cluster.add_node(pydot.Node("api", label="REST API", shape="box"))
app_cluster.add_node(pydot.Node("worker", label="Background Worker", shape="box"))
# Create cluster for presentation layer
ui_cluster = pydot.Cluster("cluster_ui")
ui_cluster.set_label("Presentation Layer")
ui_cluster.set_bgcolor("lightyellow")
ui_cluster.set_style("filled")
ui_cluster.set_color("orange")
ui_cluster.add_node(pydot.Node("web", label="Web App", shape="note"))
ui_cluster.add_node(pydot.Node("mobile", label="Mobile App", shape="note"))
# Add clusters to main graph
system_graph.add_subgraph(db_cluster)
system_graph.add_subgraph(app_cluster)
system_graph.add_subgraph(ui_cluster)
# Add inter-cluster connections
system_graph.add_edge(pydot.Edge("api", "mysql", label="queries"))
system_graph.add_edge(pydot.Edge("api", "redis", label="cache"))
system_graph.add_edge(pydot.Edge("web", "api", label="HTTP"))
system_graph.add_edge(pydot.Edge("mobile", "api", label="HTTP"))
system_graph.add_edge(pydot.Edge("worker", "mysql", label="updates"))
system_graph.set_rankdir("TB") # Top to bottom layout
system_graph.write_png("system_architecture.png")import pydot
# Create main organizational chart
org_chart = pydot.Dot("organization", graph_type="digraph")
# Engineering department cluster
engineering = pydot.Cluster("cluster_engineering")
engineering.set_label("Engineering Department")
engineering.set_bgcolor("lightcyan")
engineering.set_style("filled")
# Frontend team subgraph within engineering
frontend_team = pydot.Subgraph("frontend_team")
frontend_team.set_rank("same")
frontend_team.add_node(pydot.Node("fe_lead", label="Frontend Lead", shape="box"))
frontend_team.add_node(pydot.Node("fe_dev1", label="Frontend Dev 1", shape="ellipse"))
frontend_team.add_node(pydot.Node("fe_dev2", label="Frontend Dev 2", shape="ellipse"))
# Backend team subgraph within engineering
backend_team = pydot.Subgraph("backend_team")
backend_team.set_rank("same")
backend_team.add_node(pydot.Node("be_lead", label="Backend Lead", shape="box"))
backend_team.add_node(pydot.Node("be_dev1", label="Backend Dev 1", shape="ellipse"))
backend_team.add_node(pydot.Node("be_dev2", label="Backend Dev 2", shape="ellipse"))
# Add teams to engineering cluster
engineering.add_subgraph(frontend_team)
engineering.add_subgraph(backend_team)
# Add engineering head
engineering.add_node(pydot.Node("eng_director", label="Engineering Director",
shape="doubleoctagon", style="filled", fillcolor="gold"))
# Add to main chart
org_chart.add_subgraph(engineering)
# Add reporting relationships
org_chart.add_edge(pydot.Edge("eng_director", "fe_lead", style="dashed"))
org_chart.add_edge(pydot.Edge("eng_director", "be_lead", style="dashed"))
org_chart.add_edge(pydot.Edge("fe_lead", "fe_dev1"))
org_chart.add_edge(pydot.Edge("fe_lead", "fe_dev2"))
org_chart.add_edge(pydot.Edge("be_lead", "be_dev1"))
org_chart.add_edge(pydot.Edge("be_lead", "be_dev2"))
org_chart.write_png("org_chart.png")import pydot
# Create process flow with controlled layout
process_flow = pydot.Dot("process", graph_type="digraph")
# Group parallel processes
parallel_processes = pydot.Subgraph("parallel")
parallel_processes.set_rank("same") # Align horizontally
parallel_processes.add_node(pydot.Node("task_a", label="Task A"))
parallel_processes.add_node(pydot.Node("task_b", label="Task B"))
parallel_processes.add_node(pydot.Node("task_c", label="Task C"))
process_flow.add_subgraph(parallel_processes)
# Add sequential nodes
process_flow.add_node(pydot.Node("start", label="Start", shape="ellipse"))
process_flow.add_node(pydot.Node("merge", label="Merge Results", shape="diamond"))
process_flow.add_node(pydot.Node("end", label="End", shape="ellipse"))
# Connect the flow
process_flow.add_edge(pydot.Edge("start", "task_a"))
process_flow.add_edge(pydot.Edge("start", "task_b"))
process_flow.add_edge(pydot.Edge("start", "task_c"))
process_flow.add_edge(pydot.Edge("task_a", "merge"))
process_flow.add_edge(pydot.Edge("task_b", "merge"))
process_flow.add_edge(pydot.Edge("task_c", "merge"))
process_flow.add_edge(pydot.Edge("merge", "end"))
process_flow.write_png("process_flow.png")Install with Tessl CLI
npx tessl i tessl/pypi-pydot