使用Graphviz绘图

Table of Contents

1 简介

Graphviz是一款开源的绘图软件,主要用来绘制关系、流程图。与多数“所见即所得”的绘图软件不同,Graphviz使用DOT语言来绘图(可理解为“所思即所得”),这样我们可专注于图片要表达的内容本身,而不去考虑如何画一根线、如何处理元素之间的布局等等,这些工作都由Graphviz去做;由于通过编程方式绘图,源文件也是纯文本形式的,便于我们用任意编辑器编辑,同时便于使用版本控制软件管理。

DOT语言用于描述节点、线和图(Graph),其中图又分为有向图和无向图。

2 无向图

无向图即节点之间的连线没有箭头指向的方向。定义语法为:

graph 图名称 {
	节点1 -- 节点2;
}

节点之间的连线用“–”。另外,DOT语言的注释如下:

// 行注释
/* 块注释 */

例,定义一棵二叉树:

// example.dot
graph example {
	a -- b;
	a -- c;
	b -- d;
	b -- e;
	c -- f;
	c -- g;
}

然后用dot命令生成图片(命令使用方法请见“附录”)。

结果如下图:

1.png

3 有向图

有向图即节点之间通过箭头表示方向。定义语法如下:

digraph 图名称 {
	节点1 -> 节点2; // 节点之间关系用”->“表达
}

例:

digraph example{
	a -> b;
	a -> c;
}

输出如下:

2.png

a -> b;
a -> c;

还可用等价的语法表示:

a -> {b, c};

4 属性设置

在DOT语言中我们还可以对图、节点和连线进行样式的属性设置。

以下几节列举一些常用的,完整的属性请见官方文档:http://www.graphviz.org/content/attrs

4.1 通用属性

4.1.1 label

label可为图、线和节点设置文字标签。

例,为节点设置标签:

digraph example {
	0 [label="顶点0"];
	0 -> 1 [label="从0到1"];
}

效果如下:

6.png

例,为图设置标题:

digraph example {
	graph [label="图的标题"];
	a -> b;
}

效果如下:

7.png

4.1.2 设置字体样式

4.1.2.1 字体大小设置
digraph example {
	a [fontsize=18, label="18号字体"]
	b [fontsize=12, label="12号字体"];
	a -> b;
}

效果如下:

8.png

4.1.2.2 字形设置

要设置字形,需要使用的HTML标签。注意label里使用“<…>”包围字符串。

digraph example {
	b [label=<<b>粗图</b>、<i>斜体</i>、<u>下划线</u>>];
	a -> b;
}

9.png

4.2 线属性

4.2.1 箭头样式设置

digraph example {
	a -> b [label="默认是实线"];
	b -> c [style=dotted, label="虚线"];
	c -> d [style=bold, label="粗线"];
	d -> e [dir=none, label="无箭头"];
}

效果如图:

10.png

4.2.2 反向箭头

digraph example{
	a -> b;
	b -> c [dir=back]
}

效果如图:

3.png

4.2.3 双向箭头

digraph example {
	a -> b [dir=both];
}

效果如图:

4.png

4.2.4 横向箭头

digraph example {
	rankdir = LR;  // LR表示箭头方向是从左到右,要想从右到左,改成RL即可
	a -> b -> c -> d;
}

效果如图:

5.png

4.3 节点属性

4.3.1 改变节点的形状

下面代码列举了一些常用形状:

digraph example {
	a [shape=box, label="矩形"];
	b [shape=circle, label="圆"];
	c [shape=polygon, label="平行四边形"];
	d [shape=ellipse, label="椭圆"];
	a -> b -> c -> d;
}

效果如下图:

11.png

4.3.2 节点边框设置

4.3.2.1 无边框

代码如下,取消节点“b”的边框样式:

digraph example {
	b [shape=none];
	a -> b;
}

效果如下图:

12.png

还可以把节点变成文本样式:

digraph example {
	rankdir = LR;  // 节点的方向是从左到右
	node [shape=plaintext];
	a -> b -> c -> d;
}

效果如下图:

13.png

4.3.2.2 改变边框颜色
digraph example {
	b [color="red"];
	a -> b;
}

效果如下图:

14.png

4.3.3 填充颜色

4.3.3.1 渐变色填充
digraph example {
	b [style=filled, fillcolor="red:black"];  // 红色到黑色的渐变
	a -> b;
}

效果如下图:

15.png

4.3.3.2 纯色填充
digraph example {
	b [style=filled, fillcolor="red"];
	a -> b;
}

效果如下图:

16.png

4.3.4 节点局部属性设置

如果想更改图中某些局部节点的样式,可以把这些节点加在一对花括号中,并设置样式。示例如下:

digraph example {
	a -> b -> c -> d;

	// 设置“点1”和“点2”的样式
	{
		node [shape=plaintext, style="filled", fillcolor="red"];
		点1 -> 点2;
	}
}

效果如下图:

17.png

4.4 label属性的高级用法——结构体

下面展示下如何用label将一个节点“分割”成多个部分,类似C语言的结构体。

如下代码,将一个节点表达成一个链表结构,然后用连线来连接:

digraph example {
	node [shape=record];
	// label属性中,用“|”分割出元素,“<..>”之间的定义成一个变量
	struct1 [label="<head> head | item1 | item2 | ... | <tail>tail"];
	// 用”节点名:元素名“的方式引用结构体的变量
	struct1:head -> struct1:tail;
}

效果如下图:

18.png

还可以对结构体元素中再进行分割,只需在元素中用“{ … }”划分。如下,表达ELF结构体:

digraph example {
	node [shape=record];
	struct1 [label="ELF Header | Header Table | {Segment1 | Segment2 | ...} | Section Header Table"];
}

效果如下图:

19.png

5 子图

子图展示出来的效果就像下面这样,把一组节点单独划分到一个“图”中:

20.png

只需用subgraph关键字定义子图即可,注意子图的名字必须以“cluster”开头。上图对应的代码如下:

digraph example {
	a -> b;

	subgraph cluster_sub1 {
		// 子图可以设置自己的样式
		label ="子图";
		labelloc = "t";
		bgcolor = "gray";
		a -> c -> d;
	}
}

5.1 将某个节点指向一个子图

DOT语言中不能将一个节点指向一个子图,例如表达成:

a -> cluster_sub1

不能这样表达,DOT也不支持节点指向子图。要达到这种效果,只有靠设置属性来改变生成后的外观效果。

例如下面的代码:

digraph example {
	compound = true;  // 必须设置该属性
	subgraph cluster_sub1 {
		c -> d;
		c -> e;
	}

	A -> B;
	A -> C;
	A -> d [lhead=cluster_sub1];
}

其中c、d和e节点单独在一个子图中,我们想让A节点直接指向子图cluster_sub1,这时只用将A节点指向子图的某个节点,然后为这条线设置lhead属性即可。上面代码生成后的效果如下:

21.png

6 用其他工具修改图片

Graphviz最大的问题就是输出图片时是依赖自动布局算法,某些时候可能需输出的图片并非我们想要的样子,如果想调整图片,可以先输出成SVG格式:

$ dot -Tsvg example.dot -o example.svg

然后再用Inkscape等工具修正即可。

7 附录

7.1 安装Graphviz

1、Linux下可通过自带的包管理器安装,如Fedora:

$ sudo dnf install graphviz

2、官方下载二进制包:http://www.graphviz.org/

7.2 命令使用方法

在shell下执行如下命令生成图片:

$ dot -Tpng [dot脚本文件] -o [输出的文件名.png]

-T参数指定输出的格式。可通过-Tv参数可列出支持导出的格式:

$  dot -Tv
Format: "v" not recognized. Use one of: bmp canon cmap cmapx cmapx_np dot eps fig gtk gv ico imap imap_np ismap jpe jpeg jpg pdf pic plain plain-ext png pov ps ps2 svg svgz tif tiff tk vdx vml vmlz x11 xdot xdot1.2 xdot1.4 xlib