用DOT画流程图

用DOT画流程图

graphviz会进行各类关系图的绘制。图使用DOT描述,具体可参考:http://graphviz.org/pdf/dotguide.pdf。下载graphviz后,使用bin文件夹下的dot程序,可以将编写的*.gv转为图片或ps,svg等格式。

开源的LivizJS能够进行精简版的DOT实现工具,它可以直接运行在浏览器中。不过年久失修的LivizJS不支持HTML5标准,只能在IE中运行。我将其修改后,现在已可以在Chrome, Edge中运行。链接: http://lab.malic.xyz/liviz/

例如新建文本graph1.gv,并写入如下三行。

digraph G {
a->b;
}

接下来将文本graph1.gv转为graph1.png。将dot设置在PATH中并运行如下命令,其中-T参数表示输出引擎,一般与输出格式扩展名一致,-K参数表示布局模式(默认为dot,所以不加-Kdot也可以,其它布局模式查看 http://graphviz.org/about/ )。

dot -Kdot -Tpng graph1.gv -o graph1.png

这样就生成了graph1.png如下图示。如graph1.gv所示描述的,就是a指向b的一个图。

多加几个结点:

digraph G {
main -> parse -> execute;
main -> init;
main -> cleanup;
execute -> make_string;
execute -> printf
init -> make_string;
main -> printf;
execute -> compare;
}

就得到了这样的图

再复杂一点,结点可以变换一下格式,即添加参数。

在编写dot的时候,可以先声明结点名,在结点名后加一方括号,使用”参数=属性”的方法为结点添加说明。默认情况下,结点shape="ellipse",width=.75, height=.5 ,label=结点名

结点之间使用'->'连接表示一条有向边,在这条边之后加一对方括号则为这条边添加属性。

另外,node添加的参数可以作为所有结点的公共声明,以及edge的参数会作为边的公共声明。公共声明的优先级低于结点上的声明。在dot直接使用中文会出现小黑框或者不显示,这时就需要在node和edge上声明中文字体,以正确地显示文字。

digraph G {
node[shape="box",fontname="Microsoft YaHei"];
a[shape="ellipse"];
b;
c[shape="circle"];
a->b [style="dashed,bold",color="red"];
b->a [style="dotted"];
a->c ;
 }

对于结点有一种特殊的shape,即record(和Mrecord,区别在于Mrecord是圆角的),它可以表示一组数据。当shape=record(或Mrecord)时,label需要为以下格式: 由竖线分开的标签,其中标签可以直接是文本,也可以是带'<>'标记的文本。如果带有'<>'标记,若绘制边时明确指定标记指向,可以使用结点名:标记表示。

digraph G {
node[shape="record"];
root;
L1[shape="Mrecord",label="Null|<tree>T|<R>R"];
R1[shape="Mrecord",label="<L>L|<tree>T|<R>R"];
R12;
L21;
R21;
root:L -> L1:tree;
root:R - >R1:tree;
L1:R -> R12:tree;
R1:L -> L21:tree;
R1:R -> R21:tree;
 }

上图绘制的是一个树的数据结构示意图,它是从上向下布局的。如果要变换一下方式,比如从左开始布局,或者从底向上布局,则可以通过声明rankdir,属性值有TB,BT,LR,RL,分别为自上向下(默认)、从底向上、从左到右、从右到左。

例如要绘制一个时间轴,从左向右画。时间轴结点的类型是纯文本,并且需要将同一年的结点e放在同一层级,这时可以使用{}表示一个范围,并指定rank=same表示这些结点是处在同一层次的(比如rankdir=LR时同一rank就处在同一水平位置,rankdir=TB时则处在同一高度)

digraph G {
rankdir=LR;
{
node[shape="plaintext"];
past->1998->2008->future[style="bold",color="red"];
e1;e2;e3;e4;e11;e21;e32;e42;e51;e52;e53;
}
{
rank=same;past;e1,e2,e3;
}
{
rank=same;1998;e11;e21;
}
{
rank=same;2008;e32;e42;
}
{
rank=same;future;e51;e52;e53;
}
e1->e11->e51;
e2->e21->{e32;e42}->e52;
e3->e53;
 }

使用subgraph将声明一个子图,可以作为一个局部使用,subgraph的图名必须以cluster为前缀,使用方法相似,不再赘述。

digraph G {
rankdir=LR;
node[shape="box" fontname="Microsoft YaHei"];
edge[fontname="Microsoft YaHei"];
root[shape="record" label="<f0> 内存|<f1> 设备|<f2> 文件|<f3> 进程"];
memtable;
devtable;
filetable;
root:f0 -> memtable;
root:f1 -> devtable;
root:f2 -> filetable;
process[shape="record" label="<f0> 进程1| <f1> 进程2|<f3> ...|<f4> 进程n"];
subgraph cluster0{
fontname="Microsoft YaHei";
label="进程实体及所用资源列表";
style="filled";
color="lightgrey";
proc1;
procn;
}
root:f3->process:f0->proc1;
process:f4 -> procn;
 }

以上就是DOT画图的基本方法。更多具体的参数及用法可以参考dotguide文档

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注