2016-4-25 Sublime Text + Graphviz + Dot = 有向圖.md

Graphviz介紹

介紹一個高效而簡潔的繪圖工具graphviz。graphviz是貝爾實驗室開發的一個開源的工具包,它使用一個特定的DSL(領域特定語言): dot作為腳本語言,然后使用布局引擎來解析此腳本,并完成自動布局。graphviz提供豐富的導出格式,如常用的圖片格式,SVG,PDF格式等。

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

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

本文主要介紹dot有向圖。

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

  1. 定義一個圖,并向圖中添加需要的頂點和邊
  2. 為頂點和邊添加樣式
  3. 使用布局引擎進行繪制

在我的機器上,使用Sublime Text 編輯dot腳本,然后將F7/Cmd-B映射為調用dot引擎去繪制當前腳本,并打開一個新的窗口來顯示運行結果:

Sublime Text 3 集成Graphviz方法

如下:
第一步:下載https://github.com/munro/SublimeGraphvizPreview/archive/master.zip
第二步:打開Preferences -> Packages Settings-> Packages Control -> Settings User,來確認一下installed_packages沒有GraphVizPreview。并且增加"remove_orphaned": false防止Sublime Text 把手動安裝的插件包給刪除了。

{
  "bootstrapped": true,
  "in_process_packages":
  [
  ],
  "installed_packages":
  [
  "EncodingHelper",
  "Package Control",
  "Theme - Spacegray"
  ],
  "remove_orphaned": false
}

第三步:打開Preferences -> Browse Packages...進入到Sublime Text的插件包下Packagas。
第四步:解壓zip文件到Packagas下,并且更改文件夾SublimeGraphvizPreview-master為GraphVizPreview。
第五步:重啟Sublime Text。

注:快捷鍵為Win+Shift+G,調用Graphviz 調用dot引擎去繪制當前腳本。

使用graphviz繪制流程圖

Paste_Image.png

注:引用image圖片的時候,需要使用命令,
使用 dot 命令編譯,如

dot hello.dot -T png -o hello.png

完整的命令為:

<cmd> <inputfile> -T <format> -o <outputfile>

示例如下:

D:\\>dot -Tjpg tes.dot -o test.jpg
<cmd> 介紹
dot 渲染的圖具有明確方向性。
neato 渲染的圖缺乏方向性。
twopi 渲染的圖采用放射性布局。
circo 渲染的圖采用環型布局。
fdp 渲染的圖缺乏方向性。
sfdp 渲染大型的圖,圖片缺乏方向性。

基礎知識

graphviz包含3中元素,圖,頂點和邊。每個元素都可以具有各自的屬性,用來定義字體,樣式,顏色,形狀等。

第一個graphviz圖

digraph abc{
  a;
  b;
  c;
  d;
 
  a -> b;
  b -> d;
  c -> d;
}
Paste_Image.png

定義頂點和邊的樣式

digraph abc{
  node [shape="record"];
  edge [style="dashed"];
  a;
  b;
  c;
  d;
 
  a -> b;
  b -> d;
  c -> d;
}
Paste_Image.png

進一步修改頂點和邊樣式

將頂點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"];
}
Paste_Image.png

子圖的繪制

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"];
}
Paste_Image.png

注:子圖的名稱必須以cluster開頭,否則graphviz無法設別。

數據結構的可視化

一個hash表的數據結構

hash表內容

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"];
}
Paste_Image.png

注:在頂點的形狀為record的時候,label屬性的語法比較奇怪,但是使用起來非常靈活。比如,用豎線”|”隔開的串會在繪制出來的節點中展現為一條分隔符。用<>括起來的串稱為錨點,當一個節點具有多個錨點的時候,這個特性會非常有用,比如節點st_table的type屬性指向st_hash_type,第4個屬性指向st_table_entry等,都是通過錨點來實現的。

使用默認的dot布局后,綠色的這條邊覆蓋了數據結構st_table_entry,并不美觀,因此可以使用別的布局方式來重新布局,如使用circo算法可以得到更加合理的布局結果。

D:\\>circo -Tjpg tes.dot -o test.jpg
Paste_Image.png

hash表的實例

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;
}
Paste_Image.png

注:LR指定了左右排序方式。
可以看到,節點的label屬性支持類似于HTML語言中的TABLE形式的定義,通過行列的數目來定義節點的形狀,從而使得節點的組成更加靈活。

軟件模塊組成圖

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="網絡管理器"];
          log_mgr [label="日志管理器"];
          module_mgr [label="模塊管理器"];
          conf_mgr [label="配置管理器"];
          db_mgr [label="數據庫管理器"];
      };
 
      subgraph cluster_md{
          label="可插拔模塊集";
          bgcolor="lightcyan";
          node [color="chartreuse2", style="filled"];
          mod_dev [label="開發支持模塊"];
          mod_dm [label="數據建模模塊"];
          mod_dp [label="部署發布模塊"];
      };
 
  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"];
}
Paste_Image.png

狀態圖

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 ];
}
Paste_Image.png
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)" ];
}
Paste_Image.png

模塊的生命周期圖

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="已安裝狀態"];
  resolved [label="已就緒狀態"];
  uninstalled [label="已卸載狀態"];
  starting [label="正在啟動"];
  active [label="已激活(運行)狀態"];
  stopping [label="正在停止"];
  start [label="", shape="circle", width=0.5, fixedsize=true, style="filled", color="black"];
 
  start -> installed [label="安裝"];
  installed -> uninstalled [label="卸載"];
  installed -> resolved [label="準備"];
  installed -> installed [label="更新"];
  resolved -> installed [label="更新"];
  resolved -> uninstalled [label="卸載"];
  resolved -> starting [label="啟動"];
  starting -> active [label=""];
  active -> stopping [label="停止"];
  stopping -> resolved [label=""];
}
Paste_Image.png

簡單的抽象語法樹

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;
}
Paste_Image.png

簡單的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..*"];
}
Paste_Image.png

時序圖

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??梢钥吹剑诖a中有{}括起來的部分。

{
    rank="same";
    edge[style="solided"];
    Agency[shape="plaintext"];
    Agency -> step10 -> step11 -> step12 -> step13 -> step14 -> step15;
}

每一個rank=”same”的block中的所有節點都會在同一條線上。我們設置了所有的線為虛線,但是在該block中,將線改為solided。

Paste_Image.png

如果你追求高效的開發速度,并希望快速的將自己的想法畫出來,那么graphviz是一個很不錯的選擇。
graphviz的強項在于自動布局,當圖中的頂點和邊的數目變得很多的時候,才能很好的體會這一特性的好處。

最后,提供Graphviz下載地址。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容