Graphviz介紹
介紹一個(gè)高效而簡(jiǎn)潔的繪圖工具graphviz。graphviz是貝爾實(shí)驗(yàn)室開發(fā)的一個(gè)開源的工具包戚丸,它使用一個(gè)特定的DSL(領(lǐng)域特定語言): dot作為腳本語言累澡,然后使用布局引擎來解析此腳本程帕,并完成自動(dòng)布局。graphviz提供豐富的導(dǎo)出格式绊诲,如常用的圖片格式,SVG褪贵,PDF格式等掂之。
graphviz中包含了眾多的布局器:
- dot 默認(rèn)布局方式,主要用于有向圖
- neato 基于spring-model(又稱force-based)算法
- twopi 徑向布局
- circo 圓環(huán)布局
- fdp 用于無向圖
本文主要介紹dot有向圖脆丁。
首先世舰,在dot腳本中定義圖的頂點(diǎn)和邊,頂點(diǎn)和邊都具有各自的屬性槽卫,比如形狀跟压,顏色,填充模式晒夹,字體裆馒,樣式等。然后使用合適的布局算法進(jìn)行布局丐怯。布局算法除了繪制各個(gè)頂點(diǎn)和邊之外喷好,需要盡可能的將頂點(diǎn)均勻的分布在畫布上,并且盡可能的減少邊的交叉(如果交叉過多读跷,就很難看清楚頂點(diǎn)之間的關(guān)系了)梗搅。所以使用graphviz的一般流程為:
- 定義一個(gè)圖,并向圖中添加需要的頂點(diǎn)和邊
- 為頂點(diǎn)和邊添加樣式
- 使用布局引擎進(jìn)行繪制
在我的機(jī)器上效览,使用Sublime Text 編輯dot腳本无切,然后將F7/Cmd-B映射為調(diào)用dot引擎去繪制當(dāng)前腳本,并打開一個(gè)新的窗口來顯示運(yùn)行結(jié)果:
Sublime Text 3 集成Graphviz方法
如下:
第一步:下載https://github.com/munro/SublimeGraphvizPreview/archive/master.zip
第二步:打開Preferences -> Packages Settings-> Packages Control -> Settings User丐枉,來確認(rèn)一下installed_packages沒有GraphVizPreview哆键。并且增加"remove_orphaned": false防止Sublime Text 把手動(dòng)安裝的插件包給刪除了。
{
"bootstrapped": true,
"in_process_packages":
[
],
"installed_packages":
[
"EncodingHelper",
"Package Control",
"Theme - Spacegray"
],
"remove_orphaned": false
}
第三步:打開Preferences -> Browse Packages...進(jìn)入到Sublime Text的插件包下Packagas瘦锹。
第四步:解壓zip文件到Packagas下籍嘹,并且更改文件夾SublimeGraphvizPreview-master為GraphVizPreview。
第五步:重啟Sublime Text弯院。
注:快捷鍵為
Win
+Shift
+G
辱士,調(diào)用Graphviz 調(diào)用dot引擎去繪制當(dāng)前腳本。
使用graphviz繪制流程圖
注:引用image圖片的時(shí)候听绳,需要使用命令颂碘,
使用 dot 命令編譯,如
dot hello.dot -T png -o hello.png
完整的命令為:
<cmd> <inputfile> -T <format> -o <o(jì)utputfile>
示例如下:
D:\\>dot -Tjpg tes.dot -o test.jpg
<cmd> |
介紹 |
---|---|
dot | 渲染的圖具有明確方向性椅挣。 |
neato | 渲染的圖缺乏方向性头岔。 |
twopi | 渲染的圖采用放射性布局塔拳。 |
circo | 渲染的圖采用環(huán)型布局。 |
fdp | 渲染的圖缺乏方向性峡竣。 |
sfdp | 渲染大型的圖蝙斜,圖片缺乏方向性。 |
基礎(chǔ)知識(shí)
graphviz包含3中元素澎胡,圖孕荠,頂點(diǎn)和邊。每個(gè)元素都可以具有各自的屬性攻谁,用來定義字體稚伍,樣式,顏色戚宦,形狀等个曙。
第一個(gè)graphviz圖
digraph abc{
a;
b;
c;
d;
a -> b;
b -> d;
c -> d;
}
定義頂點(diǎn)和邊的樣式
digraph abc{
node [shape="record"];
edge [style="dashed"];
a;
b;
c;
d;
a -> b;
b -> d;
c -> d;
}
進(jìn)一步修改頂點(diǎn)和邊樣式
將頂點(diǎn)a的顏色改為淡綠色,并將c到d的邊改為紅色受楼。
digraph abc{
node [shape="record"];
edge [style="dashed"];
a [style="filled", color="black", fillcolor="chartreuse"];
b;
c;
d;
a -> b;
b -> d;
c -> d [color="red"];
}
子圖的繪制
digraph abc{
node [shape="record"];
edge [style="dashed"];
a [style="filled", color="black", fillcolor="chartreuse"];
b;
subgraph cluster_cd{
label="c and d";
bgcolor="blue";
c;
d;
}
a -> b;
b -> d;
c -> d [color="red"];
}
注:子圖的名稱必須以cluster開頭垦搬,否則graphviz無法設(shè)別。
數(shù)據(jù)結(jié)構(gòu)的可視化
一個(gè)hash表的數(shù)據(jù)結(jié)構(gòu)
hash表內(nèi)容
struct st_hash_type {
int (*compare) ();
int (*hash) ();
};
struct st_table_entry {
unsigned int hash;
char *key;
char *record;
st_table_entry *next;
};
struct st_table {
struct st_hash_type *type;
int num_bins;
/* slot count */
int num_entries;
/* total number of entries */
struct st_table_entry **bins;
/* slot */
};
腳本如下:
digraph st2{
fontname = "Verdana";
fontsize = 10;
rankdir=TB;
node [fontname = "Verdana", fontsize = 10, color="skyblue", shape="record"];
edge [fontname = "Verdana", fontsize = 10, color="crimson", style="solid"];
st_hash_type [label="{<head>st_hash_type|(*compare)|(*hash)}"];
st_table_entry [label="{<head>st_table_entry|hash|key|record|<next>next}"];
st_table [label="{st_table|<type>type|num_bins|num_entries|<bins>bins}"];
st_table:bins -> st_table_entry:head;
st_table:type -> st_hash_type:head;
st_table_entry:next -> st_table_entry:head [style="dashed", color="forestgreen"];
}
注:在頂點(diǎn)的形狀為record的時(shí)候艳汽,label屬性的語法比較奇怪猴贰,但是使用起來非常靈活。比如河狐,用豎線”|”隔開的串會(huì)在繪制出來的節(jié)點(diǎn)中展現(xiàn)為一條分隔符米绕。用<>括起來的串稱為錨點(diǎn),當(dāng)一個(gè)節(jié)點(diǎn)具有多個(gè)錨點(diǎn)的時(shí)候馋艺,這個(gè)特性會(huì)非常有用栅干,比如節(jié)點(diǎn)st_table的type屬性指向st_hash_type,第4個(gè)屬性指向st_table_entry等捐祠,都是通過錨點(diǎn)來實(shí)現(xiàn)的碱鳞。
使用默認(rèn)的dot布局后,綠色的這條邊覆蓋了數(shù)據(jù)結(jié)構(gòu)st_table_entry踱蛀,并不美觀窿给,因此可以使用別的布局方式來重新布局,如使用circo算法可以得到更加合理的布局結(jié)果星岗。
D:\\>circo -Tjpg tes.dot -o test.jpg
hash表的實(shí)例
digraph st{
fontname = "Verdana";
fontsize = 10;
rankdir = LR;
rotate = 90;
node [ shape="record", width=.1, height=.1];
node [fontname = "Verdana", fontsize = 10, color="skyblue", shape="record"];
edge [fontname = "Verdana", fontsize = 10, color="crimson", style="solid"];
node [shape="plaintext"];
st_table [label=<
<table border="0" cellborder="1" cellspacing="0" align="left">
<tr>
<td>st_table</td>
</tr>
<tr>
<td>num_bins=5</td>
</tr>
<tr>
<td>num_entries=3</td>
</tr>
<tr>
<td port="bins">bins</td>
</tr>
</table>
>];
node [shape="record"];
num_bins [label=" <b1> | <b2> | <b3> | <b4> | <b5> ", height=2];
node[ width=2 ];
entry_1 [label="{<e>st_table_entry|<next>next}"];
entry_2 [label="{<e>st_table_entry|<next>null}"];
entry_3 [label="{<e>st_table_entry|<next>null}"];
st_table:bins -> num_bins:b1;
num_bins:b1 -> entry_1:e;
entry_1:next -> entry_2:e;
num_bins:b3 -> entry_3:e;
}
注:LR指定了左右排序方式填大。
可以看到戒洼,節(jié)點(diǎn)的label屬性支持類似于HTML語言中的TABLE形式的定義俏橘,通過行列的數(shù)目來定義節(jié)點(diǎn)的形狀,從而使得節(jié)點(diǎn)的組成更加靈活圈浇。
軟件模塊組成圖
digraph idp_modules{
rankdir = TB;
fontname = "Microsoft YaHei";
fontsize = 12;
node [ fontname = "Microsoft YaHei", fontsize = 12, shape = "record" ];
edge [ fontname = "Microsoft YaHei", fontsize = 12 ];
subgraph cluster_sl{
label="IDP支持層";
bgcolor="mintcream";
node [shape="Mrecord", color="skyblue", style="filled"];
network_mgr [label="網(wǎng)絡(luò)管理器"];
log_mgr [label="日志管理器"];
module_mgr [label="模塊管理器"];
conf_mgr [label="配置管理器"];
db_mgr [label="數(shù)據(jù)庫管理器"];
};
subgraph cluster_md{
label="可插拔模塊集";
bgcolor="lightcyan";
node [color="chartreuse2", style="filled"];
mod_dev [label="開發(fā)支持模塊"];
mod_dm [label="數(shù)據(jù)建模模塊"];
mod_dp [label="部署發(fā)布模塊"];
};
mod_dp -> mod_dev [label="依賴..."];
mod_dp -> mod_dm [label="依賴..."];
mod_dp -> module_mgr [label="安裝...", color="yellowgreen", arrowhead="none"];
mod_dev -> mod_dm [label="依賴..."];
mod_dev -> module_mgr [label="安裝...", color="yellowgreen", arrowhead="none"];
mod_dm -> module_mgr [label="安裝...", color="yellowgreen", arrowhead="none"];
}
狀態(tài)圖
digraph automata_0 {
size = "8.5, 11";
fontname = "Microsoft YaHei";
fontsize = 10;
node [shape = circle, fontname = "Microsoft YaHei", fontsize = 10];
edge [fontname = "Microsoft YaHei", fontsize = 10];
0 [ style = filled, color=lightgrey ];
2 [ shape = doublecircle ];
0 -> 2 [ label = "a " ];
0 -> 1 [ label = "other " ];
1 -> 2 [ label = "a " ];
1 -> 1 [ label = "other " ];
2 -> 2 [ label = "a " ];
2 -> 1 [ label = "other " ];
"Machine: a" [ shape = plaintext ];
}
digraph finite_state_machine {
rankdir = LR;
size = "8,5"
node [shape = doublecircle];
LR_0 LR_3 LR_4 LR_8;
node [shape = circle];
LR_0 -> LR_2 [ label = "SS(B)" ];
LR_0 -> LR_1 [ label = "SS(S)" ];
LR_1 -> LR_3 [ label = "S($end)" ];
LR_2 -> LR_6 [ label = "SS(b)" ];
LR_2 -> LR_5 [ label = "SS(a)" ];
LR_2 -> LR_4 [ label = "S(A)" ];
LR_5 -> LR_7 [ label = "S(b)" ];
LR_5 -> LR_5 [ label = "S(a)" ];
LR_6 -> LR_6 [ label = "S(b)" ];
LR_6 -> LR_5 [ label = "S(a)" ];
LR_7 -> LR_8 [ label = "S(b)" ];
LR_7 -> LR_5 [ label = "S(a)" ];
LR_8 -> LR_6 [ label = "S(b)" ];
LR_8 -> LR_5 [ label = "S(a)" ];
}
模塊的生命周期圖
digraph module_lc{
rankdir=TB;
fontname = "Microsoft YaHei";
fontsize = 12;
node [fontname = "Microsoft YaHei", fontsize = 12, shape = "Mrecord", color="skyblue", style="filled"];
edge [fontname = "Microsoft YaHei", fontsize = 12, color="darkgreen" ];
installed [label="已安裝狀態(tài)"];
resolved [label="已就緒狀態(tài)"];
uninstalled [label="已卸載狀態(tài)"];
starting [label="正在啟動(dòng)"];
active [label="已激活(運(yùn)行)狀態(tài)"];
stopping [label="正在停止"];
start [label="", shape="circle", width=0.5, fixedsize=true, style="filled", color="black"];
start -> installed [label="安裝"];
installed -> uninstalled [label="卸載"];
installed -> resolved [label="準(zhǔn)備"];
installed -> installed [label="更新"];
resolved -> installed [label="更新"];
resolved -> uninstalled [label="卸載"];
resolved -> starting [label="啟動(dòng)"];
starting -> active [label=""];
active -> stopping [label="停止"];
stopping -> resolved [label=""];
}
簡(jiǎn)單的抽象語法樹
digraph ast{
fontname = "Microsoft YaHei";
fontsize = 10;
node [shape = circle, fontname = "Microsoft YaHei", fontsize = 10];
edge [fontname = "Microsoft YaHei", fontsize = 10];
node [shape="plaintext"];
mul [label="mul(*)"];
add [label="add(+)"];
add -> 3
add -> 4;
mul -> add;
mul -> 5;
}
簡(jiǎn)單的UML類圖
digraph G{
fontname = "Courier New"
fontsize = 10
node [ fontname = "Courier New", fontsize = 10, shape = "record" ];
edge [ fontname = "Courier New", fontsize = 10 ];
Animal [ label = "{Animal |+ name : String\\l+ age : int\\l|+ die() : void\\l}" ];
subgraph clusterAnimalImpl{
bgcolor="yellow"
Dog [ label = "{Dog||+ bark() : void\\l}" ];
Cat [ label = "{Cat||+ meow() : void\\l}" ];
};
edge [ arrowhead = "empty" ];
Dog->Animal;
Cat->Animal;
Dog->Cat [arrowhead="none", label="0..*"];
}
時(shí)序圖
digraph G {
rankdir="LR";
node[shape="point", width=0, height=0];
edge[arrowhead="none", style="dashed"]
{
rank="same";
edge[style="solided"];
LC[shape="plaintext"];
LC -> step00 -> step01 -> step02 -> step03 -> step04 -> step05;
}
{
rank="same";
edge[style="solided"];
Agency[shape="plaintext"];
Agency -> step10 -> step11 -> step12 -> step13 -> step14 -> step15;
}
{
rank="same";
edge[style="solided"];
Agent[shape="plaintext"];
Agent -> step20 -> step21 -> step22 -> step23 -> step24 -> step25;
}
step00 -> step10 [label="sends email new custumer", arrowhead="normal"];
step11 -> step01 [label="declines", arrowhead="normal"];
step12 -> step02 [label="accepts", arrowhead="normal"];
step13 -> step23 [label="forward to", arrowhead="normal"];
step24 -> step14;
step14 -> step04 [arrowhead="normal"];
}
rankdir=”LR”表示寥掐,布局從左L到右R靴寂。可以看到召耘,在代碼中有{}括起來的部分百炬。
{
rank="same";
edge[style="solided"];
Agency[shape="plaintext"];
Agency -> step10 -> step11 -> step12 -> step13 -> step14 -> step15;
}
每一個(gè)rank=”same”的block中的所有節(jié)點(diǎn)都會(huì)在同一條線上。我們?cè)O(shè)置了所有的線為虛線污它,但是在該block中剖踊,將線改為solided。
如果你追求高效的開發(fā)速度衫贬,并希望快速的將自己的想法畫出來德澈,那么graphviz是一個(gè)很不錯(cuò)的選擇。
graphviz的強(qiáng)項(xiàng)在于自動(dòng)布局固惯,當(dāng)圖中的頂點(diǎn)和邊的數(shù)目變得很多的時(shí)候梆造,才能很好的體會(huì)這一特性的好處。
最后葬毫,提供Graphviz下載地址镇辉。