使用graphviz画图

2020年12月1日 发表评论 阅读评论

Graphviz是大名鼎鼎的贝尔实验室的几位牛人开发的一个画图工具。它的理念和一般的“所见即所得”的画图工具不一样,是“所想即所得”。 Graphviz提供了dot语言来编写绘图脚本。

一、安装 

graphviz可以使用在windows上,也可以使用在linux和MAC上,在centos上只需要一条指令yum install graphviz 即可完成安装。其他OS上的安装可以参考官方文档

二、使用

1、简单示例

一个简单的示例如下(其实可以再简单点,直接hello -> world):

# vim text.dot
digraph G {
        hello [shape=box];
        world [style=filled, color="1,1,1"];
        hello -> world [label="Yes"];
    }

编译时输出格式可以根据自己的需要来灵活选择,主要有一下三种:

dot -Tpng test.dot -o test.png
dot -Tsvg test.dot -o test.svg
dot test.dot -Tpdf -o test.pdf

2、有向无向图

简单介绍下DOT语言的语法,根据输出图片的是否有箭头指向,会有有向图和无向图的区分,如下:

无向图:
graph graphname {
     a -- b -- c;
     b -- d;
 }
有向图:
digraph graphname {
     a -> b -> c;
     b -> d;
 }

注:双向图可以使用dir = "both" 解决,代码如下:

digraph graphName {
A->B[dir="both"]
}

3、节点和边的属性设置

边和节点的属性设置方法不一致,节点的属性被放置在只包含节点名称的表达式后,如下:

digraph G {
        hello [shape=box];//设置节点属性
        world [style=filled, color="1,1,1"];//节点多个属性用逗号分开
        hello -> world [label="Yes"];//设置边的属性
    }

4、节点和属性总结

画图时需要对图片做一些特别的处理,例如加粗、把图变色等。我们要控制这些东西,就需要用到属性。

属性有四种:

  1. 用在节点上(Node, N) ; 
  2. 用在线段上(Edge, E);   
  3. 用在根图片上(Graph, G);   
  4. 用在子图片上(Cluster subgraph, C)

对于节点(node) 的属性,有以下几种指定法:

  • 节点名[节点属性名=值];
  • 节点名[节点属性名=值,节点属性名=值];
  • node [节点属性名=值,节点属性名=值];

属性指定的语句必须要被中括号括起。当一次指定多值时,需用英文逗点隔开。第三行中的node 是个关键字,用来代称「图片范围内」所有「还没创建」的节点,或者您也可将它理解为:在当前大括号的范围内,所有尚未创建节点的属性预设值,会被这个语句给变更。

对于线段(edge)的属性指定,与上述节点属性指定方式很类似:

  •  节点名->节点名[线段属性名=值];
  •  节点名--节点名[线段属性名=值,线段属性名=值];
  • edge [线段属性名=值,线段属性名=值]; 其中edge 是关键字。

5、子图

subgraph 的作用主要有 3 个:

  1. 表示图的结构,对节点和边进行分组
  2. 提供一个单独的上下位文设置属性
  3. 针对特定引擎使用特殊的布局。比如下面的例子,如果 subgraph 的名字以 cluster 开头,所有属于这个子图的节点会用一个矩形和其他节点分开。
digraph graphname{
    a -> {b c};
    c -> e;
    b -> d;
    subgraph cluster_bc {
        bgcolor=red;
        b;
        c;
    }
    subgraph cluster_de {
        label="Block"
        d;
        e;
    }
}

6、布局

默认情况下图是从上到下布局的,通过设置 rankdir="LR" 可以让图从左到右布局,对应的也有从上到下的布局为 rankdir="TB"。
一个简单的表示 CI&CD 过程的图:

digraph pipleline {
    rankdir=LR;
    g [label="Gitlab"];
    j [label="Jenkins"];
    t [label="Testing"];
    p [label="Production" color=red];
    g -> j [label="Trigger"];
    j -> t [label="Build"];
    t -> p [label="Approved"];
}

graphviz

三、一些demo示例

1、进程内部模块调用示例

digraph G{
    size = "5, 5";//图片大小
    main[shape=box];/*形状*/
    main->parse;
    parse->execute;
    main->init[style = dotted];//虚线
    main->cleanup;
    edge[color = green]; // 连接线的颜色
    execute->{make_string; printf}//连接两个
    init->make_string;
    main->printf[style=bold, label="100 times"];//线的 label
    make_string[label = "make a\nstring"]// \n, 这个node的label,注意和上一行的区别
    node[shape = box, style = filled, color = ".7.3 1.0"];//一个node的属性
    execute->compare;
}

2、游戏资源更新流程

digraph startgame {
    label="游戏资源更新流程"
    rankdir="TB"
    start[label="启动游戏" shape=circle style=filled]
    ifwifi[label="网络环境判断是否 WIFI" shape=diamond]
    needupdate[label="是否有资源需要更新" shape=diamond]
    startslientdl[label="静默下载" shape=box]
    enterhall[label="进入游戏大厅" shape=box]
    enterroom[label="进入房间" shape=box]
    resourceuptodate[label="资源不完整" shape=diamond]
    startplay[label="正常游戏" shape=circle fillcolor=blue]
    warning[label="提醒玩家是否更新" shape=diamond]
    startdl[label="进入下载界面" shape=box]
    //{rank=same; needupdate, enterhall}
    {shape=diamond; ifwifi, needupdate}
    start -> ifwifi
    ifwifi->needupdate[label="是"]
    ifwifi->enterhall[label="否"]
    needupdate->startslientdl[label="是"]
    startslientdl->enterhall
    needupdate->enterhall[label="否"]
    enterhall -> enterroom
    enterroom -> resourceuptodate
    resourceuptodate -> warning[label="是"]
    resourceuptodate -> startplay[label="否"]
    warning -> startdl[label="确认下载"]
    warning -> enterhall[label="取消下载"]
    startdl -> enterhall[label="取消下载"]
    startdl -> startplay[label="下载完成"]
}

3、生成有颜色和形状的图表

digraph example3 {
    Server1 -> Server2
    Server2 -> Server3
    Server3 -> Server1
    Server1 [shape=box, label="Server1\nWeb Server", fillcolor="#ABACBA", style=filled]
    Server2 [shape=triangle, label="Server2\nApp Server", fillcolor="#DDBCBC", style=filled]
    Server3 [shape=circle, label="Server3\nDatabase Server", fillcolor="#FFAA22", style=filled]
}

4、字节点调用图

graph G{
"黑海" [shape = circle, color = blueviolet, fontcolor = blueviolet, fontsize = 20];
"黑海" -- "亚速海" [label = "刻赤海峡"];
subgraph cluster_T{
label = "黑海海峡";
fontsize = 24;
fillcolor = darkslategray;
style = filled;
fontcolor = white;
node [fontcolor = white, color = white];
"博斯普鲁斯海峡" -- "马尔马拉海" -- "达达尼尔海峡" [color = white];
"博斯普鲁斯海峡" [shape = parallelogram];
"达达尼尔海峡" [shape = parallelogram];
}
"黑海" -- "博斯普鲁斯海峡" [color = red ,penwidth = 2];
"达达尼尔海峡" -- "爱琴海" [color = red ,penwidth = 2];
subgraph cluster_M{
label = "地中海海域";
fontsize = 24;
"西部地中海" [shape = Mcircle, style = filled, color = grey, fillcolor = aquamarine, fontsize = 20];
"中部地中海" [shape = Mcircle, style = filled, color = grey, fillcolor = aquamarine, fontsize = 20];
"直布罗陀海峡" [shape = parallelogram, fontcolor = red];
"西西里海峡" [shape = parallelogram ];
"中部地中海" -- {"爱琴海" "爱奥尼亚海" "西西里海峡"};
"西部地中海" -- {"西西里海峡" "第勒安海" "利古里亚海" "伊比利海" "阿尔沃兰海"};
"爱奥尼亚海" -- "亚得里亚海"; 30 "阿尔沃兰海" -- "直布罗陀海峡";
}
}

5、python模块调用示例

import pygraphviz as pgv
G = pgv.AGraph(directed=True, rankdir="TB")
# 设置节点标签
Root = "道路交通流畅"
negative_1 = "平均延误时间"
negative_2 = "负荷度"
negative_3 = "小区位置"
negative_4 = "相对延误率"
negative_5 = "房屋密度"
negative_6 = "人口密度"
negative_7 = "总延误率"
negative_8 = "排队率"
negative_9 = "行驶时间"
positive_1 = "通行能力"
positive_2 = "公路层级"
positive_3 = "路网结构"
positive_4 = "行驶速度"
positive_5 = "路网长度"
positive_6 = "小区面积"
positive_7 = "内部道路密度"
positive_8 = "路网密度"
# 添加节点
G.add_node(Root, style="filled", shape="box3d", color="#feb64d")
for negative in [eval(_) for _ in dir() if _.startswith("negative")]:
    G.add_node(negative, style="filled", shape="ellipse", color="#CFDBF6")
for positive in [eval(_) for _ in dir() if _.startswith("positive")]:
    G.add_node(positive, style="filled", shape="ellipse", color="#B4E7B7")
# 添加边
G.add_edges_from([[Root, negative_1], [Root, negative_6], [Root, negative_8], [Root, negative_9],
                  [negative_1, negative_2], [negative_1, negative_7], [negative_2, negative_3],
                  [negative_2, negative_7], [negative_3, negative_4], [negative_8, negative_9],
                  [positive_2, negative_5], [positive_3, negative_4], [positive_4, negative_5]],
                 color="#B4DBFF", style="dashed", penwidth=1.5)
G.add_edges_from([[Root, positive_1], [Root, positive_8], [negative_5, negative_4],
                  [negative_6, positive_4], [negative_5, positive_4], [negative_9, positive_5],
                  [positive_1, positive_2], [positive_2, positive_3], [positive_6, positive_5],
                  [positive_7, positive_6], [positive_8, positive_7]],
                 color="#B4E7B7", style="dashed", penwidth=1.5)
# 导出图形
G.layout()
G.draw("因子相关性图.png", prog="dot")

6、结构体切割

digraph dfd2{
        node[shape=record]
        subgraph level0{
        enti1 [label="Customer" shape=box];
        enti2 [label="Manager" shape=box];
        }
        subgraph cluster_level1{
                        label ="Level 1";
                        proc1 [label="{<f0> 1.0|<f1> One process here\n\n\n}" shape=Mrecord];
                        proc2 [label="{<f0> 2.0|<f1> Other process here\n\n\n}" shape=Mrecord];
                        store1 [label="<f0>    |<f1> Data store one"];
                        store2 [label="<f0>   |<f1> Data store two"];
            {rank=same; store1, store2}
        }
        enti1 -> proc1
        enti2 -> proc2
        store1 -> proc1
        store2 -> proc2
    proc1 -> store2
    store2 -> proc1
}




本站的发展离不开您的资助,金额随意,欢迎来赏!

You can donate through PayPal.
My paypal id: itybku@139.com
Paypal page: https://www.paypal.me/361way

分类: 开源软件 标签:
  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.