Python Graphviz 完整教程
Graphviz 是一个强大的图形可视化软件,它使用一种叫做 DOT 语言的简单文本描述来生成图形,Python 通过 graphviz 库可以非常方便地调用 Graphviz,将数据结构(如树、图、流程图)自动渲染成漂亮的图片。

本教程将分为以下几个部分:
- 环境准备: 安装 Graphviz 和 Python 库。
- 基础入门: 创建你的第一个图形和节点。
- 深入图形元素: 学习添加边、设置属性。
- 子图与集群: 构建复杂的图形结构。
- 渲染与输出: 将图形保存为不同格式的文件。
- 进阶技巧: 使用
Source对象、直接使用 DOT 语法等。 - 实战案例: 绘制一个决策树和流程图。
- 常见问题与解决方案。
环境准备
在开始之前,你需要安装两样东西:
a) 安装 Graphviz 软件
这是核心,graphviz Python 库只是一个封装,真正负责绘图的是这个软件。
-
Windows:
(图片来源网络,侵删)- 访问 Graphviz 官方下载页面。
- 下载 Windows 安装包 (
graphviz-release.msi)。 - 重要! 安装时,请务必勾选 "Add Graphviz to the system PATH for all users" 或 "Add Graphviz to the system PATH for the current user" 选项,这能让 Python 找到它。
- 安装完成后,重启你的终端或 IDE。
-
macOS (使用 Homebrew):
brew install graphviz
-
Linux (Debian/Ubuntu):
sudo apt-get update sudo apt-get install graphviz
验证安装: 打开终端或命令提示符,输入 dot -V,如果显示版本号,说明安装成功。
$ dot -V dot - graphviz version 2.50.0 (2025-01-11)
b) 安装 Python 库
在你的 Python 环境中安装 graphviz 库。

pip install graphviz
基础入门
让我们从一个最简单的例子开始:创建一个包含两个节点和一条边的图形。
from graphviz import Digraph # Digraph 用于有向图,Graph 用于无向图
# 1. 创建一个有向图对象
dot = Digraph(comment='My First Graph')
# 2. 添加节点
# 节点可以有 ID 和标签,标签是显示在图上的文字
dot.node('A', 'Start')
dot.node('B', 'End')
# 3. 添加边
# 边连接两个节点,可以指定边的标签
dot.edge('A', 'B', label='goes to')
# 4. 渲染并显示图形
# render() 方法会生成一个文件 (默认格式是 pdf)
# view=True 会在默认的图片查看器中打开生成的文件
dot.render('my-first-graph.gv', view=True)
代码解释:
from graphviz import Digraph: 导入有向图类,如果需要无向图,请使用from graphviz import Graph。dot = Digraph(...): 创建一个图形实例。dot.node(...): 添加一个节点。- 第一个参数
'A'是节点的唯一标识符(ID)。 - 第二个参数
'Start'是在图形中显示的标签。
- 第一个参数
dot.edge(...): 添加一条有向边。'A'是源节点 ID。'B'是目标节点 ID。label='goes to'是边上显示的文字。
dot.render(...): 将图形渲染成文件。'my-first-graph.gv'是输出文件的名称(.gv是 DOT 语言的扩展名)。view=True会自动打开生成的文件。
运行上述代码后,你会得到一个名为 my-first-graph.gv.pdf 的文件,并用你的默认 PDF 阅读器打开它,显示如下:
深入图形元素
Graphviz 的强大之处在于其丰富的属性设置。
a) 节点和边的属性
你可以通过 node_attr 和 edge_attr 设置全局默认属性,也可以在添加单个节点/边时使用 **attrs 参数进行覆盖。
from graphviz import Digraph
dot = Digraph(comment='Attributes Example')
# 设置全局节点属性:所有节点默认为矩形,填充色为浅蓝色
dot.node_attr = {'shape': 'box', 'style': 'filled', 'fillcolor': 'lightblue'}
# 设置全局边属性:所有边默认为虚线
dot.edge_attr = {'style': 'dashed'}
# 添加节点,并覆盖全局属性
dot.node('A', 'Start', shape='ellipse', fillcolor='green') # A 是椭圆,绿色
dot.node('B', 'Process')
dot.node('C', 'End', shape='diamond', fillcolor='red') # C 是菱形,红色
# 添加边,并覆盖全局属性
dot.edge('A', 'B', label='Step 1', style='solid') # 这条边是实线
dot.edge('B', 'C', label='Step 2')
dot.render('attributes-example.gv', view=True)
常用属性:
| 属性类别 | 属性名 | 描述 | 示例值 |
|---|---|---|---|
| 节点 | shape |
节点形状 | 'box', 'ellipse', 'circle', 'diamond', 'plaintext' |
style |
样式 | 'filled', 'dashed', 'rounded' |
|
color |
边框颜色 | 'red', '#FF0000' |
|
fillcolor |
填充颜色 | 'lightblue', 'lightgrey' |
|
label |
显示文本 | 'Hello' |
|
| 边 | label |
边上显示的文本 | 'goes to' |
style |
样式 | 'solid', 'dashed', 'dotted' |
|
color |
颜色 | 'blue' |
|
arrowhead |
箭头形状 | 'normal', 'vee', 'dot' |
|
fontsize |
字体大小 | '12' |
|
| 图形 | rankdir |
布局方向 | 'LR' (从左到右), 'TB' (从上到下) |
bgcolor |
背景颜色 | 'white' |
子图与集群
当图形变得复杂时,可以使用子图来组织结构。cluster 前缀可以给子图添加一个视觉边界。
from graphviz import Digraph
dot = Digraph(comment='Clusters Example')
# 设置整体布局方向为从左到右
dot.attr(rankdir='LR')
# 定义主流程
dot.node('Start', 'Start')
dot.node('Process1', 'Process 1')
dot.node('Process2', 'Process 2')
dot.node('End', 'End')
dot.edge('Start', 'Process1')
dot.edge('Process1', 'Process2')
dot.edge('Process2', 'End')
# 定义一个子图(集群)
# 'cluster_1' 是子图的 ID,'cluster' 前缀会自动创建一个矩形框
with dot.subgraph(name='cluster_1') as sub:
sub.attr(label='Subprocess A') # 子图的标题
sub.attr(color='blue') # 子图的边框颜色
sub.node('A1', 'Task A1')
sub.node('A2', 'Task A2')
sub.edge('A1', 'A2')
# 将子图中的节点连接到主流程
sub.edge('Process1', 'A1')
sub.edge('A2', 'Process2')
# 定义另一个子图
with dot.subgraph(name='cluster_2') as sub:
sub.attr(label='Subprocess B', color='green')
sub.node('B1', 'Task B1')
sub.node('B2', 'Task B2')
sub.edge('B1', 'B2')
# 连接到主流程
sub.edge('Process2', 'B1')
sub.edge('B2', 'End')
dot.render('clusters-example.gv', view=True)
渲染与输出
render() 方法非常灵活,可以控制输出格式和位置。
from graphviz import Digraph
dot = Digraph()
dot.node('A', 'Apple')
dot.edge('A', 'B')
# render() 的主要参数:
# filename: 输出文件名 (不带后缀)
# directory: 输出目录 (默认是当前目录)
# format: 输出格式 ('pdf', 'png', 'svg', 'jpg' 等)
# cleanup: 是否在渲染后删除 .gv 源文件 (默认为 True)
# view: 是否在渲染后自动打开文件 (默认为 False)
# 示例1: 生成 PNG 图片到 'output' 文件夹
dot.render('output/fruit_graph', format='png', cleanup=False)
# 示例2: 生成 SVG 图片,不自动打开,不删除源文件
dot.render('fruit_graph.svg', format='svg', view=False, cleanup=False)
支持的格式: pdf, png, svg, jpg, gif, bmp, dot (输出 DOT 源文件) 等。svg 格式在现代网页应用中特别有用。
进阶技巧
a) 使用 Source 对象
Source 对象非常方便,它可以直接解析一个 DOT 语言的字符串,而不需要逐个调用 node() 和 edge() 方法。
from graphviz import Source
# 直接用 DOT 语法描述图形
dot_string = """
digraph {
rankdir=LR;
node [shape=box];
A [label="Start"];
B [label="Process"];
C [label="End"];
A -> B [label="step 1"];
B -> C [label="step 2"];
}
"""
# 创建 Source 对象
src = Source(dot_string)
# 像之前一样渲染和显示
src.render('source-example.gv', view=True)
# 你也可以直接在 Jupyter Notebook 中显示它
# src
b) 循环结构
非常适合绘制树、列表等数据结构。
from graphviz import Digraph
dot = Digraph(comment='Tree Structure')
dot.attr(rankdir='TB') # 从上到下布局
# 根节点
dot.node('root', 'Root')
# 循环创建子节点
parent = 'root'
for i in range(3):
child_id = f'child_{i}'
dot.node(child_id, f'Child {i}')
dot.edge(parent, child_id)
# 为每个子节点再创建两个子节点
for j in range(2):
grandchild_id = f'grandchild_{i}_{j}'
dot.node(grandchild_id, f'GC {i}-{j}')
dot.edge(child_id, grandchild_id)
dot.render('tree-structure.gv', view=True)
实战案例
a) 绘制一个简单的决策树
from graphviz import Digraph
dot = Digraph(comment='Decision Tree')
dot.attr(rankdir='TB')
# 定义节点和边
dot.node('A', 'Is it raining?', shape='diamond')
dot.node('B', 'Take an umbrella', shape='box')
dot.node('C', 'Wear sunglasses', shape='box')
dot.edge('A', 'B', label='Yes')
dot.edge('A', 'C', label='No')
dot.render('decision-tree.gv', view=True)
b) 绘制一个流程图
from graphviz import Graph # 使用无向图
dot = Graph(comment='Process Flow')
dot.attr(rankdir='LR', splines='ortho') # splines='ortho' 使线条呈直角
dot.node('start', 'Start', shape='ellipse')
dot.node('input', 'Input Data', shape='parallelogram')
dot.node('process', 'Process Data', shape='box3d')
dot.node('decision', 'Valid?', shape='diamond')
dot.node('output', 'Output Result', shape='parallelogram')
dot.node('stop', 'Stop', shape='ellipse')
dot.edge('start', 'input')
dot.edge('input', 'process')
dot.edge('process', 'decision')
dot.edge('decision', 'output', label='Yes')
dot.edge('output', 'stop')
dot.edge('decision', 'process', label='No') # 循环回处理
dot.render('process-flow.gv', view=True)
常见问题与解决方案
Q1: ImportError: Failed to import Graphviz's executables.
原因: Python 找不到 Graphviz 软件。 解决方案:
-
确认已安装 Graphviz 软件:在终端运行
dot -V。 -
检查系统 PATH:这是最常见的原因,确保 Graphviz 的
bin目录(C:\Program Files\Graphviz\bin)已添加到系统的环境变量PATH中,修改后重启终端和 IDE。 -
手动指定路径:如果无法修改 PATH,可以在代码中指定 Graphviz 的安装路径。
import os from graphviz import Digraph # 设置 Graphviz 的安装路径 os.environ["PATH"] += os.pathsep + 'C:/Program Files/Graphviz/bin' dot = Digraph() dot.node('A') dot.render('manual-path.gv', view=True)
Q2: graphviz.backend.execute.ExecutableNotFound: 'dot' not found
原因: 和 Q1 一样,是路径问题。 解决方案: 同上。
Q3: 渲染的图片中文字乱码或显示为方框。
原因: Graphviz 默认不支持中文字体,或者系统没有找到合适的字体。 解决方案:
-
指定中文字体: 在创建图形后,设置全局字体属性。
from graphviz import Digraph dot = Digraph() # 指定一个系统中存在的中文字体 dot.attr('node', fontname='SimHei') # 黑体 dot.attr('edge', fontname='SimHei') dot.attr('graph', fontname='SimHei') # 图形标题字体 dot.node('A', '你好') dot.node('B', '世界') dot.edge('A', 'B', label='连接') dot.render('chinese-font.gv', view=True) -
确保字体文件存在: 确保你指定的字体(如
SimHei,Microsoft YaHei)确实安装在你的系统上。
Q4: 在 Jupyter Notebook / Google Colab 中使用 Graphviz。
解决方案:
-
本地 Jupyter: 确保你的 Python 环境和系统 PATH 都配置正确,安装
graphviz库后,通常可以直接dot = Digraph(); dot在单元格末尾输出图形。 -
Google Colab: Colab 的环境是隔离的,没有预装 Graphviz,你需要先安装软件和库。
# 在 Colab 的一个单元格中运行 !apt-get install graphviz !pip install graphviz # 然后在另一个单元格中正常使用 from graphviz import Digraph dot = Digraph() dot.node('A') dot # 在单元格末尾输入变量名即可显示
通过这份教程,你应该已经掌握了使用 Python graphviz 库进行图形可视化的基本技能,从简单的节点边,到复杂的子图和属性设置,再到解决常见问题,Graphviz 是一个值得掌握的强大工具。
下一步:
- 尝试用 Graphviz 可视化你自己的数据,比如文件目录结构、算法逻辑、或者状态机。
- 探索 Graphviz 的 官方文档,了解更多高级功能和布局参数。
- 学习 DOT 语言的更多语法,直接编写
.gv文件然后用Source类加载,有时会更高效。
