概述
Markdown 是一個輕量級的標記語言,語法簡單、易於上手,深受程式設計師、博主等人群的鐘愛。Markdown 工具鏈也非常豐富,如graphviz軟體,就是可以嵌入markdown文字,進行思維導圖、流程圖、系統框圖繪製的利器,讓你心無旁騖,全心投入到系統架構、軟體、演算法的設計中。
在markdown使用 graphviz 語法,推薦使用 vnote 軟體
graphviz 是 AT&T的bell實驗室開源的指令碼自動化繪圖軟體,其主要用於繪製關係圖,自動排版,有效提升工作效率。同時,其也是自動化繪圖工具plantuml、很多資料視覺化工具的基礎。類似於python中的matplotlib。
graphviz 的基本結構如下所示,其主要包含三部分:
- 1) layout 自動化佈局工具:dot,neato等,本文重點講解dot的使用。
- 2) script指令碼:主要包含 graph,node,edge三類實體,以及attributes屬性;
- 3)APIs:若需要在其它語言中呼叫,graphviz提供了 C,java,python,php等語言的API。
graphviz的dot的基本使用也非常簡單,流程如下:
graphviz示例,也是上述基礎流程的dot指令碼程式碼如下:
digraph base_flow {
// 步驟1: 定義digraph的屬性
label = <<B>graphviz使用流程</B>>;
// 步驟2: 定義node、edge的屬性
node[shape=box];
// 步驟3: 新增node、edge
graph_attr -> node_edge_attr -> node_edge_added -> custom_attr;
// 步驟4: 定義特定node,edge的屬性
graph_attr[label="1. 定義digraph的屬性"];
node_edge_attr[label="2. 定義node、edge的屬性"];
node_edge_added[label="3. 新增node、edge"];
custom_attr[label="4. 定義特定node,edge的屬性"];
}
graphviz 指令碼語法結構
Graphviz 語法非常簡單,主要由程式碼塊、語句、識別符號、註釋等幾部分組成:
- 程式碼塊: 位於
{}
語句中的語句即為程式碼塊; - 語句:以
;
結尾,主要分為程式碼塊,節點,連線,屬性四種語句型別; - 實體物件識別符號:除了特殊字元外的所有字元都可以用於識別符號,如數字,中英文字串等;
- 註釋:
//
表示單行註釋,/*...*/
表示多行註釋。
圖
graphviz 圖實體主要分三類:
- graph: 1)
digraph {...}
定義有向圖;2)graph {...}
定義無向圖; - subgraph:
subgraph {...}
定義子圖; - cluster subgraph:
subgraph cluster_xxx {...}
定義的程式碼塊
子圖的型別(有向圖還是無向圖)與父圖相同,子圖的名稱以cluster開頭才被當成聚集子圖渲染。示例如下:
digraph graph_name{
bgcolor="transparent";//背景透明
subgraph cluster_subgraph_name{//聚集子圖
node[shape=box];
cluster_A -> cluster_B;
}
subgraph subgraph_name{//子圖
node[shape=none];
sub_A -> sub_B;
}
{//匿名子圖
node[shape=octagon];
nest_A -> nest_B;
}
global_A -> global_B;
cluster_B -> global_B;
sub_B -> global_B;
nest_B -> global_B;
}
方向,尺寸,間距
兩個重要的屬性決定了圖的尺寸,分別為 nodesep, ranksep。
nodesep
: 同一個 rank 中的相鄰節點的最小距離,單位為英寸(=2.54 cm)。直線的不同端點屬於不同的 rank;ranksep
: 相鄰 rank 之間的距離;rankdir
: rank的指向,如 LR (left to right) or RL,或者 TB (top to bottom) or BT;
digraph G {
nodesep = 2;
ranksep = 1;
rankdir = LR;
a -> b;
c;
b -> d;
}
節點
graphviz的節點的定義方式如下:
節點識別符號[節點屬性]
所有的節點屬性見官網的節點屬性介紹,下面,介紹常用的節點屬性。
當想讓節點的形狀完全由類HTML的標籤設定時,一般設定屬性 width=0, height=0, margin=0 。
shape 屬性
graphviz 主要有三種型別的形狀(shape)型別:多邊形(polygon), 記錄(record)形狀, 使用者定義(user-defined)形狀。基於記錄的形狀在很大程度上已經被類似HTML的標籤所取代。也就是說,可以考慮使用shape=none、margin=0和類HTML標籤,而不是使用shape=record。
多邊形
所有的多邊形的屬性值與形狀如下,其中 rect/rectangle 與 box 是同義詞,none 與 plaintext 是同義詞。
當想讓節點的形狀完全由類HTML的標籤設定時,一般設定屬性 width 0, height=0, margin=0 。
record-based 的形狀
基於 record的形狀,是指節點的屬性為 record 或者 Mrecord 的節點,其節點的表現形式由 label 屬性決定。 record 與 Mrecord 的區別在於 Mrecord 的外圍是圓角。
label 屬性的語法結構如下:
- 不同的欄位使用
|
隔開; - 欄位的 portname 使用
<...>
尖括號括起來; - 在
{...}
中的內容,在水平和垂直佈局之間翻轉,取決於 graph 的 rankdir 屬性:1)若graph[rankdir=TB]
,則整體圖片垂直佈局,{...}
中的內容垂直佈局。2)graph[rankdir=LR]
,則整體圖片水平佈局,{...}
中的內容水平佈局;
digraph structs {
node[shape=record];
graph[rankdir=TB];
struct1[label="<f0> left|<f1> mid\ dle|<f2> right"];
struct2[label="<f0> one|<f1> two"];
struct3[label="hello\nworld |{ b |{c|<here> d|e}| f}| g | h"];
struct1:f1 -> struct2:f0;
struct1:f2 -> struct3:here;
}
修改 graph 屬性為 LR,則整體水平佈局。
digraph structs {
node[shape=record];
graph[rankdir=LR];
struct1[label="<f0> left|<f1> mid\ dle|<f2> right"];
struct2[label="<f0> one|<f1> two"];
struct3[label="hello\nworld |{ b |{c|<here> d|e}| f}| g | h"];
struct1:f1 -> struct2:f0;
struct1:f2 -> struct3:here;
}
使用者定製
使用者定製圖形有幾種方式:
- 通過類HTML的 label 使用 IMG 屬性載入使用者定製的影像;
digraph structs {
node [shape=plaintext];
struct1 [label=<<TABLE>
<TR><TD><IMG SRC="eqn.png"/></TD></TR>
<TR><TD>caption</TD></TR>
</TABLE>>];
}
- 如果使用的是 SVG (-Tsvg),或者 postScript (-Tps, -Tps2) 或者 光柵格式 (-Tgif, -Tpng, 或者-Tjpg),可以通過指定影像檔名載入圖片,例如:
graph pic_test {
your_pic[shape=none, label="", imagepath="D:\\cloud_sync\\vnote_book\\效率工具", image="test.png"];
}
關於 image 的路徑,參考環境變數的設定
label 屬性
基本用法
label 屬性的基本用法是設定節點的文字顯示,若節點沒有顯示設定label屬性,則文字顯示節點的識別符號。
graph lebel_demo {
node1;
node2[label="文字顯示"];
}
HTML用法
label 屬性HTML用法是將節點轉換為一個類似於HTML的實體,實體的具體呈現完全由HTML指令碼語言控制,其注意事項如下:
- 節點的 shape 屬性設定為 record 或 none,建議 none ;
- 節點的 寬、高、邊緣屬性設定為0:width=0, height=0, margin=0;
- 節點的 label 屬性字串通過尖括號
<...>
包含HTML語法字串;
示例:使用HTML實現一個表格節點實體(注意:html不區分大小寫)。
digraph html_label_Example
{
label = "html_like_label_example";
bgcolor="transparent";//背景透明
html_ex_node[shape=record, margin=0, label=<
<table border="0" cellborder="1" cellspacing="0" cellpadding="4">
<tr>
<td rowspan="2">test</td>
<td>a</td>
<td rowspan="2">HTML-Like<br/>label</td>
</tr>
<tr>
<td>b</td>
</tr>
</table>
>];
}
graphviz不支援所有的html-4的語法,目前只支援如下的html-like語法:
label : text
| fonttable
text : textitem
| text textitem
textitem : string
| <BR/>
| <FONT> text </FONT>
| <I> text </I>
| <B> text </B>
| <U> text </U>
| <O> text </O>
| <SUB> text </SUB>
| <SUP> text </SUP>
| <S> text </S>
fonttable : table
| <FONT> table </FONT>
| <I> table </I>
| <B> table </B>
| <U> table </U>
| <O> table </O>
table : <TABLE> rows </TABLE>
rows : row
| rows row
| rows <HR/> row
row : <TR> cells </TR>
cells : cell
| cells cell
| cells <VR/> cell
cell : <TD> label </TD>
| <TD> <IMG/> </TD>
更多的html-like語法見 graphviz官網中對record節點的介紹
style 屬性
style 屬性用於修改節點的外觀,當前,支援8種型別的 style:filled, invisible, diagonals, rounded. dashed, dotted, solid, bold
filled
: 此值指示應填充節點的內部。使用的顏色是 fillcolor 定義的,若 fillcolor 屬性未定義,則使用 color 屬性的顏色。對於未填充的節點,節點內部對當前圖形或簇背景色的任何顏色都是透明的。請注意,點形狀始終是填充的。
digraph G {
rankdir=LR
node [shape=box, color=blue]
node1 [style=filled]
node2 [style=filled, fillcolor=red]
node0 -> node1 -> node2
}
invisible
: 不可見。設定此樣式會導致節點根本不顯示。請注意,節點仍用於佈局圖形。diagonals
: 斜線 。“斜線”樣式會導致在節點多邊形的頂點附近繪製小斜線。
digraph G {
rankdir=LR
node [shape=box, color=blue]
node0 [style=diagonals]
}
rounded
:圓形的,使節點的邊變得圓滑,可以作用在 record 形狀上。
digraph R {
rankdir=LR
node [style=rounded]
node1 [shape=box]
node2 [fillcolor=yellow, style="rounded,filled", shape=diamond]
node3 [shape=record, label="{ a | b | c }"]
node1 -> node2 -> node3
}
-
dashed
: 使節點的邊變為虛線; -
dotted
: 使節點的邊變為點線; -
solid
: 使節點的邊變為直線,預設屬性; -
bold
: 使節點的邊線加粗。
port 屬性
節點的 port 屬性是指節點連線另一個節點的線條端點位置,埠的位置有8種,分別為節點的東、南、西、北、東南、東北、西南、西北,屬性的值分別為e, s, w, n, se, ne, sw, nw
有兩種型別的 port 屬性:
- 一種使指定源節點的端點位置,使用
tailport
屬性,如下指令碼指定 a節點的端點位置為東:
digraph G {
a -> b [tailport = e];
}
- 一種指定目的節點的端點位置,使用
:pos
語法,如下指令碼指定b節點的端點位置為西:
digraph G {
a -> b:w;
}
也可以通過上述語法指定 record 形狀的域欄位(如f1)的端點位置:
digraph G {
a -> b:f1:w;
}
參考
附錄
Graphviz基本組成結構dot程式碼
digraph gv_basic_structure{
label=<<B>Graphviz基本組成結構</B>>;
node[shape=box];
graphviz[label="Graphviz"];
subgraph{
layout[label="Layouts"];
script[label="Script Files"];
api[label="APIs"]
rank=same;
}
graphviz -> layout;
graphviz -> script;
graphviz -> api;
script ->
subgraph{
element[label="Elements"];
attribute[label="Attributes"];
rank=same;
}
layout ->
subgraph{
layout_dot[label="dot", color="red"];
layout_neato[label="neato"];
layout_etc[label="......"];
}
element ->
subgraph{
ele_graph[label="Graph"];
ele_node[label="Node"];
ele_edge[label="Edge"];
}
}