0
# Subgraphs and Clusters
1
2
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.
3
4
## Capabilities
5
6
### Subgraph Creation
7
8
Subgraphs provide logical grouping of nodes and edges within a parent graph, enabling hierarchical organization and layout control.
9
10
```python { .api }
11
class Subgraph:
12
def __init__(self, graph_name='', obj_dict=None, suppress_disconnected=False,
13
simplify=False, **attrs):
14
"""
15
Create a subgraph for logical grouping and layout control.
16
17
Parameters:
18
- graph_name (str): Name of the subgraph
19
- obj_dict (dict): Object dictionary for internal use
20
- suppress_disconnected (bool): Hide disconnected nodes in output
21
- simplify (bool): Remove redundant elements
22
- **attrs: Subgraph attributes
23
"""
24
25
def add_node(self, node):
26
"""Add a node to the subgraph."""
27
28
def add_edge(self, edge):
29
"""Add an edge to the subgraph."""
30
31
def get_nodes(self):
32
"""Get all nodes in the subgraph."""
33
34
def get_edges(self):
35
"""Get all edges in the subgraph."""
36
```
37
38
### Cluster Creation
39
40
Clusters are special subgraphs that provide visual grouping with borders and styling options. Cluster names must begin with 'cluster' to be recognized by Graphviz.
41
42
```python { .api }
43
class Cluster:
44
def __init__(self, graph_name='subG', obj_dict=None, suppress_disconnected=False,
45
simplify=False, **attrs):
46
"""
47
Create a cluster for visual grouping with borders.
48
49
Parameters:
50
- graph_name (str): Name of the cluster (should start with 'cluster')
51
- obj_dict (dict): Object dictionary for internal use
52
- suppress_disconnected (bool): Hide disconnected nodes in output
53
- simplify (bool): Remove redundant elements
54
- **attrs: Cluster attributes (bgcolor, style, etc.)
55
"""
56
```
57
58
### Cluster Attribute Methods
59
60
Clusters support dynamically generated attribute methods for visual styling:
61
62
```python { .api }
63
# Visual styling methods (22 attributes)
64
def get_bgcolor(self): """Get background color."""
65
def set_bgcolor(self, value): """Set background color."""
66
67
def get_color(self): """Get border color."""
68
def set_color(self, value): """Set border color."""
69
70
def get_style(self): """Get cluster style."""
71
def set_style(self, value): """Set cluster style ('filled', 'rounded', etc.)."""
72
73
def get_label(self): """Get cluster label."""
74
def set_label(self, value): """Set cluster label."""
75
76
def get_fontname(self): """Get font family."""
77
def set_fontname(self, value): """Set font family."""
78
79
def get_fontsize(self): """Get font size."""
80
def set_fontsize(self, value): """Set font size."""
81
82
def get_penwidth(self): """Get border line width."""
83
def set_penwidth(self, value): """Set border line width."""
84
```
85
86
### Integration with Parent Graphs
87
88
Both subgraphs and clusters integrate seamlessly with parent graphs:
89
90
```python { .api }
91
# In Dot/Graph classes:
92
def add_subgraph(self, subgraph):
93
"""Add a subgraph to the parent graph."""
94
95
def get_subgraphs(self):
96
"""Get all subgraphs in the graph."""
97
```
98
99
## Usage Examples
100
101
### Creating Subgraphs for Layout Control
102
103
```python
104
import pydot
105
106
# Create main graph
107
main_graph = pydot.Dot("hierarchical", graph_type="digraph")
108
109
# Create subgraph for input nodes
110
input_subgraph = pydot.Subgraph("inputs")
111
input_subgraph.set_rank("same") # Align nodes horizontally
112
113
input_a = pydot.Node("input_a", label="Input A", shape="ellipse")
114
input_b = pydot.Node("input_b", label="Input B", shape="ellipse")
115
input_subgraph.add_node(input_a)
116
input_subgraph.add_node(input_b)
117
118
# Create subgraph for processing nodes
119
processing_subgraph = pydot.Subgraph("processing")
120
processing_subgraph.set_rank("same")
121
122
process_x = pydot.Node("process_x", label="Process X", shape="box")
123
process_y = pydot.Node("process_y", label="Process Y", shape="box")
124
processing_subgraph.add_node(process_x)
125
processing_subgraph.add_node(process_y)
126
127
# Add subgraphs to main graph
128
main_graph.add_subgraph(input_subgraph)
129
main_graph.add_subgraph(processing_subgraph)
130
131
# Add cross-subgraph edges
132
main_graph.add_edge(pydot.Edge("input_a", "process_x"))
133
main_graph.add_edge(pydot.Edge("input_b", "process_y"))
134
135
main_graph.write_png("hierarchical_layout.png")
136
```
137
138
### Creating Visual Clusters
139
140
```python
141
import pydot
142
143
# Create main graph
144
system_graph = pydot.Dot("system_architecture", graph_type="digraph")
145
146
# Create cluster for database layer
147
db_cluster = pydot.Cluster("cluster_database")
148
db_cluster.set_label("Database Layer")
149
db_cluster.set_bgcolor("lightblue")
150
db_cluster.set_style("filled")
151
db_cluster.set_color("blue")
152
153
# Add database components
154
db_cluster.add_node(pydot.Node("mysql", label="MySQL", shape="cylinder"))
155
db_cluster.add_node(pydot.Node("redis", label="Redis Cache", shape="cylinder"))
156
157
# Create cluster for application layer
158
app_cluster = pydot.Cluster("cluster_application")
159
app_cluster.set_label("Application Layer")
160
app_cluster.set_bgcolor("lightgreen")
161
app_cluster.set_style("filled")
162
app_cluster.set_color("green")
163
164
app_cluster.add_node(pydot.Node("api", label="REST API", shape="box"))
165
app_cluster.add_node(pydot.Node("worker", label="Background Worker", shape="box"))
166
167
# Create cluster for presentation layer
168
ui_cluster = pydot.Cluster("cluster_ui")
169
ui_cluster.set_label("Presentation Layer")
170
ui_cluster.set_bgcolor("lightyellow")
171
ui_cluster.set_style("filled")
172
ui_cluster.set_color("orange")
173
174
ui_cluster.add_node(pydot.Node("web", label="Web App", shape="note"))
175
ui_cluster.add_node(pydot.Node("mobile", label="Mobile App", shape="note"))
176
177
# Add clusters to main graph
178
system_graph.add_subgraph(db_cluster)
179
system_graph.add_subgraph(app_cluster)
180
system_graph.add_subgraph(ui_cluster)
181
182
# Add inter-cluster connections
183
system_graph.add_edge(pydot.Edge("api", "mysql", label="queries"))
184
system_graph.add_edge(pydot.Edge("api", "redis", label="cache"))
185
system_graph.add_edge(pydot.Edge("web", "api", label="HTTP"))
186
system_graph.add_edge(pydot.Edge("mobile", "api", label="HTTP"))
187
system_graph.add_edge(pydot.Edge("worker", "mysql", label="updates"))
188
189
system_graph.set_rankdir("TB") # Top to bottom layout
190
system_graph.write_png("system_architecture.png")
191
```
192
193
### Nested Subgraphs
194
195
```python
196
import pydot
197
198
# Create main organizational chart
199
org_chart = pydot.Dot("organization", graph_type="digraph")
200
201
# Engineering department cluster
202
engineering = pydot.Cluster("cluster_engineering")
203
engineering.set_label("Engineering Department")
204
engineering.set_bgcolor("lightcyan")
205
engineering.set_style("filled")
206
207
# Frontend team subgraph within engineering
208
frontend_team = pydot.Subgraph("frontend_team")
209
frontend_team.set_rank("same")
210
frontend_team.add_node(pydot.Node("fe_lead", label="Frontend Lead", shape="box"))
211
frontend_team.add_node(pydot.Node("fe_dev1", label="Frontend Dev 1", shape="ellipse"))
212
frontend_team.add_node(pydot.Node("fe_dev2", label="Frontend Dev 2", shape="ellipse"))
213
214
# Backend team subgraph within engineering
215
backend_team = pydot.Subgraph("backend_team")
216
backend_team.set_rank("same")
217
backend_team.add_node(pydot.Node("be_lead", label="Backend Lead", shape="box"))
218
backend_team.add_node(pydot.Node("be_dev1", label="Backend Dev 1", shape="ellipse"))
219
backend_team.add_node(pydot.Node("be_dev2", label="Backend Dev 2", shape="ellipse"))
220
221
# Add teams to engineering cluster
222
engineering.add_subgraph(frontend_team)
223
engineering.add_subgraph(backend_team)
224
225
# Add engineering head
226
engineering.add_node(pydot.Node("eng_director", label="Engineering Director",
227
shape="doubleoctagon", style="filled", fillcolor="gold"))
228
229
# Add to main chart
230
org_chart.add_subgraph(engineering)
231
232
# Add reporting relationships
233
org_chart.add_edge(pydot.Edge("eng_director", "fe_lead", style="dashed"))
234
org_chart.add_edge(pydot.Edge("eng_director", "be_lead", style="dashed"))
235
org_chart.add_edge(pydot.Edge("fe_lead", "fe_dev1"))
236
org_chart.add_edge(pydot.Edge("fe_lead", "fe_dev2"))
237
org_chart.add_edge(pydot.Edge("be_lead", "be_dev1"))
238
org_chart.add_edge(pydot.Edge("be_lead", "be_dev2"))
239
240
org_chart.write_png("org_chart.png")
241
```
242
243
### Subgraph Layout Control
244
245
```python
246
import pydot
247
248
# Create process flow with controlled layout
249
process_flow = pydot.Dot("process", graph_type="digraph")
250
251
# Group parallel processes
252
parallel_processes = pydot.Subgraph("parallel")
253
parallel_processes.set_rank("same") # Align horizontally
254
255
parallel_processes.add_node(pydot.Node("task_a", label="Task A"))
256
parallel_processes.add_node(pydot.Node("task_b", label="Task B"))
257
parallel_processes.add_node(pydot.Node("task_c", label="Task C"))
258
259
process_flow.add_subgraph(parallel_processes)
260
261
# Add sequential nodes
262
process_flow.add_node(pydot.Node("start", label="Start", shape="ellipse"))
263
process_flow.add_node(pydot.Node("merge", label="Merge Results", shape="diamond"))
264
process_flow.add_node(pydot.Node("end", label="End", shape="ellipse"))
265
266
# Connect the flow
267
process_flow.add_edge(pydot.Edge("start", "task_a"))
268
process_flow.add_edge(pydot.Edge("start", "task_b"))
269
process_flow.add_edge(pydot.Edge("start", "task_c"))
270
process_flow.add_edge(pydot.Edge("task_a", "merge"))
271
process_flow.add_edge(pydot.Edge("task_b", "merge"))
272
process_flow.add_edge(pydot.Edge("task_c", "merge"))
273
process_flow.add_edge(pydot.Edge("merge", "end"))
274
275
process_flow.write_png("process_flow.png")
276
```