关于 graphviz-diagrams
使用 Graphviz DOT 制作复杂图表的技能,适用于需要精确布局的结构可视化。输出同时包含可编辑源码和渲染图像。
主要功能
- 01使用 DOT 创建图表
- 02将图表渲染为图像
- 03在 Markdown 中附上源码和图像
- 04选择多种布局引擎
- 05使用常见图类型示例
适用场景
- 01可视化复杂依赖关系
- 02记录调用图和代码流程
- 03绘制网络拓扑或状态机
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ mindmorass/ reflex --skill graphviz-diagrams安装后在 Claude Code 中输入/graphviz-diagrams确认已加载。
详细介绍
Graphviz 图表技能
用途
使用 Graphviz DOT 语言创建复杂的图形可视化,同时提供源代码和预渲染图像。
适用场景
- 复杂依赖关系图
- 调用图和代码流程
- 网络拓扑
- 层级结构
- 具有复杂转换的状态机
- 任何需要精确控制布局的图形
输出格式
每个 Graphviz 图表都应包含:
- 内嵌 DOT 源代码 - 供参考和后续编辑
- 预渲染图像 - 可在任何 Markdown 渲染器中查看
文档结构
## Diagram: [Name]
### Source
```dot
digraph G {
A -> B
}
```
### Rendered

渲染工作流
步骤 1:编写 DOT 源代码
dot_source = """
digraph G {
rankdir=LR;
A -> B -> C;
}
"""
步骤 2:渲染为图像
import subprocess
from pathlib import Path
def render_graphviz(
dot_source: str,
output_path: Path,
format: str = "png",
engine: str = "dot"
) -> Path:
"""
Render DOT source to image file.
Args:
dot_source: DOT language source code
output_path: Output file path (without extension)
format: Output format (png, svg, pdf)
engine: Layout engine (dot, neato, fdp, circo, twopi, sfdp)
Returns:
Path to rendered image
"""
output_file = output_path.with_suffix(f".{format}")
result = subprocess.run(
[engine, f"-T{format}", "-o", str(output_file)],
input=dot_source,
text=True,
capture_output=True
)
if result.returncode != 0:
raise RuntimeError(f"Graphviz error: {result.stderr}")
return output_file
步骤 3:嵌入 Markdown
def create_diagram_markdown(
name: str,
dot_source: str,
image_path: str
) -> str:
"""Create markdown with both source and rendered image."""
return f"""## Diagram: {name}
### Source
```dot
{dot_source}
渲染结果
"""
## DOT Language Reference
### Basic Graph Types
#### Directed Graph (digraph)
```dot
digraph G {
A -> B;
B -> C;
A -> C;
}
无向图(graph)
graph G {
A -- B;
B -- C;
A -- C;
}
图属性
digraph G {
// Graph attributes
rankdir=LR; // Direction: TB, BT, LR, RL
splines=ortho; // Edge style: line, polyline, curved, ortho, spline
nodesep=0.5; // Space between nodes
ranksep=1.0; // Space between ranks
bgcolor="white"; // Background color
fontname="Helvetica"; // Font for labels
// Nodes and edges
A -> B;
}
节点属性
digraph G {
// Node defaults
node [shape=box, style=filled, fillcolor=lightblue];
// Individual node styling
A [label="Start", shape=ellipse, fillcolor=green];
B [label="Process
Data", shape=box];
C [label="Decision", shape=diamond, fillcolor=yellow];
D [label="End", shape=ellipse, fillcolor=red];
A -> B -> C;
C -> D;
}
常见节点形状
| 形状 | 用途 |
|---|---|
box | 进程、操作 |
ellipse | 开始/结束、终端 |
diamond | 决策 |
circle | 状态 |
record | 结构化数据 |
Mrecord | 圆角记录 |
cylinder | 数据库 |
folder | 目录/集合 |
component | 组件 |
note | 注释 |
边属性
digraph G {
// Edge defaults
edge [color=gray, fontsize=10];
A -> B [label="step 1", color=blue, penwidth=2];
B -> C [label="step 2", style=dashed];
C -> D [label="step 3", arrowhead=empty];
D -> A [label="loop", style=dotted, constraint=false];
}
箭头样式
| 箭头 | 描述 |
|---|---|
normal | 实心三角形(默认) |
empty | 空心三角形 |
dot | 实心圆 |
odot | 空心圆 |
diamond | 实心菱形 |
none | 无箭头 |
vee | V 形 |
box | 实心方形 |
子图和集群
digraph G {
// Cluster (named subgraph with cluster_ prefix)
subgraph cluster_frontend {
label="Frontend";
style=filled;
fillcolor=lightgray;
UI -> Components -> State;
}
subgraph cluster_backend {
label="Backend";
style=filled;
fillcolor=lightyellow;
API -> Service -> Database;
}
// Cross-cluster edges
State -> API [label="HTTP"];
}
记录(结构化节点)
digraph G {
node [shape=record];
user [label="User|{id: int|name: string|email: string}"];
order [label="Order|{id: int|user_id: int|total: decimal}"];
user -> order [label="1:N"];
}
HTML 标签
digraph G {
node [shape=none];
table [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="lightblue"><B>User</B></TD></TR>
<TR><TD ALIGN="LEFT">id: int</TD></TR>
<TR><TD ALIGN="LEFT">name: string</TD></TR>
<TR><TD ALIGN="LEFT">email: string</TD></TR>
</TABLE>
>];
}
布局引擎
| 引擎 | 最适合 | 描述 |
|---|---|---|
dot | 层级结构 | 有向图、树、DAG |
neato | 网络 | 无向图、弹簧模型 |
fdp | 大型网络 | 力导向,可扩展 |
sfdp | 超大型网络 | 多尺度力导向 |
circo | 环形布局 | 环形布局 |
twopi | 径向布局 | 从根节点向外的径向布局 |
用法
# Different engines produce different layouts
dot -Tpng graph.dot -o graph-hierarchical.png
neato -Tpng graph.dot -o graph-spring.png
circo -Tpng graph.dot -o graph-circular.png
常见模式
依赖关系图
digraph Dependencies {
rankdir=BT;
node [shape=box, style=filled, fillcolor=lightblue];
// Packages
app [label="app"];
api [label="api"];
core [label="core"];
utils [label="utils"];
db [label="database"];
// Dependencies (arrows point to dependency)
app -> api;
app -> core;
api -> core;
api -> db;
core -> utils;
db -> utils;
}
状态机
digraph StateMachine {
rankdir=LR;
node [shape=circle];
// Start state
start [shape=point, width=0.2];
// States
idle [label="Idle"];
loading [label="Loading"];
success [label="Success", shape=doublecircle];
error [label="Error"];
// Transitions
start -> idle;
idle -> loading [label="fetch()"];
loading -> success [label="200 OK"];
loading -> error [label="error"];
error -> idle [label="retry()"];
success -> idle [label="reset()"];
}
调用图
digraph CallGraph {
rankdir=TB;
node [shape=box, fontname="Courier"];
main [style=filled, fillcolor=lightgreen];
main -> init;
main -> process;
main -> cleanup;
init -> loadConfig;
init -> connectDB;
process -> validateInput;
process -> transform;
process -> save;
transform -> normalize;
transform -> enrich;
save -> connectDB [style=dashed, label="reuse"];
}
网络拓扑
graph Network {
layout=neato;
overlap=false;
node [shape=box];
// Nodes
internet [shape=cloud, label="Internet"];
firewall [shape=box3d, label="Firewall"];
lb [label="Load
Balancer"];
web1 [label="Web 1"];
web2 [label="Web 2"];
app1 [label="App 1"];
app2 [label="App 2"];
db [shape=cylinder, label="Database"];
// Connections
internet -- firewall;
firewall -- lb;
lb -- web1;
lb -- web2;
web1 -- app1;
web1 -- app2;
web2 -- app1;
web2 -- app2;
app1 -- db;
app2 -- db;
}
实体关系
digraph ERD {
rankdir=LR;
node [shape=record, fontname="Helvetica"];
edge [arrowhead=none];
user [label="<pk> User|id: PK\lname: string\lemail: string\l"];
order [label="<pk> Order|id: PK\luser_id: FK\ltotal: decimal\l"];
item [label="<pk> OrderItem|id: PK\lorder_id: FK\lproduct_id: FK\l"];
product [label="<pk> Product|id: PK\lname: string\lprice: decimal\l"];
user:pk -> order:pk [label="1:N", arrowhead=crow];
order:pk -> item:pk [label="1:N", arrowhead=crow];
product:pk -> item:pk [label="1:N", arrowhead=crow];
}
流程图
digraph Flowchart {
rankdir=TB;
node [fontname="Helvetica"];
start [shape=ellipse, label="Start", style=filled, fillcolor=lightgreen];
input [shape=parallelogram, label="Get Input"];
validate [shape=diamond, label="Valid?"];
process [shape=box, label="Process Data"];
error [shape=box, label="Show Error", style=filled, fillcolor=lightyellow];
output [shape=parallelogram, label="Output Result"];
end [shape=ellipse, label="End", style=filled, fillcolor=lightcoral];
start -> input;
input -> validate;
validate -> process [label="Yes"];
validate -> error [label="No"];
error -> input;
process -> output;
output -> end;
}
与发布器集成
Obsidian 集成
def publish_graphviz_to_obsidian(
vault_path: str,
folder: str,
filename: str,
diagram_name: str,
dot_source: str
):
"""Publish Graphviz diagram to Obsidian with source and image."""
from pathlib import Path
vault = Path(vault_path)
target_dir = vault / folder
attachments_dir = vault / "attachments"
target_dir.mkdir(parents=True, exist_ok=True)
attachments_dir.mkdir(parents=True, exist_ok=True)
# Render image
image_name = f"{filename}-diagram.png"
image_path = attachments_dir / image_name
render_graphviz(dot_source, image_path.with_suffix(""), "png")
# Create markdown with both source and image
content = f"""# {diagram_name}
## Source
```dot
{dot_source}
渲染结果
![[{image_name}]] """
note_path = target_dir / f"{filename}.md"
note_path.write_text(content)
### Joplin Integration
```python
def publish_graphviz_to_joplin(
notebook: str,
title: str,
diagram_name: str,
dot_source: str
):
"""Publish Graphviz diagram to Joplin with source and image."""
import tempfile
from pathlib import Path
with tempfile.TemporaryDirectory() as tmpdir:
tmpdir = Path(tmpdir)
# Render image
image_path = tmpdir / "diagram.png"
render_graphviz(dot_source, image_path.with_suffix(""), "png")
# Create markdown
content = f"""# {diagram_name}
## Source
```dot
{dot_source}
渲染结果
"""
md_path = tmpdir / f"{title}.md"
md_path.write_text(content)
# 导入到 Joplin(导入 Markdown 和引用的图像)
subprocess.run([
"joplin", "import", str(tmpdir),
"--notebook", notebook
], check=True)
## Graphviz vs Mermaid
| Feature | Graphviz | Mermaid |
|---------|----------|---------|
| **Layout control** | Precise, many engines | Automatic only |
| **Complexity** | Handles very large graphs | Better for simpler diagrams |
| **Rendering** | External tool required | Browser-native |
| **Styling** | Extensive options | Limited but sufficient |
| **Learning curve** | Steeper | Easier |
| **Use case** | Complex dependencies, call graphs | Quick diagrams, sequences |
**Use Graphviz when:**
- You need precise layout control
- Graph is large or complex
- You need specific node arrangements
- Creating dependency or call graphs
**Use Mermaid when:**
- Quick inline diagrams
- Sequence diagrams
- Simple flowcharts
- Native browser rendering preferred
## Prerequisites
### Install Graphviz
```bash
# macOS
brew install graphviz
# Ubuntu/Debian
sudo apt-get install graphviz
# Windows (chocolatey)
choco install graphviz
# Verify installation
dot -V
检查清单
创建 Graphviz 图表前:
- 已安装 Graphviz(
dot -V) - 输出目录可写
- DOT 语法已验证
- 已选择合适的布局引擎
- 输出中包含源代码和图像
- 图像路径适用于目标平台(Obsidian/Joplin)
评论
支持 Markdown