uolcano / blog

ScriptLife's Blog
MIT License
148 stars 23 forks source link

DOT语言学习笔记 #13

Open uolcano opened 7 years ago

uolcano commented 7 years ago

DOT语言学习笔记

介绍

之前上网瞎逛的时候,发现的一个挺好的自动布局绘图工具Graphviz。倒腾了一番,写下一些笔记,方便以后复习。

Graphviz全称Graph Visualization Software,最早出自AT&T实验室,是基于DOT语言脚本的自动绘图软件。是开源且免费的软件。

优点

相对于ms viso需要手动拖动图标,graphviz只需要通过DOT代码表达各节点的逻辑关系,然后自动布局和输出关系图。最小化复杂关系的连线交叉,增强可读性。

尤其在绘制非常复杂的逻辑关系、组织架构、模块组成、数据结构等图时,可以大大减少工作量,理清思路。

缺点

无法绘制需要自定义或者固定布局的图,比如时序图。

安装

Graphviz Download

官网提供安装包和源码包两种安装方式。安装完成后,最好是将graphviz安装目录下的bin目录设置到环境变量中,以便于随处使用dot命令行。

# windows 设置环境变量
SETX /M PATH "%PATH%;E:\your\graphviz\path\bin"
# 安装和设置完成后,可打开dot命令行帮助提示,验证成功安装
dot -h

应用

扩展名

Graphviz支持两种文件扩展名:*.gv*.dot,使用*.gv是为了防止与早期的ms word的扩展名冲突。 后文中的DOT文件即表示输入graphviz处理的文件。

输出格式

Graphviz支持的输出文件格式:

更多格式

编辑

DOT文件可以用任何文本编辑器编辑。但是graphviz自带一个及时输出图像的软件gvedit,软件打开DOT文件,然后按F5及时得到输出图像。这种“所见即所得”的编辑体验非常棒。 在命令行中输入gvedit可打开这个编辑器。

当然,有些Unix/Linux上可以使用的编辑器也支持graphviz及时编辑与输出,比如Emacs的graphviz dot mode和VI/VIM插件

布局器

Graphviz除了DOT语言用于描述图像,还有许多渲染生成工具 —— 布局器:

  1. dot,默认布局,主要用于有向图
  2. neato,用于“spring model”布局(Mac OS的版本称为“energy minimised”)
  3. circo,圆环布局
  4. twopi,径向布局
  5. fdp,用于无向图
  6. sfdp,用于需要放大到很大尺寸的无向图
  7. osage,紧凑集群布局

命令行

dot -Tv -Kv -O abc.gv

这里的-Tv中的v可用graphviz支持的输出格式代替,如:-Tpng-Tjpg等等。 而-Kv中的v可用布局器代替,如:-Kfdp-Kcirco等等。dot是默认布局器,因此可以省略-Kv这部分参数。 -O表示自动根据输入文件名来给输出文件命名。 abc.gv即使输入的DOT文件

可以通过dot -hdot -?来获得命令行帮助

更多命令行

第一个graphviz图

digraph abc {
    a -> b;
    b -> c;
    b -> d;
}

保存为abc.gv

dot -Tpng -O abc.gv

利用命令行以dot布局器(默认),输出同名的png格式图像abc.gv.png

first DOT

三个示例快速入门

节点示例

digraph node_intro {
    graph [label="节点示例 ", fontname="Microsoft Yahei"];
    node [fontname="Microsoft Yahei"];

    shape1 [shape=box, label="矩形 "];
    shape2 [shape=circle, label="圆形 "];
    shape3 [shape=ellipse, label="椭圆 "];
    shape4 [shape=polygon, sides=4, skew=0.4, label="平行四边形 "];
    shape5 [shape=diamond, label="菱形 "];
    shape6 [shape=record, label="{记录1|记录2|记录3}"];
    shape7 [shape=none, label="无边框 "];
    shape1:s -> shape2 -> shape3 -> shape4 -> shape5 -> shape6 -> shape7;

    color1 [color=blue, label="蓝色边框 "];
    color2 [style=filled, fillcolor=green, label="绿色填充 "];
    color3 [color="#0000ff", style=filled, fillcolor="green:red", label="蓝色边框\n+\n由绿到红渐变色填充 "];
    color1 -> color2 -> color3;

    text1 [shape=box, fontsize=12, label="小字体 "];
    text2 [shape=box, fontsize=24, label="大字体 "];
    text3 [shape=box, fontcolor=blue, label="蓝色字体 "];
    text4 [shape=box, label=<
        <table bgcolor="#aa99ff" align="center">
            <tr>
                <td colspan="3" width="20"><font point-size="24">类HTML标签 </font></td>
            </tr>
            <tr>
                <td color="red"><b>加粗 </b></td>
                <td color="green"><u>下划线 </u></td>
                <td color="blue"><i>斜体 </i></td>
            </tr>
        </table>
    >];
    text1 -> text2 -> text3 -> text4;
}

node introduction

连线示例

digraph edge_intro {
    graph [label="连线示例 ", fontname="Microsoft Yahei"];
    edge [fontname="Microsoft Yahei"];
    node [fontname="Microsoft Yahei"];

    style0 [label="以下是连线样式示例 "];
    style1, style2, style3, style4 [label=""];

    style0 -> style1 [style=solid, label="实线 "];
    style1 -> style2 [style=bold, label="粗线 "];
    style2 -> style3 [style=dashed, label="短划线 "];
    style3 -> style4 [style=dotted, label="虚线 "];

    arrow0 [label="以下是箭头形状示例 "];
    arrow1, arrow2, arrow3, arrow4, arrow5, arrow6, arrow7, arrow8 [label=""];

    arrow0 -> arrow1 [dir=both, label="双向 "];
    arrow1 -> arrow2 [arrowsize=2, label="大箭头 "];
    arrow2 -> arrow3 [arrowhead=box, label="方块 "];
    arrow3 -> arrow4 [arrowhead=diamond, label="菱形 "];
    arrow4 -> arrow5 [arrowhead=curve, label="弧形 "];
    arrow5 -> arrow6 [arrowhead=normal, label="默认的三角形 "];
    arrow6 -> arrow7 [arrowhead=dot, label="圆点 "];
    arrow7 -> arrow8 [arrowhead=oboxdotrveecurve, label="镂空方块 圆点 半边箭头\n圆弧 构成的复合图形 ", fontsize=12];

    color0 [label="以下是连线颜色示例 "];
    color1, color2, color3 [label=""];

    color0 -> color1 [color=blue, label="蓝色 "];
    color1 -> color2 [color="red:blue", label="双色 "];
    color2 -> color3 [color="red:green;0.4:blue", label="多颜色分段 "];
}

edge introduction

图示例

digraph graph_intro {
    graph [bgcolor=lemonchiffon, fontsize=24, fontcolor=limegreen, rankdir=LR, fontname="Microsoft Yahei", label="图的名称"];

    node0 -> {node1, node2};
    node2 -> node3;
}

graph introduction

更多示例

【注意】后文中的示例大多需要通过命令行DOT或者gvedit输出图像,不再外加说明。

DOT语法

基础

DOT只有图graph、节点node和连线edge三个主要结构。graphnodeedge三个关键字还能用于全局属性定义,后面细讲。

图分为有向图和无向图。无向图声明的时候使用关键字graph,有向图使用digraph

关键字

  1. node:定义全局节点属性时使用
  2. edge:定义全局连线属性时使用
  3. graph:定义全局图属性,或声明一个无向图时使用
  4. digraph:声明一个有向图时使用
  5. subgraph:声明一个子图时使用,如果父图是有向图则子图是有向图,如果父图是无向图则子图也是无向图
  6. strict:用于防止相同的两个节点间使用重复的连线。

ID有效字符

ID是编辑者自定义的字符串,相当于C语言中的标识符。

命名规则

  1. 英文字母[a-zA-Z\200-\377],下划线_,数字[0-9](但不能数字开头),如:Version_3
  2. 纯数字[-]?(.[0-9]+|[0-9]+(.[0-9]*)?),如:-.11.414
  3. 双引号包裹的字符串"...",字符串中的双引号需要转义\",如:"DOT language"
  4. 尖括号包裹的HTML字符串<...>,如:<<b>Welcome</b> <u>to</u> <i>China.</i>>;实际上甚至可以插入表格。HTML-Like Labels

使用

ID主要是作为节点的命名字符串

graph ID{
    label="A empty graph";
}

简单的DOT代码中的图名ID可以省略,这就有了匿名图

graph {
    a_node [label="this is a anonymous graph"]
}

作为子图的图的ID一定要以cluster开头命名,否则graphviz不识别。

因为父图的图ID和子图的图ID,共享相同命名空间,父图和子图、子图与子图间的图的ID命名一定要不同。

digraph abcd{
    subgraph cluster_ab{
        bgcolor=mintcream;
        a b;
    }
    subgraph cluster_cd{
        bgcolor=chartreuse;
        c d;
    }
    a -> b;
    b -> c -> d;
}

【注意】如果ID中间包含空格一定要用双引号包裹。

注释

DOT支持类似C++的注释,单行注释//,多行或部分注释/**/

字符串

分隔符

分号;和逗号,都不是必须的,可以用空格替代。

竖杠|在节点的属性shape=record时,作为划分分组内部的分隔符。后面有举例

字符串处理

长字符串换行,类似C语言,如:

graph {
greeting [label="hello \
everyone, \
welcom \
to China.
"];
}

DOT还支持拼接操作符+,如:

graph {
"hello "+"world, "+"welcome";
}

【注意】这里描述的字符串都是指以双引号"包裹的

转义与实体符号

字符串中的字符,只有双引号\"和反斜杠\\需要转义。

字符串支持还HTML式的实体符号,如:&amp;表示&&lt;表示<&beta;表示β

当节点的属性shape=record时,竖杠\|、花括号\{ \}、方括号\[ \]和尖括号\< \>都需要转义。

分组符号

前面介绍了两种包裹字符串的符号,双引号"..."<...>; 另外还有两种包裹分组的符号:方括号[...]和花括号{...}

方括号

用于属性定义,给图、节点和连线添加各种样式。如:

digraph {
    graph [label="graph with styles", bgcolor=mintcream, fontsize=24, fontcolor=green];
    a [color=blue, fontsize=12, style=filled, fillcolor=yellow];
}

花括号

用法1,图的声明,,包括有向、无向图以及子图的声明

graph {
    a -- b -- c;
}

用法2,某个节点连接到多个节点时的简写方式

digraph {
    a -> {b c d};
}

用法3,只有在节点的属性shape=record时,包裹节点内部的分组

digraph {
    node [shape=record];
    group1 [label="{a|b|c}"];
    group2 [label="{x|{&alpha;|&beta;|&gamma;|{L|M|N}}|y|z}"];
}

结构声明

一个图的声明,需关键字graphvdigraphv{...}两部分

digraph {
// 这里添加节点声明、连线声明以及图、节点和连线的属性设置
}

另外,在关键字graphdigraph前面可以添加一个空格隔开的关键字strict用来防止两节点间重复连线

strict graph {
    a -- b;
    a -- b [label="repetitive edge", style=bold];
    b -- c;
    a -- b [color=red, style=dashed];
}

节点

节点可以不声明直接在连线声明时使用。 一般提前声明节点的情况有两种:

  1. 给节点定义属性

    graph {
        a, b [color=red];
        a -- b;
    }
  2. 节点被分组到子图中

    graph {
        subgraph cluster {
            a, b;
        }
        a -- b;
    }

连线

连线的声明需要连接两个节点。

对于有向图,连线使用->;无向图,连线使用--

digraph {
    a -> b;
}

graph {
    c -- d;
}

两种连线都可以简写

digraph {
    a -> b -> c -> d;
}

等价于

digraph {
    a -> b;
    b -> c;
    c -> d;
}

属性

属性定义可以给节点、连线和图渲染上不同的样式,使得最终得到的关系图更加美观,提升可读性。 属性均是以名值对attr=val的方式出现。属性值val,可用双引号或尖括号包裹,但是可选的;不过,如果属性值中间包含DOT无法直接解析的字符,就必须用双引号或尖括号包裹,甚至转义。

全局属性

    1. 直接定义

      graph {
          label="A graph directly \ndefined attributes";
          bgcolor=skyblue;
          fontname="Microsoft Yahei";
          fontsize=24;
          rankdir=LR;
      
          a -- b -- c -- a;
      }
    2. 使用关键字graph

      graph {
          graph [label="an undirected graph"];
      
          a -- b -- c -- a;
      }
  1. 节点 使用关键字node

    digraph {
        graph [label=<<b>styled</b> graph> fontname=Vardana fontsize=20];
        node [shape=doublecircle, style=filled, fillcolor=orange];
    
        a -> {b d};
        b -> c;
    }
  2. 连线 使用关键字edge

    digraph {
        edge [style=bold, color=blue:red];
        a -> {b d};
        b -> c;
    }

局部属性

局部设置属性,除了可以对一个一个的节点或者连线设置,也可以同时对多个节点或者多个连线进行设置

  1. 节点

    graph {
        a, b [shape=Mdiamond];
        c [shape=doublecircle];
        a -- c -- b;
    }
  2. 连线

    digraph {
        a -> {b d} [arrowhead=olnormalol];
        b -> c [style=dashed, color=red, arrowhead=obox];
    }

常用属性

label

可用于节点、连线和图的名称设置,如:

graph {
    graph [label=G];
    edge [label=E];
    node [label=N];

    a -- b -- {c, d};
    e[color=red];
}

属性值的形式:"val"<val>val

shape

用于节点的形状修改,如:

graph {
    a [shape=doublecircle];
    b [shape=polygon, skew=0.6];
    c [shape=diamond];
    a -- {b, c};
}

常用属性值:box矩形、circle圆形、polygon多边形、ellipse默认的椭圆、record记录式、diamond菱形等等。 其中shape=polygon结合倾斜度属性skew=0.4可以将节点设置成平行四边形;shape=record可以将节点设置成记录表的样式。

更多形状

style

用于节点样式连线样式设置,如:

graph {
    node [style=filled];
    a [fillcolor="green:red"];
    b, c [fillcolor=skyblue];

    a -- b [style=dashed];
    a -- c [style=dotted];
}

常用属性值:

  1. 节点:filled填充节点背景色,需要结合填充色属性fillcolor
  2. 连线:solid默认的实线、bold粗线、dashed短划线、dotted虚线。

color

设置节点边框线颜色连线颜色,如:

graph {
    a, b [color=blue];
    c [color=red];

    a -- b [color=green];
    a -- c [color=yellow];
}

颜色值有3种形式:

  1. 颜色名,如:red green blue yellow orange navy white black等等
  2. 6位16进制颜色值,如:"#ff0000"等同于red,注意因为DOT不直接识别#,一定要用双引号或尖括号包裹
  3. 渐变色只适用于连线,有两种形式:"red:blue""red:green;0.4:blue",同样因为不直接识别:,需要包裹成字符串

bgcolor

用于填充图的背景色,如:

graph {
    bgcolor=yellow;
    subgraph cluster {
        bgcolor="green:red";
        a b;
    }
    a -- b -- c;
}

属性值类似color,但是渐变色只支持2种色构成的渐变。

fillcolor

用于填充节点的背景色,用法同bgcolor

fontname

可设置节点、连线和图的ID的字体,相当于CSS中的font-family

【注意】使用中文时,fontname一定要设置系统支持的中文字体,否则graphviz输出图片的中文会乱码。而且,图的名称、连线的名称以及节点的名称只要使用了中文就都应该设置fontname属性,比如:fontname="Microsoft Yahei"。另外,最好是在中文字符后留有一个空格,防止乱码后,英文字符也被修改。

fontsize

设置节点、连线和图的字体大小,属性值可以是纯数字或者是数字字符串

fontcolor

设置字体的颜色,用法类似color,但是不支持渐变色

rankdir

设置图的绘制方向,如:

digraph {
    rankdir=LR;
    subgraph cluster {
        b, c;
    }
    a -> b -> c;
    a -> d;
}

属性值: 默认由上至下TB、由下至上BT、由左至右LR和由右至左RL

dir

设置连线的箭头方向,如:

digraph {
    rankdir=LR;
    client [shape=tab];
    server [shape=box3d];

    client:e -> server:w [dir=both, style=bold, color="blue:green", arrowhead=r, arrowtail=r];
}

属性值:向前forward(有向图默认)、向后back、双向both以及无none(无向图默认)

arrowhead

设置无向图或有向图的连线头部箭头形状,如:

digraph {
    a -> b [arrowhead=curve]
}

属性值:箭头形状有很多变种,而且可以同时设置多个形状,基本组合是[是否镂空][左右边][基础形状]。字母o决定箭头形状是否镂空open,lr分别表示只出现左边left和右边right,基础形状有boxcrowcurveicurvediamonddotinvnonenormaltee以及vee

graph {
    a -- b [dir=both, arrowtail=curve, arrowhead=teeoldiamond];
}

解读teeoldiamond:第一个形状tee紧跟着第二个形状是镂空的o取左边的l钻石形状diamond

更多箭头形状

arrowtail

设置无向图或有向图的连线尾部箭头形状,与arrowhead用法相同

arrowsize

设置连线箭头大小

digraph {
    a -> b [arrowsize=2];
}

更多属性

端口

节点上的端口,类似于指针。在节点上确定端口以后,再连接两个节点时可以找到节点上准确的位置。

罗盘端口

罗盘也就是确定八个方位,罗盘端口就是方位端口

方位端口是每个节点隐藏自带的,可以直接使用。

每个节点都有八个方位:北n、东北ne、东e、东南se、南s、西南sw、西w和西北nw

利用方位可以指定连线从哪个位置连接节点,如:

digraph {
    node [shape=box];
    subgraph cluster {
        c, d;
    }
    a:e -> b:n;
    a:nw -> c:n;
    c:w -> d:sw;
    b:sw -> d;
}

不同于默认情况下的自动连线,利用方位可以手动指定连线的位置。

命名端口

命名端口需要在节点中手动设置端口,并且位置命名。

一般在节点的属性shape=record时,对于节点内分组中的单元进行端口设置。

digraph {
    rankdir=LR;
    fontsize=12;
    node [shape=record];

    struct1 [label="{<head>* int|float|<next>* int}"];
    struct2 [label="{<prev>int|* char|<next>* int}"];
    struct3 [label="{<prev>int|* char|* int}"];

    struct1:next -> struct2:prev;
    struct2:next -> struct3:prev;
    struct1:head:s -> int;
}

struct1:next中的nextstruct2:prev中的prev都是命名端口,通过这两个端口可以将节点struct1* int单元与节点struct2int单元连接起来。而struct1:head:s则即使用了命名端口head,又使用了罗盘端口s

以上可以看出,命名端口的设置出现在节点声明时,使用尖括号<...>包裹,命名端口和罗盘端口都是在节点ID后面加冒号:来引用。

属性叠层

继承

主要是指子图会从父图继承图、连线和节点的所有属性定义

digraph abc{
    graph [bgcolor=pink];
    edge [color=blue];
    node [shape=hexagon];

    a -> b;

    subgraph cluster {
        c -> d;
    }

    b -> c;
}

覆盖

DOT中的属性可以多次定义,后面定义的会覆盖前面定义的。 但是要注意如果一个声明在两个属性定义之间,声明的节点或者连线只会获得前一个属性定义。

digraph {
    node [shape=doublecircle, fontcolor=blue];
    edge [style=bold, color=blue];

    a b e;

    a -> b;

    node [shape=box3d];
    edge [style=dotted, color=red];

    a -> c ->d;
    c -> e;
}

节点abe获得了属性shape=doublecircle,而节点cd的属性被覆盖为了shape=box3d,但是五个节点有相同的属性fontcolor=blue。可见shape属性被覆盖了。同理,连线a -> c -> d的属性style=bold也被style=dotted覆盖,而a -> b却没有变。

当然图的属性定义也能覆盖,不过是父图与子图之间

digraph abc{
    graph [bgcolor=pink];
    edge [color=blue];
    node [shape=box];

    subgraph cluster1 {
        graph [bgcolor=chartreuse];
        edge [color=red];
        node [shape=hexagon];
        a -> b;
    }

    subgraph cluster2 {
        c -> d;
    }

    b -> c;
}

对比cluster1cluster2两个子图,前者把父图abc定义的图、连线和节点的属性全部覆盖了;而后者是继承自父图。

另外,匿名子图还能够在视觉上不出现子图效果的情况下,覆盖父图定义的属性

digraph {
    graph [bgcolor=pink];
    edge [color=blue];
    node [shape=triangle];

    a;

    subgraph {
        node [shape=tripleoctagon];
        b;
    }

    c;

    node [shape=doublecircle];

    d;

    a -> b -> {c d};
}

很明显,这里的节点bshape属性被修改为了tripleoctagon,而节点c却没有被修改。

总结

现学现卖,用Graphviz做个思维导图

DOT mindmap

参考资料

feng00011 commented 2 years ago

DOT语言学习笔记

介绍

之前上网瞎逛的时候,发现的一个挺好的自动布局绘图工具Graphviz。倒腾了一番,写下一些笔记,方便以后复习。

Graphviz全称Graph Visualization Software,最早出自AT&T实验室,是基于DOT语言脚本的自动绘图软件。是开源且免费的软件。

优点

相对于ms viso需要手动拖动图标,graphviz只需要通过DOT代码表达各节点的逻辑关系,然后自动布局和输出关系图。最小化复杂关系的连线交叉,增强可读性。

尤其在绘制非常复杂的逻辑关系、组织架构、模块组成、数据结构等图时,可以大大减少工作量,理清思路。

缺点

无法绘制需要自定义或者固定布局的图,比如时序图。

安装

Graphviz Download

官网提供安装包和源码包两种安装方式。安装完成后,最好是将graphviz安装目录下的bin目录设置到环境变量中,以便于随处使用dot命令行。

# windows 设置环境变量
SETX /M PATH "%PATH%;E:\your\graphviz\path\bin"
# 安装和设置完成后,可打开dot命令行帮助提示,验证成功安装
dot -h

应用

扩展名

Graphviz支持两种文件扩展名:*.gv*.dot,使用*.gv是为了防止与早期的ms word的扩展名冲突。 后文中的DOT文件即表示输入graphviz处理的文件。

输出格式

Graphviz支持的输出文件格式:

  • 图片格式:bmp、png、gif、ico、jpg、jpeg、svg
  • 文档格式:pdf、ps

更多格式

编辑

DOT文件可以用任何文本编辑器编辑。但是graphviz自带一个及时输出图像的软件gvedit,软件打开DOT文件,然后按F5及时得到输出图像。这种“所见即所得”的编辑体验非常棒。 在命令行中输入gvedit可打开这个编辑器。

当然,有些Unix/Linux上可以使用的编辑器也支持graphviz及时编辑与输出,比如Emacs的graphviz dot mode和VI/VIM插件

布局器

Graphviz除了DOT语言用于描述图像,还有许多渲染生成工具 —— 布局器:

  1. dot,默认布局,主要用于有向图
  2. neato,用于“spring model”布局(Mac OS的版本称为“energy minimised”)
  3. circo,圆环布局
  4. twopi,径向布局
  5. fdp,用于无向图
  6. sfdp,用于需要放大到很大尺寸的无向图
  7. osage,紧凑集群布局

命令行

dot -Tv -Kv -O abc.gv

这里的-Tv中的v可用graphviz支持的输出格式代替,如:-Tpng-Tjpg等等。 而-Kv中的v可用布局器代替,如:-Kfdp-Kcirco等等。dot是默认布局器,因此可以省略-Kv这部分参数。 -O表示自动根据输入文件名来给输出文件命名。 abc.gv即使输入的DOT文件

可以通过dot -hdot -?来获得命令行帮助

更多命令行

第一个graphviz图

digraph abc {
  a -> b;
  b -> c;
  b -> d;
}

保存为abc.gv

dot -Tpng -O abc.gv

利用命令行以dot布局器(默认),输出同名的png格式图像abc.gv.png

first DOT

三个示例快速入门

节点示例

digraph node_intro {
  graph [label="节点示例 ", fontname="Microsoft Yahei"];
  node [fontname="Microsoft Yahei"];

  shape1 [shape=box, label="矩形 "];
  shape2 [shape=circle, label="圆形 "];
  shape3 [shape=ellipse, label="椭圆 "];
  shape4 [shape=polygon, sides=4, skew=0.4, label="平行四边形 "];
  shape5 [shape=diamond, label="菱形 "];
  shape6 [shape=record, label="{记录1|记录2|记录3}"];
  shape7 [shape=none, label="无边框 "];
  shape1:s -> shape2 -> shape3 -> shape4 -> shape5 -> shape6 -> shape7;

  color1 [color=blue, label="蓝色边框 "];
  color2 [style=filled, fillcolor=green, label="绿色填充 "];
  color3 [color="#0000ff", style=filled, fillcolor="green:red", label="蓝色边框\n+\n由绿到红渐变色填充 "];
  color1 -> color2 -> color3;

  text1 [shape=box, fontsize=12, label="小字体 "];
  text2 [shape=box, fontsize=24, label="大字体 "];
  text3 [shape=box, fontcolor=blue, label="蓝色字体 "];
  text4 [shape=box, label=<
      <table bgcolor="#aa99ff" align="center">
          <tr>
              <td colspan="3" width="20"><font point-size="24">类HTML标签 </font></td>
          </tr>
          <tr>
              <td color="red"><b>加粗 </b></td>
              <td color="green"><u>下划线 </u></td>
              <td color="blue"><i>斜体 </i></td>
          </tr>
      </table>
  >];
  text1 -> text2 -> text3 -> text4;
}

node introduction

连线示例

digraph edge_intro {
  graph [label="连线示例 ", fontname="Microsoft Yahei"];
  edge [fontname="Microsoft Yahei"];
  node [fontname="Microsoft Yahei"];

  style0 [label="以下是连线样式示例 "];
  style1, style2, style3, style4 [label=""];

  style0 -> style1 [style=solid, label="实线 "];
  style1 -> style2 [style=bold, label="粗线 "];
  style2 -> style3 [style=dashed, label="短划线 "];
  style3 -> style4 [style=dotted, label="虚线 "];

  arrow0 [label="以下是箭头形状示例 "];
  arrow1, arrow2, arrow3, arrow4, arrow5, arrow6, arrow7, arrow8 [label=""];

  arrow0 -> arrow1 [dir=both, label="双向 "];
  arrow1 -> arrow2 [arrowsize=2, label="大箭头 "];
  arrow2 -> arrow3 [arrowhead=box, label="方块 "];
  arrow3 -> arrow4 [arrowhead=diamond, label="菱形 "];
  arrow4 -> arrow5 [arrowhead=curve, label="弧形 "];
  arrow5 -> arrow6 [arrowhead=normal, label="默认的三角形 "];
  arrow6 -> arrow7 [arrowhead=dot, label="圆点 "];
  arrow7 -> arrow8 [arrowhead=oboxdotrveecurve, label="镂空方块 圆点 半边箭头\n圆弧 构成的复合图形 ", fontsize=12];

  color0 [label="以下是连线颜色示例 "];
  color1, color2, color3 [label=""];

  color0 -> color1 [color=blue, label="蓝色 "];
  color1 -> color2 [color="red:blue", label="双色 "];
  color2 -> color3 [color="red:green;0.4:blue", label="多颜色分段 "];
}

edge introduction

图示例

digraph graph_intro {
  graph [bgcolor=lemonchiffon, fontsize=24, fontcolor=limegreen, rankdir=LR, fontname="Microsoft Yahei", label="图的名称"];

  node0 -> {node1, node2};
  node2 -> node3;
}

graph introduction

更多示例

【注意】后文中的示例大多需要通过命令行DOT或者gvedit输出图像,不再外加说明。

DOT语法

基础

DOT只有图graph、节点node和连线edge三个主要结构。graphnodeedge三个关键字还能用于全局属性定义,后面细讲。

图分为有向图和无向图。无向图声明的时候使用关键字graph,有向图使用digraph

关键字

  1. node:定义全局节点属性时使用
  2. edge:定义全局连线属性时使用
  3. graph:定义全局图属性,或声明一个无向图时使用
  4. digraph:声明一个有向图时使用
  5. subgraph:声明一个子图时使用,如果父图是有向图则子图是有向图,如果父图是无向图则子图也是无向图
  6. strict:用于防止相同的两个节点间使用重复的连线。

ID有效字符

ID是编辑者自定义的字符串,相当于C语言中的标识符。

命名规则

  1. 英文字母[a-zA-Z\200-\377],下划线_,数字[0-9](但不能数字开头),如:Version_3
  2. 纯数字[-]?(.[0-9]+|[0-9]+(.[0-9]*)?),如:-.11.414
  3. 双引号包裹的字符串"...",字符串中的双引号需要转义\",如:"DOT language"
  4. 尖括号包裹的HTML字符串<...>,如:<<b>Welcome</b> <u>to</u> <i>China.</i>>;实际上甚至可以插入表格。HTML-Like Labels

使用

ID主要是作为节点的命名字符串

graph ID{
  label="A empty graph";
}

简单的DOT代码中的图名ID可以省略,这就有了匿名图

graph {
  a_node [label="this is a anonymous graph"]
}

作为子图的图的ID一定要以cluster开头命名,否则graphviz不识别。

因为父图的图ID和子图的图ID,共享相同命名空间,父图和子图、子图与子图间的图的ID命名一定要不同。

digraph abcd{
  subgraph cluster_ab{
      bgcolor=mintcream;
      a b;
  }
  subgraph cluster_cd{
      bgcolor=chartreuse;
      c d;
  }
  a -> b;
  b -> c -> d;
}

【注意】如果ID中间包含空格一定要用双引号包裹。

注释

DOT支持类似C++的注释,单行注释//,多行或部分注释/**/

字符串

分隔符

分号;和逗号,都不是必须的,可以用空格替代。

竖杠|在节点的属性shape=record时,作为划分分组内部的分隔符。后面有举例

字符串处理

长字符串换行,类似C语言,如:

graph {
greeting [label="hello \
everyone, \
welcom \
to China.
"];
}

DOT还支持拼接操作符+,如:

graph {
"hello "+"world, "+"welcome";
}

【注意】这里描述的字符串都是指以双引号"包裹的

转义与实体符号

字符串中的字符,只有双引号\"和反斜杠\\需要转义。

字符串支持还HTML式的实体符号,如:&amp;表示&&lt;表示<&beta;表示β

当节点的属性shape=record时,竖杠\|、花括号\{ \}、方括号\[ \]和尖括号\< \>都需要转义。

分组符号

前面介绍了两种包裹字符串的符号,双引号"..."<...>; 另外还有两种包裹分组的符号:方括号[...]和花括号{...}

方括号

用于属性定义,给图、节点和连线添加各种样式。如:

digraph {
  graph [label="graph with styles", bgcolor=mintcream, fontsize=24, fontcolor=green];
  a [color=blue, fontsize=12, style=filled, fillcolor=yellow];
}

花括号

用法1,图的声明,,包括有向、无向图以及子图的声明

graph {
  a -- b -- c;
}

用法2,某个节点连接到多个节点时的简写方式

digraph {
  a -> {b c d};
}

用法3,只有在节点的属性shape=record时,包裹节点内部的分组

digraph {
  node [shape=record];
  group1 [label="{a|b|c}"];
  group2 [label="{x|{&alpha;|&beta;|&gamma;|{L|M|N}}|y|z}"];
}

结构声明

一个图的声明,需关键字graphvdigraphv{...}两部分

digraph {
// 这里添加节点声明、连线声明以及图、节点和连线的属性设置
}

另外,在关键字graphdigraph前面可以添加一个空格隔开的关键字strict用来防止两节点间重复连线

strict graph {
  a -- b;
  a -- b [label="repetitive edge", style=bold];
  b -- c;
  a -- b [color=red, style=dashed];
}

节点

节点可以不声明直接在连线声明时使用。 一般提前声明节点的情况有两种:

  1. 给节点定义属性
    graph {
      a, b [color=red];
      a -- b;
    }
  2. 节点被分组到子图中
    graph {
      subgraph cluster {
          a, b;
      }
      a -- b;
    }

连线

连线的声明需要连接两个节点。

对于有向图,连线使用->;无向图,连线使用--

digraph {
  a -> b;
}

graph {
  c -- d;
}

两种连线都可以简写

digraph {
  a -> b -> c -> d;
}

等价于

digraph {
  a -> b;
  b -> c;
  c -> d;
}

属性

属性定义可以给节点、连线和图渲染上不同的样式,使得最终得到的关系图更加美观,提升可读性。 属性均是以名值对attr=val的方式出现。属性值val,可用双引号或尖括号包裹,但是可选的;不过,如果属性值中间包含DOT无法直接解析的字符,就必须用双引号或尖括号包裹,甚至转义。

全局属性

    1. 直接定义

      graph {
        label="A graph directly \ndefined attributes";
        bgcolor=skyblue;
        fontname="Microsoft Yahei";
        fontsize=24;
        rankdir=LR;
      
        a -- b -- c -- a;
      }
    2. 使用关键字graph

      graph {
        graph [label="an undirected graph"];
      
        a -- b -- c -- a;
      }
  1. 节点 使用关键字node

    digraph {
      graph [label=<<b>styled</b> graph> fontname=Vardana fontsize=20];
      node [shape=doublecircle, style=filled, fillcolor=orange];
    
      a -> {b d};
      b -> c;
    }
  2. 连线 使用关键字edge
    digraph {
      edge [style=bold, color=blue:red];
      a -> {b d};
      b -> c;
    }

局部属性

局部设置属性,除了可以对一个一个的节点或者连线设置,也可以同时对多个节点或者多个连线进行设置

  1. 节点
    graph {
      a, b [shape=Mdiamond];
      c [shape=doublecircle];
      a -- c -- b;
    }
  2. 连线
    digraph {
      a -> {b d} [arrowhead=olnormalol];
      b -> c [style=dashed, color=red, arrowhead=obox];
    }

常用属性

label

可用于节点、连线和图的名称设置,如:

graph {
  graph [label=G];
  edge [label=E];
  node [label=N];

  a -- b -- {c, d};
  e[color=red];
}

属性值的形式:"val"<val>val

shape

用于节点的形状修改,如:

graph {
  a [shape=doublecircle];
  b [shape=polygon, skew=0.6];
  c [shape=diamond];
  a -- {b, c};
}

常用属性值:box矩形、circle圆形、polygon多边形、ellipse默认的椭圆、record记录式、diamond菱形等等。 其中shape=polygon结合倾斜度属性skew=0.4可以将节点设置成平行四边形;shape=record可以将节点设置成记录表的样式。

更多形状

style

用于节点样式连线样式设置,如:

graph {
  node [style=filled];
  a [fillcolor="green:red"];
  b, c [fillcolor=skyblue];

  a -- b [style=dashed];
  a -- c [style=dotted];
}

常用属性值:

  1. 节点:filled填充节点背景色,需要结合填充色属性fillcolor
  2. 连线:solid默认的实线、bold粗线、dashed短划线、dotted虚线。
color

设置节点边框线颜色连线颜色,如:

graph {
  a, b [color=blue];
  c [color=red];

  a -- b [color=green];
  a -- c [color=yellow];
}

颜色值有3种形式:

  1. 颜色名,如:red green blue yellow orange navy white black等等
  2. 6位16进制颜色值,如:"#ff0000"等同于red,注意因为DOT不直接识别#,一定要用双引号或尖括号包裹
  3. 渐变色只适用于连线,有两种形式:"red:blue""red:green;0.4:blue",同样因为不直接识别:,需要包裹成字符串
bgcolor

用于填充图的背景色,如:

graph {
  bgcolor=yellow;
  subgraph cluster {
      bgcolor="green:red";
      a b;
  }
  a -- b -- c;
}

属性值类似color,但是渐变色只支持2种色构成的渐变。

fillcolor

用于填充节点的背景色,用法同bgcolor

fontname

可设置节点、连线和图的ID的字体,相当于CSS中的font-family

【注意】使用中文时,fontname一定要设置系统支持的中文字体,否则graphviz输出图片的中文会乱码。而且,图的名称、连线的名称以及节点的名称只要使用了中文就都应该设置fontname属性,比如:fontname="Microsoft Yahei"。另外,最好是在中文字符后留有一个空格,防止乱码后,英文字符也被修改。

fontsize

设置节点、连线和图的字体大小,属性值可以是纯数字或者是数字字符串

fontcolor

设置字体的颜色,用法类似color,但是不支持渐变色

rankdir

设置图的绘制方向,如:

digraph {
  rankdir=LR;
  subgraph cluster {
      b, c;
  }
  a -> b -> c;
  a -> d;
}

属性值: 默认由上至下TB、由下至上BT、由左至右LR和由右至左RL

dir

设置连线的箭头方向,如:

digraph {
  rankdir=LR;
  client [shape=tab];
  server [shape=box3d];

  client:e -> server:w [dir=both, style=bold, color="blue:green", arrowhead=r, arrowtail=r];
}

属性值:向前forward(有向图默认)、向后back、双向both以及无none(无向图默认)

arrowhead

设置无向图或有向图的连线头部箭头形状,如:

digraph {
  a -> b [arrowhead=curve]
}

属性值:箭头形状有很多变种,而且可以同时设置多个形状,基本组合是[是否镂空][左右边][基础形状]。字母o决定箭头形状是否镂空open,lr分别表示只出现左边left和右边right,基础形状有boxcrowcurveicurvediamonddotinvnonenormaltee以及vee

graph {
  a -- b [dir=both, arrowtail=curve, arrowhead=teeoldiamond];
}

解读teeoldiamond:第一个形状tee紧跟着第二个形状是镂空的o取左边的l钻石形状diamond

更多箭头形状

arrowtail

设置无向图或有向图的连线尾部箭头形状,与arrowhead用法相同

arrowsize

设置连线箭头大小

digraph {
  a -> b [arrowsize=2];
}

更多属性

端口

节点上的端口,类似于指针。在节点上确定端口以后,再连接两个节点时可以找到节点上准确的位置。

罗盘端口

罗盘也就是确定八个方位,罗盘端口就是方位端口

方位端口是每个节点隐藏自带的,可以直接使用。

每个节点都有八个方位:北n、东北ne、东e、东南se、南s、西南sw、西w和西北nw

利用方位可以指定连线从哪个位置连接节点,如:

digraph {
  node [shape=box];
  subgraph cluster {
      c, d;
  }
  a:e -> b:n;
  a:nw -> c:n;
  c:w -> d:sw;
  b:sw -> d;
}

不同于默认情况下的自动连线,利用方位可以手动指定连线的位置。

命名端口

命名端口需要在节点中手动设置端口,并且位置命名。

一般在节点的属性shape=record时,对于节点内分组中的单元进行端口设置。

digraph {
  rankdir=LR;
  fontsize=12;
  node [shape=record];

  struct1 [label="{<head>* int|float|<next>* int}"];
  struct2 [label="{<prev>int|* char|<next>* int}"];
  struct3 [label="{<prev>int|* char|* int}"];

  struct1:next -> struct2:prev;
  struct2:next -> struct3:prev;
  struct1:head:s -> int;
}

struct1:next中的nextstruct2:prev中的prev都是命名端口,通过这两个端口可以将节点struct1* int单元与节点struct2int单元连接起来。而struct1:head:s则即使用了命名端口head,又使用了罗盘端口s

以上可以看出,命名端口的设置出现在节点声明时,使用尖括号<...>包裹,命名端口和罗盘端口都是在节点ID后面加冒号:来引用。

属性叠层

继承

主要是指子图会从父图继承图、连线和节点的所有属性定义

digraph abc{
  graph [bgcolor=pink];
  edge [color=blue];
  node [shape=hexagon];

  a -> b;

  subgraph cluster {
      c -> d;
  }

  b -> c;
}

覆盖

DOT中的属性可以多次定义,后面定义的会覆盖前面定义的。 但是要注意如果一个声明在两个属性定义之间,声明的节点或者连线只会获得前一个属性定义。

digraph {
  node [shape=doublecircle, fontcolor=blue];
  edge [style=bold, color=blue];

  a b e;

  a -> b;

  node [shape=box3d];
  edge [style=dotted, color=red];

  a -> c ->d;
  c -> e;
}

节点abe获得了属性shape=doublecircle,而节点cd的属性被覆盖为了shape=box3d,但是五个节点有相同的属性fontcolor=blue。可见shape属性被覆盖了。同理,连线a -> c -> d的属性style=bold也被style=dotted覆盖,而a -> b却没有变。

当然图的属性定义也能覆盖,不过是父图与子图之间

digraph abc{
  graph [bgcolor=pink];
  edge [color=blue];
  node [shape=box];

  subgraph cluster1 {
      graph [bgcolor=chartreuse];
      edge [color=red];
      node [shape=hexagon];
      a -> b;
  }

  subgraph cluster2 {
      c -> d;
  }

  b -> c;
}

对比cluster1cluster2两个子图,前者把父图abc定义的图、连线和节点的属性全部覆盖了;而后者是继承自父图。

另外,匿名子图还能够在视觉上不出现子图效果的情况下,覆盖父图定义的属性

digraph {
  graph [bgcolor=pink];
  edge [color=blue];
  node [shape=triangle];

  a;

  subgraph {
      node [shape=tripleoctagon];
      b;
  }

  c;

  node [shape=doublecircle];

  d;

  a -> b -> {c d};
}

很明显,这里的节点bshape属性被修改为了tripleoctagon,而节点c却没有被修改。

总结

现学现卖,用Graphviz做个思维导图

DOT mindmap

参考资料