zoukankan      html  css  js  c++  java
  • 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"];
        }
    }
    
  • 相关阅读:
    个人冲刺二(7)
    个人冲刺二(6)
    个人冲刺二(5)
    个人冲刺二(4)
    对称二叉树 · symmetric binary tree
    108 Convert Sorted Array to Binary Search Tree数组变成高度平衡的二叉树
    530.Minimum Absolute Difference in BST 二叉搜索树中的最小差的绝对值
    pp 集成工程师 mism师兄问一问
    17. Merge Two Binary Trees 融合二叉树
    270. Closest Binary Search Tree Value 二叉搜索树中,距离目标值最近的节点
  • 原文地址:https://www.cnblogs.com/kekukele/p/13544915.html
Copyright © 2011-2022 走看看