2016年10月28日星期五

使用Graphviz绘制流程图和关系图

Graphviz概述

Graphviz是一个由AT&T实验室启动的使用DOT语言来绘制关系图/流程图的开源工具包。
DOT语言是一种文本图形描述语言,它提供了一种简单的描述图形的方法。
使用Graphviz,我们只需要将精力集中在逻辑设计上,而不需要花费大量时间在图形布局的调整上,图形绘制布局都由工具引擎来搞定。也因此,需要精确定位的图形就不适合用Graphviz来绘制了。另外,文本代码绘制图形的方式也便于版本管理。
另外,plantuml也包含了dot语法,并且还具有更多的功能,或许你可以尝试一下。

Graphviz工具

工具善其事,必先利其器。原谅我是一个工具痴 :-)
要想使用graphviz,首先需要上官网下载graphviz并安装。安装之后就可以使用graphviz自带的编辑器gvedit来编写dot代码来绘制图形了。快捷键F5来预览生成的图片,Shift+F5打开对话框并点击ok就可以生成指定类型的图片文件。
但是graphviz自带的gvedit编辑器那是非常非常的难用。怎么办呢?经过四处寻找和妥协,最终找到了windows平台下的还挺好用的工具,此工具不仅具有dot代码高亮、代码提示,还阔以预览生成的图片,只是暂时还不能导出图片。该工具就是github出品的代码编辑器Atom,另外需要安装两个插件:language-dotgraphviz-preview
使用Atom来编写和预览图片,最后使用gvedit来导出图片。看起来还不错的样子。
  • windows中安装好graphviz之后,最好配置一下PATH环境变量。
  • Atom的language-dot插件中的代码提示模板(snippet)可能不能满足你的需求,不过你可以自己添加和修改。这里有我自己修改的代码模板。

中文乱码解决

若渲染出的图片中出现乱码,检查两项:
  1. 文件编码需要使用utf-8。
  2. windows平台下需要在dot源码中指定字体名称。具体如下:
    // 影响图片级别的字体
    graph [fontname="宋体"];
    // 影响节点中的文字字体
    node [fontname="宋体"];
    // 影响箭头或线条上的文字字体
    edge [fontname="宋体"];

DOT基本语法

DOT语法相对简单和松散,没有特别的格式要求,也没有复杂的运算符和结构。你可以查看官方文档以了解更多,其实安装完graphviz后,其安装目录中就有文档了,位置如下:
GRAPHVIZ_HOME/share/graphviz/doc/html/index.html

概述

  • DOT中使用图(digraph/graph)、节点(node)和边(edge)来描述关系图/流程图。
  • DOT的注释风格和C类似(//用于单行注释,/* */用于多行注释)。

DOT可以描述有向图(使用digraph声明)和无向图(使用graph声明)两种图。图由{}中包含的节点和边构成。
在图的开头使用graph []对图进行设置,如:graph [bgcolor="gray"]将图背景色设置为灰色。属性设置语句也可以不包含在graph []中而直接使用。
子图(使用subgraph声明)可以进行和“父图”类似的设置,唯一注意的是子图必须以cluster做为名称的前缀。

节点

DOT中,节点可以不用声明直接使用,但如果需要设置节点的属性,则需声明节点并在声明处设置属性然后再使用。每个节点首次出现的名称做为该节点的唯一标识。
node []用于设置节点默认属性(对设置位置之后的点有效),在节点后面用[]设置单独一个点的属性。

DOT中有有向边(使用->表示)和无向边(使用--表示)两种,有向边用于有向图,无向边用于无向图,不可混用。
和节点类似的,用edge []设置边默认属性,在边之后用[]设置单独一条边的属性。对于有向边,还可以设置边的起点/终点的位置(用n、e、s、w或它们的组合表示位置)。

常用属性

对于各种结构的通用属性如下:
属性名称 默认值 含义
color black 颜色,颜色设置支持形如red#FF0000两种形式
fontcolor black 文字颜色
fontname Times-Roman 字体
fontsize 14 文字大小
label
显示的标签,支持’\n’换行,对于节点默认为节点名称
penwidth 1.0 线条宽度
style
样式
常用属性如下:
属性名称 默认值 含义
bgcolor
背景颜色
concentrate false 让多条边有公共部分
nodesep .25 节点之间的间隔(英寸)
peripheries 1 边界数
rank
same,min,source, max,sink,设置多个节点顺序
rankdir TB 排序方向
ranksep .75 间隔
size
图的大小(英寸)
labelloc
调整图或子图的标签的上下位置
labeljust
调整图或子图的标签的左右位置
compound false If true, allow edges between clusters. 配合lheadltail使用
常用节点属性如下:
属性名称 默认值 含义
shape ellipse 形状
sides 4 当shape=polygon时的边数
fillcolor lightgrey/black 填充颜色
fixedsize false 标签是否影响节点的大小
常用属性如下:
属性名称 默认值 含义
arrowhead normal 箭头头部形状
arrowsize 1.0 箭头大小
arrowtail normal 箭头尾部形状
constraint true 是否根据边来影响节点的排序
decorate
设置之后会用一条线来连接edge和label
dir forward 设置方向:forward,back,both,none
headclip true 是否到边界为止
tailclip true 与headclip类似
headlabel
边的头部显示的标签
taillabel
边的尾部显示的标签
lhead
compound为true时,lhead用于指定边指向的cluster
ltail
与ltail类似

简单示例

dot代码:
digraph simple_demo {
    // 设置图、节点和边的默认属性
    graph [label="simple demo", bgcolor="#EEEEEE"];
    node [style="filled", fillcolor="#AAAAAA"];
    edge [style="dashed", arrowsize=0.6];
    // 节点和边
    {a, b} -> {c, d};
}
图片效果:
simple_demo

高级用法

绘制二叉树

绘制二叉树使用到了记录形式的节点,代码如下:
digraph g {
    node [shape="record", height=.1];
    node0[label="<f0> |<f1> G|<f2>"];
    node1[label="<f0> |<f1> E|<f2>"];
    node2[label="<f0> |<f1> B|<f2>"];
    node0:f0 -> node1:f1;
    node0:f2 -> node2:f1;
}
其中,用|隔开的串会在绘制出来的节点中展现为一条分隔符,用<>括起来的串称为锚点。
效果如下:
binary_tree

竖排记录

记录形式的节点也可以是竖形排列的。与横向排列的记录的不同只是label的形式不同,label中内容使用{}包围则是竖形排列的。代码如下:
digraph g {
    node [shape="record"];
    a [label="{a | b | c}"];
}
效果如下:
col_record

自定义复杂节点

label还支持HTML格式的,这样你能想得到的大部分样子的节点都可以被自定义出来。代码如下:
digraph html {
    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>>];
}
效果如下:
custom_node

直接指向cluster

边直接指向cluster,需要设置compound为true,并配合lheadltail来实现。代码如下:
digraph G {
    compound=true;
    subgraph cluster0 {
        a;
    }
    subgraph cluster1 {
        b;
    }
    a -> b [lhead=cluster1];
}
效果如下:
point_to_cluster

简单的时序图

简单的时序图代码如下:
digraph g {
    rankdir="LR";
    {
        rank="same";
        a0 -> a1 -> a2;
    }
    {
        rank="same";
        b0 -> b1 -> b2;
    }
    a1 -> b1;
}
其中,rankdir="LR"表示,布局从左L到右R,每一个rank="same"的block中的所有节点都会在同一条线(横线或竖线)上。
效果如下:
sequence

以图片为节点

节点还可以使用图片,通过在节点中使用image="image_path"来设置图片。不过需要注意的是,在使用图片作为节点的时候,需要将本来的形状设置为none,并且将label置为空字符串,避免出现文字对图片的干扰。

Graphviz编程库

没有评论:

发表评论