Graphivz
绘图
sudo apt-get install graphviz graphviz-doc
dot :file ./img/example1.png :cmdline -Kdot -Tpng
里的:cmdline -Kdot -Tpng
就是命令行参数. 他们告诉dot如何渲染和展示。
-
-Kdot
使用dot布局方式. 你也可以尝试其他的布局方式,比如Kneato, Kcirco, Ktwopi, Kfdp, Ksfdp -
-Tpng
渲染成png格式
graphviz默认情况下对中文支持不好,如果代码中有中文,则必须使用UTF-8的格式保存文件,并且在代码中指定字体。
基本概念
安装graphviz工具,dot作为图形工具对dot源文件调整。
常用属性
所有结点通用属性
属性名称 | 默认值 | 含义 |
---|---|---|
color | black | 颜色 |
colorscheme | X11 | 颜色描述 |
fontcolor | black | 文字颜色 |
fontname | Times-Roman | 字体 |
fontsize | 14 | 文字大小 |
label | 显示的标签,对于节点默认为节点名称 | |
penwidth | 1.0 | 线条宽度 |
style | 样式 | |
weight | 重要性 |
图像属性
-
label="My Graph"
给图像设置标签 -
rankdir=LR
将图片由原来的从上到下布局变成从左到右布局 -
{rank=same; a, b, c }
将一组元素放到同一个level -
splines="line"
让边框变为直线,没有曲线和锐角 -
K=0.6
用来在布局中影响spring属性,spring属性可以用于将节点往外推,这个在twopi和sfdp布局中很有用。
译注:暂时还没明白这个spring属性应该怎么翻,初步猜测是弹性。胡克定律里面的常量名也叫K。
属性名称 | 默认值 | 含义 |
---|---|---|
bgcolor | 背景颜色 | |
concentrate | false | 让多条边有公共部分 |
nodesep | .25 | 节点之间的间隔(英寸) |
peripheries | 1 | 边界数 |
rank | same,min,source, max,sink,设置多个节点顺序 | |
rankdir | TB | 排序方向 |
ranksep | .75 | 间隔 |
size | 图的大小(英寸) |
交点属性
-
[label="Some Label"]
给交点打标签 -
[color="red"]
给交点上色 -
[fillcolor="blue"]
设置交点的填充色
属性名称 | 默认值 | 含义 |
---|---|---|
shape | ellipse | 形状 |
sides | 4 | 当shape=polygon时的边数 |
fillcolor | lightgrey/black | 填充颜色 |
fixedsize | false | 标签是否影响节点的大小 |
边的属性
-
[label="Some Label"]
给边设置标签 (设置路径权重的时候很有用) -
[color="red"]
给交点上色 (标示路径的时候很有用) -
[penwidth=2.0]
给边适配厚度,标示路径的时候很有用。
属性名称 | 默认值 | 含义 |
---|---|---|
arrowhead | normal | 箭头头部形状 |
arrowsize | 1.0 | 箭头大小 |
arrowtail | normal | 箭头尾部形状 |
constraint | true | 是否根据边来影响节点的排序 |
decorate | 设置之后会用一条线来连接edge和label | |
dir | forward | 设置方向:forward,back,both,none |
headclip | true | 是否到边界为止 |
tailclip | true | 与headclip类似 |
尺寸, 背景颜色
-
fixedsize=true
-
size="1,1"
-
resolution=72
-
bgcolor="#C6CFD532"
基本元素
无向图
graph
绘制无向图:
graph gp0101 { a -- b; b -- c; b -- d; d -- a; }
用{}
可以代表多个结点:
graph gh0106 { a -- {b, c, d, e}; }
另一个例子:
graph gh0107 { {a, b, c, d} -- e; }
节点的形状
digraph gp0501 { a [shape=box] b [shape=polygon,sides=6] c [shape=triangle] d [shape=invtriangle] e [shape=polygon,sides=4,skew=.5] f [shape=polygon,sides=4,distortion=.5] g [shape=diamond] h [shape=Mdiamond] i [shape=Msquare] a - b a - c a - d a - e a - f a - g a - h a - i }
graph gp0502 { a [style=filled,color=green] b [peripheries=4,color=blue] c [fontcolor=crimson] d [style=filled,fillcolor=dodgerblue,color=coral4,penwidth=3] e [style=dotted] f [style=dashed] g [style=diagonals] h [style=filled,color="#333399"] i [style=filled,color="#ff000055"] j [shape=box,style=striped,fillcolor="red:green:blue"] k [style=wedged,fillcolor="green:white:red"] a -> b a -> c a -> d a -> e b -> f b -> g b -> h b -> i d -> j j -> k }
指定布局方向
graph gp0102 { rankdir=LR; //Rank Direction Left to Right a -- b; b -- c; b -- d; d -- a; }
指定有向图
有向图不能用graph
,要用digraph
:
digraph gp0103 { a -> b; b -> c; a -> c; }
另一个例子:
digraph gh0107 { a -> {b, c, d, e}; }
另一个例子:
digraph gh0109 { {a, b, c, d} -> e; }
digraph db0503 { a -> b [dir=both,arrowhead=open,arrowtail=inv] a -> c [dir=both,arrowhead=dot,arrowtail=invdot] a -> d [dir=both,arrowhead=odot,arrowtail=invodot] a -> e [dir=both,arrowhead=tee,arrowtail=empty] a -> f [dir=both,arrowhead=halfopen,arrowtail=crow] a -> g [dir=both,arrowhead=diamond,arrowtail=box] }
digraph dg0504 { a -> b [color="black:red:blue"] a -> c [color="black:red;0.5:blue"] a -> d [dir=none,color="green:red:blue"] a -> e [dir=none,color="green:red;.3:blue"] a -> f [dir=none,color="orange"] d -> g [arrowsize=2.5] d -> h [style=dashed] d -> i [style=dotted] d -> j [penwidth=5] }
加上说明标签
digraph graphname{ T [label="Teacher"]; // node T P [label="Pupil"]; // node P T->P [label="Instructions", fontcolor=darkgreen] ; // edge T->P }
节点顺序
digraph MyGraph { rankdir=LR a -> b b -> c a -> d a -> c {rank=same;c;b} }
digraph MyGraph { subgraph cluster_A { a1 -> a2 a2 -> a3 {rank=same;a1;a2;a3} } subgraph cluster_B { a3 -> b1 b1 -> b2 b2 -> b3 {rank=same;b1;b2;b3} } begin -> a1 }
digraph MyGraph { subgraph cluster_A { a1 a2 a3 {rank=same;a1;a2;a3} } subgraph cluster_B { b1 b2 b3 {rank=same;b1;b2;b3} } begin -> a1 a1 -> a2 [constraint=false] a2 -> a3 [constraint=false] a3 -> b1 b1 -> b2 b2 -> b3 }
The default value for ranksep
is .5
.
digraph MyGraph { rankdir=LR ranksep=1 a -> b b -> c c -> d }
指定样式
常用属性:
-
color
-
fontcolor
-
fontsize
-
shape
digraph gh0105 { // node T T [label="Teacher" color=Blue, fontcolor=Red, fontsize=24, shape=box]; // node P P [label="Pupil" color=Blue, fontcolor=Red, fontsize=24, shape=box]; T->P [label="Instructions", fontcolor=darkgreen] // edge T->P }
更多形状看文档:https://graphviz.gitlab.io/_pages/doc/info/shapes.html
高级特性
定义复用组件
digraph gp0201 { nodesep=1.0 // increases the separation between nodes //All nodes will this shape and colour node [color=Red, fontname=Courier, shape=box] edge [color=Blue, style=dashed] //All the lines look like this Headteacher -> {Deputy1 Deputy2 BusinessManager} Deputy1 -> {Teacher1 Teacher2} BusinessManager -> ITManager {rank=same; ITManager Teacher1 Teacher2} // Put them on the same level }
定义数据结构
指定形状为record
开描述数据结构:
digraph gp0202 { node[shape=record] struct1 [label="<f0> left|<f1> mid\ dle|<f2> right"]; struct2 [label="{<f0> one|<f1> two\n\n\n}" shape=Mrecord]; struct3 [label="hello\nworld |{ b |{c|<here> d|e}| f}| g | h"]; struct1:f1 -> struct2:f0; struct1:f0 -> struct3; }
另一个例子:
digraph gp0203 { node[shape=record] store1 [label="<f0> left|<f1> Some data store"]; proc1 [label="{<f0> 1.0|<f1> Some process here\n\n\n}" shape=Mrecord]; enti1 [label="Customer" shape=box]; store1:f1 -> proc1:f0; enti1-> proc1:f0; }
使用HTML样式
digraph gp0205 { 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>>]; }
digraph dg0508 { a [shape=plaintext,label=< <table> <tr> <td color="#ff0000" bgcolor="#008822"><font color="#55ff00">Hello</font></td> <td>world!</td> </tr> <tr> <td colspan="2" color="#00ff00" bgcolor="#ff0000"> <font color="#ffffff">are you ok?</font> </td> </tr> </table> >] }
digraph dg0507 { a [shape=plaintext,label=< <table> <tr> <td>Hello</td> <td>world!</td> </tr> <tr> <td colspan="2" port="a1">are you ok?</td> </tr> </table> >] b [shape=plaintext,label=< <table border="0" cellborder="1" cellspacing="0"> <tr> <td rowspan="3">left</td> <td>top</td> <td rowspan="3" port="b2">right</td> </tr> <tr> <td port="b1">center</td> </tr> <tr> <td>bottom</td> </tr> </table> >] a:a1 -> b:b1 a:a1 -> b:b2 }
子图
subgraph
定义子图:
digraph gp0203 { node[shape=record] subgraph level0{ enti1 [label="Customer" shape=box]; enti2 [label="Manager" shape=box]; } subgraph cluster_level1{ label ="Level 1"; proc1 [label="{<f0> 1.0|<f1> One process here\n\n\n}" shape=Mrecord]; proc2 [label="{<f0> 2.0|<f1> Other process here\n\n\n}" shape=Mrecord]; store1 [label="<f0> |<f1> Data store one"]; store2 [label="<f0> |<f1> Data store two"]; {rank=same; store1, store2} } enti1 -> proc1 enti2 -> proc2 store1 -> proc1 store2 -> proc2 proc1 -> store2 store2 -> proc1 }
子布局
在dot中以cluster*
开头的子图会被当做是一个新的布局来处理,
而不是在原图的基础上继续操作。
digraph dg0505 { subgraph cluster_a { b c -> d } a -> b d -> e }
digraph dg0506 { subgraph cluster_a { subgraph cluster_b { subgraph cluster_c { d } c -> d } b -> c } a -> b d -> e }
digraph gp0206 { 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 gp0207 { 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; }
图像节点
digraph MyGraph { ec2 [shape=none,label="",image="icons/ec2.png"] igw [shape=none,label="",image="icons/igw.png"] rds [shape=none,label="",image="icons/rds.png"] vpc [shape=none,label="",image="icons/vpc.png"] subgraph cluster_vpc { label="VPC" subgraph cluster_public_subnet { label="Public Subnet" ec2 } subgraph cluster_private_subnet { label="Private Subnet" ec2 -> rds } vpc igw -> ec2 } users -> igw }
常用例子
UML
类图
digraph gp0301 { node[shape=record]; rankdir="BT"; teacher [label = "{<f0> Teacher | <f1> \n | <f2> \n }"]; course [label = "{<f0> Course | <f1> \n | <f2> \n }"]; student [label = "{<f0> Student | <f1> \n | <f2> \n }"]; lesson [label = "{<f0> Lesson | <f1> \n | <f2> \n }"]; tutorial [label = "{<f0> Tutorial| <f1> \n | <f2> \n }"]; assessment [label = "{<f0> Assessment|<f1> \n | <f2> \n }"]; coursework [label = "{<f0> Coursework|<f1> \n | <f2> \n }"]; exam [label = "{<f0> Exam|<f1> \n | <f2> \n }"]; {rank=same; teacher course student} teacher -> course [dir="forward", arrowhead="none", arrowtail="normal", headlabel="1", taillabel="1.."]; student -> course [dir="forward", arrowhead="none", arrowtail="normal", headlabel="1", taillabel="1.."]; lesson -> course [dir="forward", arrowhead="diamond", arrowtail="normal"]; tutorial -> course [dir="forward", arrowhead="diamond", arrowtail="normal"]; assessment -> course [dir="forward", arrowhead="diamond", arrowtail="normal"]; coursework -> assessment; exam -> assessment; }
ER图
digraph gp0302 { node[shape=box]; Book; Customer; Loan; {rank=same; Book, Loan, Customer} Book -> Loan[dir="forward", arrowhead="crow", arrowtail="normal"]; Customer -> Loan[dir="forward", arrowhead="crow", arrowtail="normal"]; }
数据流
digraph gp0203 { node[shape=record] subgraph level0{ enti1 [label="Customer" shape=box]; enti2 [label="Manager" shape=box]; } subgraph cluster_level1{ label ="Level 1"; proc1 [label="{<f0> 1.0|<f1> One process here\n\n\n}" shape=Mrecord]; proc2 [label="{<f0> 2.0|<f1> Other process here\n\n\n}" shape=Mrecord]; store1 [label="<f0> |<f1> Data store one"]; store2 [label="<f0> |<f1> Data store two"]; {rank=same; store1, store2} } enti1 -> proc1 enti2 -> proc2 store1 -> proc1 store2 -> proc2 proc1 -> store2 store2 -> proc1 }
进程图
digraph gp0305 { subgraph cluster0 { node[style=filled, color=white]; style=filled; color=lightgrey; a0 -> a1 -> a2 -> a3; label="process #1"; } subgraph cluster1 { node[style=filled]; color=blue; b0 -> b1 -> b2 -> b3; label="process #2"; } start -> a0; start -> b0; a1 -> b3; b2 -> a3; a3 -> end; b3 -> end; start[shape=Mdiamond]; end[shape=Msquare]; }
函数调用关系
digraph gp0307 { node [shape=ellipse, style=filled, color="#40e0d0"]; edge [color="#606060", penwidth=3]; main [color=green]; main -> init[color=blue, label="hello, I'm \llable of edge"]; main -> mainloop; main -> exit; init -> a_init; init -> b_init; init -> c_init; mainloop -> select; }
label中\r
, \l
, \n
都表示换行,但对齐方式分别是右对齐,左对齐,居中。
状态机
digraph gp0308 { accept -> TS_HTTP_POST_REMAP_HOOK; TS_HTTP_POST_REMAP_HOOK -> "check request method (and header)"; "check request method (and header)" -> "get CacheUrl hash_key using MurmurHash3" [label = "GET request (required_header present)"]; "check request method (and header)" -> "pass request" [label = "others"]; "get CacheUrl hash_key using MurmurHash3" -> "check hash_key from hashTable"; "check hash_key from hashTable" -> "lock URL in hashTable" [label = "not found"]; "check hash_key from hashTable" -> "pass request" [label = "found, but marked pass"]; "check hash_key from hashTable" -> "check hash_key from hashTable" [label = "locked or unable to get mutex, wait insert_lock_retry_time"]; "lock URL in hashTable" -> TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK; TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK -> "remove URL from hashTable(1)" [label = "hit_fresh or skipped"]; TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK -> "request origin server" [label = "miss or stale"]; "request origin server" -> TS_HTTP_READ_RESPONSE_HDR_HOOK; TS_HTTP_READ_RESPONSE_HDR_HOOK -> "remove URL from hashTable(1)" [label = "not 200/OK response"]; TS_HTTP_READ_RESPONSE_HDR_HOOK -> "check read_while_writer config"; "check read_while_writer config" -> "remove URL from hashTable(1)" [label = "enabled"]; "check read_while_writer config" -> TS_HTTP_TXN_CLOSE_HOOK [label = "disabled"]; TS_HTTP_READ_RESPONSE_HDR_HOOK -> "mark pass in hashTable" [label = "non-cacheable"]; "remove URL from hashTable(1)" -> TS_HTTP_TXN_CLOSE_HOOK; "mark pass in hashTable" -> TS_HTTP_TXN_CLOSE_HOOK; "pass request" -> TS_HTTP_TXN_CLOSE_HOOK; TS_HTTP_TXN_CLOSE_HOOK -> "remove URL from hashTable(2)"; TS_HTTP_TXN_CLOSE_HOOK -> "check keep_pass_record_time" [label = "non-cacheable"]; "check keep_pass_record_time" -> "add into KeepPassList" [label = "> 0"]; "check keep_pass_record_time" -> "remove URL from hashTable(2)" [label = "= 0"]; "add into KeepPassList" -> "transaction close"; "remove URL from hashTable(2)" -> "transaction close"; "transaction close" -> accept; TS_HTTP_POST_REMAP_HOOK [shape = box]; TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK[shape = box]; TS_HTTP_READ_RESPONSE_HDR_HOOK [shape = box]; TS_HTTP_TXN_CLOSE_HOOK [shape = box]; "check request method (and header)" [shape = diamond]; "check hash_key from hashTable" [shape = diamond]; "check read_while_writer config" [shape = diamond]; "check keep_pass_record_time" [shape = diamond]; }
数据结构
二叉树
digraph gp0303 { node[shape=record, height=.1]; node0[label="<f0> |<f1> G|<f2> "]; node1[label="<f0> |<f1> E|<f2> "]; node2[label="<f0> |<f1> B|<f2> "]; node3[label="<f0> |<f1> F|<f2> "]; node4[label="<f0> |<f1> R|<f2> "]; node5[label="<f0> |<f1> H|<f2> "]; node6[label="<f0> |<f1> Y|<f2> "]; node7[label="<f0> |<f1> A|<f2> "]; node8[label="<f0> |<f1> C|<f2> "]; "node0":f2->"node4":f1; "node0":f0->"node1":f1; "node1":f0->"node2":f1; "node1":f2->"node3":f1; "node2":f2->"node8":f1; "node2":f0->"node7":f1; "node4":f2->"node6":f1; "node4":f0->"node5":f1; }
哈希表
digraph gp0304 { nodesep=.05; rankdir=LR; node[shape=record, width=.1, height=.1]; node0[label="<f0> |<f1> |<f2> |<f3> |<f4> |<f5> |<f6> |", height=2.5] node[width=1.5]; node1[label="{<n> n14 | 719 |<p>}"]; node2[label="{<n> a1 | 719 |<p>}"]; node3[label="{<n> i9 | 512 |<p>}"]; node4[label="{<n> e5 | 632 |<p>}"]; node5[label="{<n> t20 | 959 |<p>}"]; node6[label="{<n> o15 | 794 |<p>}"]; node7[label="{<n> s19 | 659 |<p>}"]; node0:f0 -> node1:n; node0:f1 -> node2:n; node0:f2 -> node3:n; node0:f5 -> node4:n; node0:f6 -> node5:n; node2:p -> node6:n; node4:p -> node7:n; }
模型
化学分子式
digraph gp0306 { C_0 -- H_0; C_0 -- H_1; C_0 -- H_2; C_0 -- C_1; C_1 -- H_3; C_1 -- H_4; C_1 -- H_5; }