一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

使用graphviz繪制流程圖(2015版)

 王小筷 2016-01-21
原文出處: 邱俊濤的博客(@正反反長(zhǎng))   歡迎分享原創(chuàng)到伯樂(lè)頭條

2015年11月10日更新 在實(shí)踐中,我又發(fā)現(xiàn)了一些graphviz的有趣的特性,比如時(shí)序圖,rank以及圖片節(jié)點(diǎn)等。在這里一并更新。

前言

日常的開(kāi)發(fā)工作中,為代碼添加注釋是代碼可維護(hù)性的一個(gè)重要方面,但是僅僅提供注釋是不夠的,特別是當(dāng)系統(tǒng)功能越來(lái)越復(fù)雜,涉及到的模塊越來(lái)越多的時(shí)候,僅僅靠代碼就很難從宏觀的層次去理解。因此我們需要圖例的支持,圖例不僅僅包含功能之間的交互,也可以包含復(fù)雜的數(shù)據(jù)結(jié)構(gòu)的示意圖,數(shù)據(jù)流向等。

但是,常用的UML建模工具,如Visio等都略顯復(fù)雜,且體積龐大。對(duì)于開(kāi)發(fā)人員,特別是后臺(tái)開(kāi)發(fā)人員來(lái)說(shuō),命令行,腳本才是最友好的,而圖形界面會(huì)很大程度的限制開(kāi)發(fā)效率。相對(duì)于鼠標(biāo),鍵盤才是開(kāi)發(fā)人員最好的朋友。

graphviz簡(jiǎn)介

本文介紹一個(gè)高效而簡(jiǎn)潔的繪圖工具graphviz。graphviz是貝爾實(shí)驗(yàn)室開(kāi)發(fā)的一個(gè)開(kāi)源的工具包,它使用一個(gè)特定的DSL(領(lǐng)域特定語(yǔ)言): dot作為腳本語(yǔ)言,然后使用布局引擎來(lái)解析此腳本,并完成自動(dòng)布局。graphviz提供豐富的導(dǎo)出格式,如常用的圖片格式,SVG,PDF格式等。

graphviz中包含了眾多的布局器:

  • dot 默認(rèn)布局方式,主要用于有向圖
  • neato 基于spring-model(又稱force-based)算法
  • twopi 徑向布局
  • circo 圓環(huán)布局
  • fdp 用于無(wú)向圖

graphviz的設(shè)計(jì)初衷是對(duì)有向圖/無(wú)向圖等進(jìn)行自動(dòng)布局,開(kāi)發(fā)人員使用dot腳本定義圖形元素,然后選擇算法進(jìn)行布局,最終導(dǎo)出結(jié)果。

首先,在dot腳本中定義圖的頂點(diǎn)和邊,頂點(diǎn)和邊都具有各自的屬性,比如形狀,顏色,填充模式,字體,樣式等。然后使用合適的布局算法進(jìn)行布局。布局算法除了繪制各個(gè)頂點(diǎn)和邊之外,需要盡可能的將頂點(diǎn)均勻的分布在畫布上,并且盡可能的減少邊的交叉(如果交叉過(guò)多,就很難看清楚頂點(diǎn)之間的關(guān)系了)。所以使用graphviz的一般流程為:

  • 定義一個(gè)圖,并向圖中添加需要的頂點(diǎn)和邊
  • 為頂點(diǎn)和邊添加樣式
  • 使用布局引擎進(jìn)行繪制

一旦熟悉這種開(kāi)發(fā)模式,就可以快速的將你的想法繪制出來(lái)。配合一個(gè)良好的編輯器(vim/emacs)等,可以極大的提高開(kāi)發(fā)效率,與常見(jiàn)的GUI應(yīng)用的所見(jiàn)即所得模式對(duì)應(yīng),此模式稱為所思即所得。比如在我的機(jī)器上,使用Sublime Text 編輯dot腳本,然后將F7/Cmd-B映射為調(diào)用dot引擎去繪制當(dāng)前腳本,并打開(kāi)一個(gè)新的窗口來(lái)顯示運(yùn)行結(jié)果:

對(duì)于開(kāi)發(fā)人員而言,經(jīng)常會(huì)用到的圖形繪制可能包括:函數(shù)調(diào)用關(guān)系,一個(gè)復(fù)雜的數(shù)據(jù)結(jié)構(gòu),系統(tǒng)的模塊組成,抽象語(yǔ)法樹等。

基礎(chǔ)知識(shí)

graphviz包含3中元素,圖,頂點(diǎn)和邊。每個(gè)元素都可以具有各自的屬性,用來(lái)定義字體,樣式,顏色,形狀等。下面是一些簡(jiǎn)單的示例,可以幫助我們快速的了解graphviz的基本用法。

第一個(gè)graphviz圖

比如,要繪制一個(gè)有向圖,包含4個(gè)節(jié)點(diǎn)a,b,c,d。其中a指向b,b和c指向d??梢远x下列腳本:

1
2
3
4
5
6
7
8
9
10
digraph abc{
  a;
  b;
  c;
  d;
  
  a -> b;
  b -> d;
  c -> d;
}

使用dot布局方式,繪制出來(lái)的效果如下:

默認(rèn)的頂點(diǎn)中的文字為定義頂點(diǎn)變量的名稱,形狀為橢圓。邊的默認(rèn)樣式為黑色實(shí)線箭頭,我們可以在腳本中做一下修改,將頂點(diǎn)改為方形,邊改為虛線。

定義頂點(diǎn)和邊的樣式

在digraph的花括號(hào)內(nèi),添加頂點(diǎn)和邊的新定義:

1
2
node [shape="record"];
edge [style="dashed"];

則繪制的效果如下:

進(jìn)一步修改頂點(diǎn)和邊樣式

進(jìn)一步,我們將頂點(diǎn)a的顏色改為淡綠色,并將c到d的邊改為紅色,腳本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
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"];
}

繪制的結(jié)果如下:

應(yīng)當(dāng)注意到,頂點(diǎn)和邊都接受屬性的定義,形式為在頂點(diǎn)和邊的定義之后加上一個(gè)由方括號(hào)括起來(lái)的key-value列表,每個(gè)key-value對(duì)由逗號(hào)隔開(kāi)。如果圖中頂點(diǎn)和邊采用統(tǒng)一的風(fēng)格,則可以在圖定義的首部定義node, edge的屬性。比如上圖中,定義所有的頂點(diǎn)為方框,所有的邊為虛線,在具體的頂點(diǎn)和邊之后定義的屬性將覆蓋此全局屬性。如特定與a的綠色,c到d的邊的紅色。

以圖片為節(jié)點(diǎn)

除了顏色,節(jié)點(diǎn)還可以使用圖片。不過(guò)需要注意的是,在使用圖片作為節(jié)點(diǎn)的時(shí)候,需要將本來(lái)的形狀設(shè)置為none,并且將label置為空字符串,避免出現(xiàn)文字對(duì)圖片的干擾。

1
2
3
4
5
6
7
8
9
10
11
12
13
digraph abc{
  node [shape="record"];
  edge [style="dashed"];
    
  a [style="filled", color="black", fillcolor="chartreuse"];
  b;
  c [shape="none", image="logos/browser-icon-chrome-resized.png", label=""];
  d;
    
  a -> b;
  b -> d;
  c -> d [color="red"];
}

子圖的繪制

graphviz支持子圖,即圖中的部分節(jié)點(diǎn)和邊相對(duì)對(duì)立(軟件的模塊劃分經(jīng)常如此)。比如,我們可以將頂點(diǎn)c和d歸為一個(gè)子圖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
digraph abc{
  node [shape="record"];
  edge [style="dashed"];
    
  a [style="filled", color="black", fillcolor="chartreuse"];
  b;
  
    subgraph cluster_cd{
      label="c and d";
      bgcolor="mintcream";
      c;
      d;
    }
  
  a -> b;
  b -> d;
  c -> d [color="red"];
}

將c和d劃分到cluster_cd這個(gè)子圖中,標(biāo)簽為c and d,并添加背景色,以方便與主圖區(qū)分開(kāi),繪制結(jié)果如下:

應(yīng)該注意的是,子圖的名稱必須以cluster開(kāi)頭,否則graphviz無(wú)法設(shè)別。

數(shù)據(jù)結(jié)構(gòu)的可視化

實(shí)際開(kāi)發(fā)中,經(jīng)常要用到的是對(duì)復(fù)雜數(shù)據(jù)結(jié)構(gòu)的描述,graphviz提供完善的機(jī)制來(lái)繪制此類圖形。

一個(gè)hash表的數(shù)據(jù)結(jié)構(gòu)

比如一個(gè)hash表的內(nèi)容,可能具有下列結(jié)構(gòu):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 */
};

繪制hash表的數(shù)據(jù)結(jié)構(gòu)

從代碼上看,由于結(jié)構(gòu)體存在引用關(guān)系,不夠清晰,如果層次較多,則很難以記住各個(gè)結(jié)構(gòu)之間的關(guān)系,我們可以通過(guò)下圖來(lái)更清楚的展示:

腳本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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"];
}

應(yīng)該注意到,在頂點(diǎn)的形狀為record的時(shí)候,label屬性的語(yǔ)法比較奇怪,但是使用起來(lái)非常靈活。比如,用豎線”|”隔開(kāi)的串會(huì)在繪制出來(lái)的節(jié)點(diǎn)中展現(xiàn)為一條分隔符。用<>括起來(lái)的串稱為錨點(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等,都是通過(guò)錨點(diǎn)來(lái)實(shí)現(xiàn)的。

我們發(fā)現(xiàn),使用默認(rèn)的dot布局后,綠色的這條邊覆蓋了數(shù)據(jù)結(jié)構(gòu)st_table_entry,并不美觀,因此可以使用別的布局方式來(lái)重新布局,如使用circo算法:

則可以得到更加合理的布局結(jié)果。

hash表的實(shí)例

另外,這個(gè)hash表的一個(gè)實(shí)例如下:

腳本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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;
}

上例中可以看到,節(jié)點(diǎn)的label屬性支持類似于HTML語(yǔ)言中的TABLE形式的定義,通過(guò)行列的數(shù)目來(lái)定義節(jié)點(diǎn)的形狀,從而使得節(jié)點(diǎn)的組成更加靈活。

軟件模塊組成圖

Apache httpd 模塊關(guān)系

在實(shí)際的開(kāi)發(fā)中,隨著系統(tǒng)功能的完善,軟件整體的結(jié)構(gòu)會(huì)越來(lái)越復(fù)雜,通常開(kāi)發(fā)人員會(huì)將軟件劃分為可理解的多個(gè)子模塊,各個(gè)子模塊通過(guò)協(xié)作,完成各種各樣的需求。

下面有個(gè)例子,是某軟件設(shè)計(jì)時(shí)的一個(gè)草稿:

IDP支持層為一個(gè)相對(duì)獨(dú)立的子系統(tǒng),其中包括如數(shù)據(jù)庫(kù)管理器,配置信息管理器等模塊,另外為了提供更大的靈活性,將很多其他的模塊抽取出來(lái)作為外部模塊,而支持層提供一個(gè)模塊管理器,來(lái)負(fù)責(zé)加載/卸載這些外部的模塊集合。

這些模塊間的關(guān)系較為復(fù)雜,并且有部分模塊關(guān)系密切,應(yīng)歸類為一個(gè)子系統(tǒng)中,上圖對(duì)應(yīng)的dot腳本為:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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ù)庫(kù)管理器"];
      };
   
      subgraph cluster_md{
          label="可插拔模塊集";
          bgcolor="lightcyan";
          node [color="chartreuse2", style="filled"];
          mod_dev [label="開(kāi)發(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)圖

有限自動(dòng)機(jī)示意圖

上圖是一個(gè)簡(jiǎn)易有限自動(dòng)機(jī),接受a及a結(jié)尾的任意長(zhǎng)度的串。其腳本定義如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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 ];
}

形狀值為plaintext的表示不用繪制邊框,僅展示純文本內(nèi)容,這個(gè)在繪圖中,繪制指示性的文本時(shí)很有用,如上圖中的Machine: a。

OSGi中模塊的生命周期圖

OSGi中,模塊具有生命周期,從安裝到卸載,可能的狀態(tài)具有已安裝,已就緒,正在啟動(dòng),已啟動(dòng),正在停止,已卸載等。如下圖所示:

對(duì)應(yīng)的腳本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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=""];
}

其他實(shí)例

一棵簡(jiǎn)單的抽象語(yǔ)法樹(AST)

表達(dá)式 (3+4)*5 在編譯時(shí)期,會(huì)形成一棵語(yǔ)法樹,一邊在計(jì)算時(shí),先計(jì)算3+4的值,最后與5相乘。

對(duì)應(yīng)的腳本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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類圖

下面是一簡(jiǎn)單的UML類圖,Dog和Cat都是Animal的子類,Dog和Cat同屬一個(gè)包,且有可能有聯(lián)系(0..n)。

腳本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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..*"];
}

狀態(tài)圖

腳本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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)" ];
}

時(shí)序圖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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??梢钥吹?,在代碼中有{}括起來(lái)的部分。

1
2
3
4
5
6
{
    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。

附錄

事實(shí)上,從dot的語(yǔ)法及上述的示例中,很容易看出,dot腳本很容易被其他語(yǔ)言生成。比如,使用一些簡(jiǎn)單的數(shù)據(jù)庫(kù)查詢就可以生成數(shù)據(jù)庫(kù)中的ER圖的dot腳本。

如果你追求高效的開(kāi)發(fā)速度,并希望快速的將自己的想法畫出來(lái),那么graphviz是一個(gè)很不錯(cuò)的選擇。

當(dāng)然,graphviz也有一定的局限,比如繪制時(shí)序圖(序列圖)就很難實(shí)現(xiàn)。graphviz的節(jié)點(diǎn)出現(xiàn)在畫布上的位置事實(shí)上是不確定的,依賴于所使用的布局算法,而不是在腳本中出現(xiàn)的位置,這可能使剛開(kāi)始接觸graphviz的開(kāi)發(fā)人員有點(diǎn)不適應(yīng)。graphviz的強(qiáng)項(xiàng)在于自動(dòng)布局,當(dāng)圖中的頂點(diǎn)和邊的數(shù)目變得很多的時(shí)候,才能很好的體會(huì)這一特性的好處:

比如上圖,或者較上圖更復(fù)雜的圖,如果采用手工繪制顯然是不可能的,只能通過(guò)graphviz提供的自動(dòng)布局引擎來(lái)完成。如果僅用于展示模塊間的關(guān)系,子模塊與子模塊間通信的方式,模塊的邏輯位置等,graphviz完全可以勝任,但是如果圖中對(duì)象的物理位置必須是準(zhǔn)確的,如節(jié)點(diǎn)A必須位于左上角,節(jié)點(diǎn)B必須與A相鄰等特性,使用graphviz則很難做到。畢竟,它的強(qiáng)項(xiàng)是自動(dòng)布局,事實(shí)上,所有的節(jié)點(diǎn)對(duì)與布局引擎而言,權(quán)重在初始時(shí)都是相同的,只是在渲染之后,節(jié)點(diǎn)的大小,形狀等特性才會(huì)影響權(quán)重。

本文只是初步介紹了graphviz的簡(jiǎn)單應(yīng)用,如圖的定義,頂點(diǎn)/邊的屬性定義,如果運(yùn)行等,事實(shí)上還有很多的屬性,如畫布的大小,字體的選擇,顏色列表等,大家可以通過(guò)graphviz的官網(wǎng)來(lái)找到更詳細(xì)的資料。

文中的代碼都已經(jīng)在Github上。

1 贊 2 收藏 評(píng)論

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    欧美黑人在线一区二区| 91在线爽的少妇嗷嗷叫| 我想看亚洲一级黄色录像| 欧美熟妇一区二区在线| 大香蕉网国产在线观看av| 国产午夜精品美女露脸视频| 久久99精品日韩人妻| 国产福利一区二区久久| 午夜午夜精品一区二区| 欧美中文字幕一区在线| 91欧美日韩国产在线观看| 国产爆操白丝美女在线观看| 一个人的久久精彩视频| 日韩一区二区三区四区乱码视频| 麻豆tv传媒在线观看| 精品国产成人av一区二区三区| 蜜桃av人妻精品一区二区三区| 欧美多人疯狂性战派对| 好骚国产99在线中文| 亚洲欧美国产网爆精品| 日韩精品你懂的在线观看| 欧美色婷婷综合狠狠爱| 好骚国产99在线中文| 欧美大胆女人的大胆人体| 嫩呦国产一区二区三区av| 色鬼综合久久鬼色88| 欧美国产在线观看精品| 儿媳妇的诱惑中文字幕| 国产亚洲精品久久99| 欧美激情区一区二区三区| 国产亚洲午夜高清国产拍精品| 国产精品久久熟女吞精| 国产一区二区三区丝袜不卡| 久久99爱爱视频视频| 欧美自拍偷自拍亚洲精品| 丝袜人妻夜夜爽一区二区三区| 国产传媒免费观看视频| 国产精品十八禁亚洲黄污免费观看 | 日韩黄色一级片免费收看| 在线免费国产一区二区三区 | 尤物久久91欧美人禽亚洲|