寫在前面
第一次了解到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="<f0> left|<f1> mid\ dle|<f2> right"];
struct2 [shape=record,label="<f0> one|<f1> two"];
struct3 [shape=record,label="hello\nworld |{ b |{c|<here> d|e}| f}| g | h"];
struct1 -> struct2;
struct1 -> struct3;
}
這個還不算厲害的,label還支持HTML格式的,這樣你能想得到的大部分樣子的節點都可以被定義出來了:
HTML格式的label
代碼如下:
digraph html {
abc [shape=none, margin=0, label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
<TR><TD ROWSPAN="3"><FONT COLOR="red">hello</FONT><BR/>world</TD>
<TD COLSPAN="3">b</TD>
<TD ROWSPAN="3" BGCOLOR="lightgrey">g</TD>
<TD ROWSPAN="3">h</TD>
</TR>
<TR><TD>c</TD>
<TD PORT="here">d</TD>
<TD>e</TD>
</TR>
<TR><TD COLSPAN="3">f</TD></TR>
</TABLE>>];
}
接著來看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