• markdown 绘图利器之graphviz


    概述

    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/rectanglebox 是同义词,noneplaintext 是同义词。

    当想让节点的形状完全由类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&#92; dle|<f2> right"];
        struct2[label="<f0> one|<f1> two"];
        struct3[label="hello&#92;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&#92; dle|<f2> right"];
        struct2[label="<f0> one|<f1> two"];
        struct3[label="hello&#92;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;
    }
    

    微信公共号

    NFVschool,关注最前沿的网络技术。

    参考

    附录

    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"];
        }
    }
    
  • 相关阅读:
    fabu dao fenleizhong
    net core 发布到服务器的方式
    暗示net core
    new post
    gg
    asb
    Nodejs 8.0 踩坑经验汇总
    禁止未发布的博文上首页等
    SortedList
    JSON.stringify 语法实例讲解
  • 原文地址:https://www.cnblogs.com/kekukele/p/13544915.html
Copyright © 2020-2023  润新知