Graphviz 图形可视化教程 / 09 - 高级特性
第 09 章 · 高级特性
9.1 HTML 表格标签
HTML 标签是 Graphviz 中创建复杂节点标签的最强大方式。
基本表格结构
digraph HTMLBasics {
node [fontname="Microsoft YaHei" shape=plain]
edge [fontname="Microsoft YaHei"]
// 基本表格
table [label=<
<TABLE BORDER="1" CELLBORDER="0" CELLSPACING="2" CELLPADDING="8">
<TR>
<TD BGCOLOR="#1976D2"><FONT COLOR="white"><B>标题栏</B></FONT></TD>
</TR>
<TR>
<TD>内容区域</TD>
</TR>
</TABLE>
>]
another [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD>单元格 1</TD><TD>单元格 2</TD></TR>
<TR><TD COLSPAN="2">合并列</TD></TR>
</TABLE>
>]
table -> another
}
TABLE 属性
| 属性 | 说明 | 常用值 |
|---|---|---|
BORDER | 外边框宽度 | 0、1、2 |
CELLBORDER | 单元格边框宽度 | 0、1 |
CELLSPACING | 单元格间距 | 0、2、4 |
CELLPADDING | 单元格内边距 | 4、8、12 |
BGCOLOR | 表格背景色 | 颜色值 |
ALIGN | 水平对齐 | LEFT、CENTER、RIGHT |
VALIGN | 垂直对齐 | TOP、MIDDLE、BOTTOM |
FIXEDSIZE | 固定尺寸 | TRUE、FALSE |
WIDTH | 宽度(像素) | 数值 |
HEIGHT | 高度(像素) | 数值 |
ROWS | 行优先布局 | ROW、ROWSPAN |
COLUMNS | 列优先布局 | COL、COLSPAN |
TD 单元格属性
| 属性 | 说明 | 常用值 |
|---|---|---|
COLSPAN | 合并列数 | 2、3 |
ROWSPAN | 合并行数 | 2、3 |
ALIGN | 水平对齐 | LEFT、CENTER、RIGHT |
VALIGN | 垂直对齐 | TOP、MIDDLE、BOTTOM |
BGCOLOR | 背景色 | 颜色值 |
WIDTH | 宽度 | 像素值 |
HEIGHT | 高度 | 像素值 |
PORT | 端口名 | 字符串 |
SIDES | 边框方向 | L、R、T、B(可组合) |
HREF | 超链接 | URL |
TARGET | 链接目标 | _blank、_self |
TOOLTIP | 鼠标提示 | 文本 |
FIXEDSIZE | 固定尺寸 | TRUE、FALSE |
9.2 复杂表格示例
UML 类图
digraph UMLAdvanced {
node [fontname="Microsoft YaHei" shape=plain]
edge [fontname="Microsoft YaHei" fontsize=9]
UserService [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="6">
<TR>
<TD COLSPAN="2" BGCOLOR="#1976D2">
<FONT COLOR="white" POINT-SIZE="13"><B>«interface» UserService</B></FONT>
</TD>
</TR>
<TR>
<TD COLSPAN="2" BGCOLOR="#E3F2FD" ALIGN="LEFT">
<FONT POINT-SIZE="10">
- repository: UserRepository<BR/>
- cache: RedisClient<BR/>
</FONT>
</TD>
</TR>
<TR>
<TD COLSPAN="2" BGCOLOR="#E8F5E9" ALIGN="LEFT">
<FONT POINT-SIZE="10">
+ findById(id: Long): User<BR/>
+ create(user: User): User<BR/>
+ update(user: User): void<BR/>
+ delete(id: Long): void<BR/>
</FONT>
</TD>
</TR>
</TABLE>
>]
UserRepository [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="6">
<TR>
<TD COLSPAN="2" BGCOLOR="#388E3C">
<FONT COLOR="white" POINT-SIZE="13"><B>«interface» UserRepository</B></FONT>
</TD>
</TR>
<TR>
<TD COLSPAN="2" BGCOLOR="#E8F5E9" ALIGN="LEFT">
<FONT POINT-SIZE="10">
+ findById(id: Long): User<BR/>
+ save(user: User): User<BR/>
+ findByEmail(email: String): User<BR/>
</FONT>
</TD>
</TR>
</TABLE>
>]
UserService -> UserRepository [label="依赖" arrowhead=open style=dashed]
}
数据库表结构
digraph DBSchema {
node [fontname="Microsoft YaHei" shape=plain]
edge [fontname="Microsoft YaHei" fontsize=9]
Users [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="6">
<TR><TD COLSPAN="3" BGCOLOR="#1976D2"><FONT COLOR="white"><B>users</B></FONT></TD></TR>
<TR>
<TD BGCOLOR="#FFE0B2">🔑</TD>
<TD ALIGN="LEFT">id</TD>
<TD ALIGN="LEFT">BIGINT PK</TD>
</TR>
<TR>
<TD></TD>
<TD ALIGN="LEFT">username</TD>
<TD ALIGN="LEFT">VARCHAR(50)</TD>
</TR>
<TR>
<TD></TD>
<TD ALIGN="LEFT">email</TD>
<TD ALIGN="LEFT">VARCHAR(100)</TD>
</TR>
<TR>
<TD></TD>
<TD ALIGN="LEFT">created_at</TD>
<TD ALIGN="LEFT">TIMESTAMP</TD>
</TR>
</TABLE>
>]
Orders [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="6">
<TR><TD COLSPAN="3" BGCOLOR="#388E3C"><FONT COLOR="white"><B>orders</B></FONT></TD></TR>
<TR>
<TD BGCOLOR="#FFE0B2">🔑</TD>
<TD ALIGN="LEFT">id</TD>
<TD ALIGN="LEFT">BIGINT PK</TD>
</TR>
<TR>
<TD BGCOLOR="#BBDEFB">🔗</TD>
<TD ALIGN="LEFT">user_id</TD>
<TD ALIGN="LEFT">BIGINT FK</TD>
</TR>
<TR>
<TD></TD>
<TD ALIGN="LEFT">amount</TD>
<TD ALIGN="LEFT">DECIMAL(10,2)</TD>
</TR>
<TR>
<TD></TD>
<TD ALIGN="LEFT">status</TD>
<TD ALIGN="LEFT">VARCHAR(20)</TD>
</TR>
</TABLE>
>]
Users -> Orders [label="1 : N" arrowhead=none arrowhead=crow]
}
服务器监控面板
digraph MonitorDashboard {
node [fontname="Microsoft YaHei" shape=plain]
edge [fontname="Microsoft YaHei" fontsize=9]
Server [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="6" BGCOLOR="#FAFAFA">
<TR>
<TD COLSPAN="4" BGCOLOR="#1976D2">
<FONT COLOR="white" POINT-SIZE="14"><B>🖥 Web Server 01</B></FONT>
</TD>
</TR>
<TR>
<TD BGCOLOR="#E3F2FD"><B>指标</B></TD>
<TD BGCOLOR="#E3F2FD"><B>当前值</B></TD>
<TD BGCOLOR="#E3F2FD"><B>阈值</B></TD>
<TD BGCOLOR="#E3F2FD"><B>状态</B></TD>
</TR>
<TR>
<TD>CPU</TD>
<TD ALIGN="RIGHT">72%</TD>
<TD ALIGN="RIGHT">80%</TD>
<TD BGCOLOR="#C8E6C9">正常</TD>
</TR>
<TR>
<TD>内存</TD>
<TD ALIGN="RIGHT">85%</TD>
<TD ALIGN="RIGHT">90%</TD>
<TD BGCOLOR="#FFE0B2">警告</TD>
</TR>
<TR>
<TD>磁盘</TD>
<TD ALIGN="RIGHT">95%</TD>
<TD ALIGN="RIGHT">90%</TD>
<TD BGCOLOR="#FFCDD2">危险</TD>
</TR>
<TR>
<TD>连接数</TD>
<TD ALIGN="RIGHT">1,234</TD>
<TD ALIGN="RIGHT">5,000</TD>
<TD BGCOLOR="#C8E6C9">正常</TD>
</TR>
</TABLE>
>]
}
9.3 图片嵌入
使用 IMG 标签
在 HTML 标签中嵌入图片:
digraph ImageEmbed {
node [fontname="Microsoft YaHei" shape=plain]
logo [label=<
<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="4">
<TR>
<TD><IMG SRC="/image/logo.png" SCALE="TRUE" WIDTH="64" HEIGHT="64"/></TD>
</TR>
<TR>
<TD><B>应用名称</B></TD>
</TR>
</TABLE>
>]
service [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR>
<TD><IMG SRC="/image/icon.png" SCALE="TRUE" WIDTH="32" HEIGHT="32"/></TD>
<TD>服务节点</TD>
</TR>
</TABLE>
>]
logo -> service
}
使用 image 属性
digraph ImageAttr {
node [fontname="Microsoft YaHei"]
// image 属性(节点形状必须是 none 或 plaintext)
icon [shape=none label="" image="/image/icon.png" width=1 height=1]
text [label="纯文本节点"]
icon -> text
}
⚠️ 图片路径:图片路径相对于运行
dot命令时的工作目录,或使用绝对路径/URL。
9.4 高级端口
HTML 端口
digraph HTMLPorts {
node [fontname="Microsoft YaHei" shape=plain]
router [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="6">
<TR>
<TD COLSPAN="3" BGCOLOR="#1976D2">
<FONT COLOR="white"><B>路由器</B></FONT>
</TD>
</TR>
<TR>
<TD PORT="eth0">ETH0</TD>
<TD PORT="eth1">ETH1</TD>
<TD PORT="eth2">ETH2</TD>
</TR>
</TABLE>
>]
sw1 [label="交换机 1" shape=box style=rounded]
sw2 [label="交换机 2" shape=box style=rounded]
sw3 [label="交换机 3" shape=box style=rounded]
router:eth0 -> sw1 [label="端口 0"]
router:eth1 -> sw2 [label="端口 1"]
router:eth2 -> sw3 [label="端口 2"]
}
端口与 Record 对比
| 特性 | Record 端口 | HTML 端口 |
|---|---|---|
| 语法 | <f0> | PORT="name" |
| 布局 | 垂直/水平 | 完全自由(表格) |
| 样式 | 有限 | 丰富(字体、颜色等) |
| 合并单元格 | 不支持 | 支持 COLSPAN/ROWSPAN |
| 推荐程度 | 旧项目兼容 | 推荐 |
9.5 排名约束 (Rank)
rank=same 详解
digraph RankSameExample {
rankdir=TB
node [fontname="Microsoft YaHei" shape=box style="filled,rounded" fillcolor="#E3F2FD" color="#1976D2"]
Start [shape=circle fillcolor="#C8E6C9" color="#388E3C"]
// 这三个节点在同一层
{
rank=same
A [label="步骤 A"]
B [label="步骤 B"]
C [label="步骤 C"]
}
End [shape=doublecircle fillcolor="#FFCDD2" color="#C62828"]
Start -> {A B C}
{A B C} -> End
}
rank=source / rank=sink
digraph RankSourceSink {
rankdir=TB
node [fontname="Microsoft YaHei" shape=box]
// source — 尽可能靠近顶层
{
rank=source
Input1
Input2
}
// sink — 尽可能靠近底层
{
rank=sink
Output1
Output2
}
// 中间层
Process1
Process2
Input1 -> Process1
Input2 -> Process2
Process1 -> Output1
Process2 -> Output2
Input1 -> Process2 [style=dashed]
}
强制层级间距
digraph RankSep {
rankdir=TB
node [fontname="Microsoft YaHei" shape=box style=rounded]
// 不同层级使用不同的 ranksep
ranksep=1.5
L1 [label="第 1 层" fillcolor="#E3F2FD" style=filled]
L2 [label="第 2 层" fillcolor="#E8F5E9" style=filled]
L3 [label="第 3 层" fillcolor="#FFF3E0" style=filled]
L4 [label="第 4 层" fillcolor="#F3E5F5" style=filled]
L1 -> L2 -> L3 -> L4
}
9.6 边的约束与自由
constraint=false 的用法
digraph ConstraintDemo {
rankdir=TB
node [fontname="Microsoft YaHei" shape=box style="filled,rounded" fillcolor="#E3F2FD" color="#1976D2"]
A -> B -> C -> D
// 这条边不影响排名,但仍然绘制
A -> D [constraint=false style=dashed color="#F44336" label="非约束边"]
// B 和 D 同层(因为 A->D 不影响 D 的排名)
// 实际布局中 C 和 D 可能同层
}
虚拟节点
通过不可见节点控制边的路径:
digraph VirtualNodes {
rankdir=TB
node [fontname="Microsoft YaHei"]
A [shape=box label="A"]
B [shape=box label="B"]
C [shape=box label="C"]
// 虚拟节点(不可见)
V [shape=point width=0 style=invis label=""]
A -> V [style=invis]
V -> C [style=invis]
// 绕过 B 的边
A -> C [style=dashed color="#999" label="绕过"]
A -> B -> C
}
9.7 集中边 (Concentrate)
digraph ConcentrateDemo {
concentrate=true
node [fontname="Microsoft YaHei" shape=box]
A -> B
A -> C
A -> D
A -> E
// concentrate=true 会合并从同一节点出发的多条边
// 使用共享路径减少视觉混乱
}
9.8 业务场景:API 文档图
digraph APIDoc {
rankdir=TB
node [fontname="Microsoft YaHei" shape=plain]
edge [fontname="Microsoft YaHei" fontsize=9]
// API 端点
GetUser [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="6">
<TR>
<TD BGCOLOR="#4CAF50"><FONT COLOR="white"><B>GET /api/users/:id</B></FONT></TD>
</TR>
<TR>
<TD ALIGN="LEFT">
<FONT POINT-SIZE="10">
<B>参数:</B><BR/>
- id: Long (路径参数)<BR/>
<B>响应:</B><BR/>
- 200: User 对象<BR/>
- 404: 用户未找到<BR/>
</FONT>
</TD>
</TR>
</TABLE>
>]
CreateUser [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="6">
<TR>
<TD BGCOLOR="#2196F3"><FONT COLOR="white"><B>POST /api/users</B></FONT></TD>
</TR>
<TR>
<TD ALIGN="LEFT">
<FONT POINT-SIZE="10">
<B>请求体:</B><BR/>
- username: String<BR/>
- email: String<BR/>
<B>响应:</B><BR/>
- 201: 创建成功<BR/>
- 400: 参数错误<BR/>
</FONT>
</TD>
</TR>
</TABLE>
>]
GetUser -> CreateUser [label="关联" style=dashed]
}
注意事项
⚠️ HTML 标签不用引号:
label=<TABLE>...</TABLE>而不是label="<TABLE>...</TABLE>"。
⚠️ 嵌套引号:HTML 属性值中的引号用单引号
'<FONT COLOR='red'>'或双引号均可。
⚠️ IMG 路径:图片路径相对于
dot命令的工作目录,不是相对于 DOT 文件。
⚠️ Record vs HTML:HTML 标签功能更强大,新项目推荐使用 HTML 标签替代 Record。
⚠️ rank=same 仅 dot 有效:在其他引擎中
rank属性无效。
⚠️ 输出格式限制:HTML 标签中的
HREF、TOOLTIP等交互属性仅在 SVG 输出中有效。
扩展阅读
下一章:10 - 编程语言绑定 — 使用编程语言动态生成图形。