• Graphviz 作图工具


    Graphviz 是有门槛的,在日常工作中,程序员可能也不会使用这个工具,大家普遍更接受 processon 以及 excalidraw 这种拖拽式的作图工具。Graphviz 更偏向由程序自动作图,直接将业务上的模块通过可视化的图形展示出来。

    我比较喜欢Graphviz 的代码生成特性,业务流程每隔一段时间都会有变化,对应的业务流程图也需要做调整,这个时候只需要简单的更新代码就可以,可维护性要比拖拽式的要好。但Graphviz也有缺点,在某些情况下很难用 Graphviz 编辑出理想中的排列结构。

    选择 Graphviz 作为作图工具,主要是想通过代码创建图标,按照 Graphviz 的代码规范就可以生成 svg 的图片。当然,这样的工具也有很多,有些 markdown 编辑器也做了集成,比如:

    1. flowchart.js
    2. Mermaid

    了解 Graphviz 的第一步是官方文档,对象和属性是画图的核心:Graphive 提供的可视化对象有哪些,以及这些对象可以设置的属性。

    以属性 nodesep 来说明,它表示一个属性,作用的对象是 GraphsGraphs 表示无向图。通过文档说明,Graphviz 提供的可视化对象就显而易见。

    在这里插入图片描述
    下面我们利用官网的示例来操作一下,加深对 Graphviz 中对象和属性的认识。建议大家在可视化界面下原型查看效果。

    node

    在 Graphviz 中表示独立的元素,流程图的关键步骤。形状、大小、颜色、边界都是它的基本属性。如果存在多个 node 元素,页面上也可以控制多个 node 水平或垂直布局、间距、位置等。

    节点样式

    style

    下面代码列举了节点样式,大家可关注版本变更跟踪最新属性。代码中没有声明 fillcolor 或者 color 属性,节点填充色就是透明的。

    digraph G {
      rankdir=TB
    
      node1 [style=filled label=filled] 
      node3 [style=diagonals label=diagonals]
      node4 [style=rounded label=rounded]
      node5 [style=dashed label=dashed]
      node6 [style=dotted label=dotted]
      node7 [style=solid label=solid]
      node8 [style=bold label=bold]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

    peripheries

    声明一个节点的基础属性,后续创建的节点默认会继承 node 声明的属性,也可以重新声明覆盖这里的默认属性。

    peripheries 2表示增加一个默认的边缘,0表示关闭边缘。

    digraph G {
        rankdir=TB
        node [ peripheries=2, style=filled, color="#eecc80" ];
        
        { rank="same" 0 0.1 0.2 }
        1 [ peripheries=0, style=filled, color="green" ];
        0.1 -> 1 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    元素水平布局

    digraph {
        nodesep=0.1;
        node1; node2; node3;
    }
    
    • 1
    • 2
    • 3
    • 4

    三个节点是水平排列的,属性 nodesep 控制两个相邻节点的间距,间距单位是 inch 英尺。另外,这三个节点属于相同的 rank,如果继续增加 node4、node5,节点不会发生换行,而是会一直水平向后延伸。

    在这里插入图片描述

    元素垂直布局

    graph {
        rankdir="TB"
        ranksep=".3 equally"
        node1 -- node2 -- node3;
        // node1 -- node2 -- node3 [ style="invis" ];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    graph 表示无向图,digraph 表示有向图,两者没有明显差别。无方向的连线使用 -- 表示,图中的三个节点处于不同的 rankrankdir 指定从上到下排列,ranksep 指定相邻节点的间距。

    在这里插入图片描述

    某些情况下,我们并不希望看到节点间的连线,我们只是想表达一种并列关系,可以通过设置 edge 属性实现隐藏效果。Graphviz 中代码注释也是 //,注释掉的代码就是隐藏连线的实现。

    水平垂直布局

    rank 属性
    graph {
        rankdir="TB"
        ranksep=".3 equally"
        edge [ style="invis" ]
        node1 -- node2;
        { rank="same" node2 -- node3 };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    统一设置 edge 对象属性,通过 rank 指定 node2、node3位于同一层,最终效果如下图。rank 简单理解为元素按照 rankdir 的方向排列,通过指定 rank 将多个元素固定在同一行。
    在这里插入图片描述

    下面是正三角形布局,和上面略有差异。下面要实现 node1 和 node2 在竖直方向对齐,绘制直接三角形。

    graph {
        rankdir="TB"
        ranksep=".3 equally"
        node1 -- node2;
        node1 -- node3;
        { rank="same" node2 -- node3 };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述
    rankdir 生效的条件是 node 间设置了 edge,只有这样 TB、LR 才会生效,表现出节点间的方向上的分层排放。如果要强制将某几个节点放在同一层,可以通过 rank="smae“ 属性做强制声明。

    digraph G {
        rankdir=TB
        { rank="same" 0 0.1 0.2 }
        0 -> 1; 
        1 -> 2;
        2 -> 3;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述
    流程上的层次结构就有了,我们可以利用这样的模式做结构排版,再通过 style=“invis” 将这些 “位置控制” 的节点掩藏掉。

    group 属性

    我们可以依赖 group 属性:如果两个节点设置了相同的 group ,节点间的连线会尽量保持直线。

    下面的代码,如果给 node3 也指定相同的 group,直角的效果就会消失。

    graph {
        rankdir="TB"
        ranksep=".3 equally"
        node1 [ group="g1" ]
        node2 [ group="g1" ]
        node1 -- node2;
        node1 -- node3;
        { rank="same" node2 -- node3 };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

    weight 属性

    使用 weight 也可以实现相同的效果,指定的 weight 值越大,节点间的连线就越短、越值、越垂直。下面代码绘制的效果和上图完全相同。

    graph {
        rankdir="TB"
        ranksep=".3 equally"
        node1 -- node2 [ weight=2 ];
        node1 -- node3;
        { rank="same" node2 -- node3 };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    constraint 属性
    digraph {
        rankdir="TB"
        ranksep=".3 equally"
        node1 -> node2 [weight=100];
        node1 -> node3;
        node2 -> node3 [ constraint=false ];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    同介绍 weight 属性的代码相似,constraint 的作用对象是 edge,属性值为 false 表示不受 rank 约束,最终的效果是 node2 和 node3 位于统一层。

    在这里插入图片描述

    下面的示例,虽然 rankdir 设置为 TB,但因为 constraint 设置为 false,最终的效果是从左到右,而不是从上到下。

    digraph {
        rankdir="TB"
        ranksep=".3 equally"
        node1 -> node2 -> node3 -> node4 [ constraint=false ];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    subgraph

    结构图一般都有多个模块组成,每个模块内部又包含完整的处理流程。在 Graphviz 中使用 subgraph 来表示子图。

    网上找了 subgrahp 的示例,注意观察:subgraph 后的命名都是以 cluster 为前缀的,并通过指定 compoundltaillhead 来实现 subgraph 间的连线。观察 "Item 1" -> "Item 3" 的连线属性,这里实现了在两个 subgraph间创建连线。

    digraph G {
        graph [fontsize=10 fontname="Verdana" compound=true];
        node [shape=record fontsize=10 fontname="Verdana"];
        subgraph cluster_0 {
            node [style=filled];
            "Item 1" "Item 2";
            label = "Container A";
            color=blue;
        }
        subgraph cluster_1 {
            node [style=filled];
            "Item 3" "Item 4";
            label = "Container B";
            color=blue;
        }
        subgraph cluster_2 {
            node [style=filled];
            "Item 5" "Item 6";
            label = "Container C";
            color=blue;
        }
        // Edges between nodes render fine
        "Item 1" -> "Item 2";
        "Item 2" -> "Item 3";
        // Edges that directly connect one cluster to another
        "Item 1" -> "Item 3" [ltail=cluster_0 lhead=cluster_1];
        "Item 1" -> "Item 5" [ltail=cluster_0 lhead=cluster_2];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    在这里插入图片描述

    节点间的连线非常不好把握,有时候特别想连线到目标节点中间,但箭头偏偏指在了别处,虽然也将意思表达清楚了,但就是看起来不舒服。

  • 相关阅读:
    利用特殊反序列化组件攻击原生反序列化入口
    SVN目录中空格引起的权限异常问题?
    探索高效开发神器:Blackbox AI(免费编程助手)
    C语言02、语句、函数
    【JavaScript】一文了解定时器的使用
    智能疾病查询接口
    Abnova LiquidCell-负富集细胞分离和回收系统
    springboot家政服务管理平台 LW +PPT+源码+讲解
    工作:三菱伺服驱动器连接参数及其电机钢性参数配置与调整
    JS基础6--逻辑运算符
  • 原文地址:https://blog.csdn.net/f1520107395/article/details/133702048