写在前面
第一次了解到dot已经是很久之前的事情了,但是到今天才决定写下一点东西,可见懒到什么程度。最开始对dot感兴趣是因为下面这张图:
网络图
对于这种网络图用visio很容易搞定,但是复制、粘贴、拖拽这些用起来总是感觉不怎么顺手。但是用dot来做就完全不一样了,只需写段文本:
digraph G {
main -> parse -> execute;
main -> init;
main -> cleanup;
execute -> make_string;
execute -> printf;
init -> make_string;
main -> printf;
execute -> compare;
}
然后在需要的时候在这段文本中增加边、节点即可。保存之后只需要用贝尔实验室搞的graphviz来生成目标图片就可以了:
dot -Tpng G.dot -o g.png
这样你的vim、emacs就马上变成了一个可以画图的工具了:)。
可能你觉得这样还不够爽,想想看如果用代码自动生成dot文件,也就是说可以通过用代码输出dot文件来间接到达输出图片的目的,是不是能做的事情多多了?
简单用法
日常中用到画图的地方,知道下面几点基本上就够用了:
有向图(digraph)用a->b,无向图(graph)用a--b;
节点、边通过中括号中的key=value来设置,比如main[shape=box];将main节点设置为矩形;
连接点的方向可以通过b->c:se;进行指定;
使用subgraph定义子流程图;
常用属性
对于各种结构的通用的属性如下:
属性名称
默认值
含义
color
black
颜色
colorscheme
X11
颜色描述
fontcolor
black
文字颜色
fontname
Times-Roman
字体
fontsize
14
文字大小
label
显示的标签,对于节点默认为节点名称
penwidth
1.0
线条宽度
style
样式
weight
重要性
常用边属性如下:
属性名称
默认值
含义
arrowhead
normal
箭头头部形状
arrowsize
1.0
箭头大小
arrowtail
normal
箭头尾部形状
constraint
true
是否根据边来影响节点的排序
decorate
设置之后会用一条线来连接edge和label
dir
forward
设置方向:forward,back,both,none
headclip
true
是否到边界为止
tailclip
true
与headclip类似
常用节点属性如下:
属性名称
默认值
含义
shape
ellipse
形状
sides
4
当shape=polygon时的边数
fillcolor
lightgrey/black
填充颜色
fixedsize
false
标签是否影响节点的大小
常用图属性如下:
属性名称
默认值
含义
bgcolor
背景颜色
concentrate
false
让多条边有公共部分
nodesep
.25
节点之间的间隔(英寸)
peripheries
1
边界数
rank
same,min,source, max,sink,设置多个节点顺序
rankdir
TB
排序方向
ranksep
.75
间隔
size
图的大小(英寸)
高级用法
在dot里面label的玩法比较多,在上面看到的每个节点都是简单的一段文字,如果想要比较复杂的结构怎么办?如下图:
复杂的节点
对应的代码如下:
digraph structs {
node [shape=record];
struct1 [shape=record,label=" left| mid\ dle| right"];
struct2 [shape=record,label=" one| two"];
struct3 [shape=record,label="hello\nworld |{ b |{c| d|e}| f}| g | h"];
struct1 -> struct2;
struct1 -> struct3;
}
这个还不算厉害的,label还支持HTML格式的,这样你能想得到的大部分样子的节点都可以被定义出来了:
HTML格式的label
代码如下:
digraph html {
abc [shape=none, margin=0, label=<
>];
}
接着来看cluster的概念,在dot中以cluster开头的子图会被当做是一个新的布局来处理,而不是在原图的基础上继续操作。比如:
流程图
对应代码如下:
digraph G {
subgraph cluster0 {
node [style=filled,color=white];
style=filled;
color=lightgrey;
a0 -> a1 -> a2 -> a3;
label = "process #1";
}
subgraph cluster1 {
node [style=filled];
b0 -> b1 -> b2 -> b3;
label = "process #2";
color=blue
}
start -> a0;
start -> b0;
a1 -> b3;
b2 -> a3;
a3 -> a0;
a3 -> end;
b3 -> end;
start [shape=Mdiamond];
end [shape=Msquare];
}
如果没有cluster的话我们大概能想象的出来最后的结果是什么样子的。可能会想能不能将一个节点直接指向cluster?答案是不能!对于这种需求可以用lhead来搞定:
digraph G {
compound=true;
subgraph cluster0 {
a -> b;
a -> c;
b -> d;
c -> d;
}
subgraph cluster1 {
e -> g;
e -> f;
}
b -> f [lhead=cluster1];
d -> e;
c -> g [ltail=cluster0, lhead=cluster1];
c -> e [ltail=cluster0];
d -> h;
cluster0->cluster1;
}
生成的图片如下:
节点指向cluster