graphviz 程序生成多种类型图表详解

简介

  一幅图抵得上千言万语,这在描述复杂的计算机系统时尤为正确。当系统环境变得更加复杂时,用图将它们表示出来并记入文档就显得更加重要。例如,虚拟化技术有很多优点,但它们通常会让环境变得更加复杂和更难理解。Graphviz 是一个可以创建图表的灵活应用程序,可以轻松实现脚本化。本文将介绍 Graphviz 的安装、使用,以及其中使用 DOT 语言的基础知识,并提供了一些示例脚本。本文有助于您了解 Graphviz 应用程序的基础知识,以及如何编写自动创建图表的脚本。

  还有另外一个我觉得更加牛逼的作用,在使用 markdown 时无法直接将图片直接粘贴上去,还得想办法将图片传到某一个中间服务再引入,有么有?而 graphviz 可以完美的解决这个问题,在 markdown 中也可以直接编写相应的代码生成相应的图表,使用起来相当 easy,越用越 happy。

  针对 graphviz 想要了解更加详细内容,请查看其对对应的 官方网址

安装 graphviz

  Graphviz 是一个开源工具,可以运行在类似于 UNIX® 的大多数平台和 Microsoft® Windows® 之上。graphviz 支持 Windows、Mac OS X、FreeBSD、Solaris、Linux 等多种系统

  •  Ubuntu 或 Debian 下安装: 
sudo apt-get install graphviz
或
sudo yum install graphviz
  •  Mac 下安装使用 brew 命令:

  我本人 mac 上安装的时候并没有装 ruby,故需要先安装 ruby 再进行安装 graphviz

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" < /dev/null 2> /dev/null ; brew install caskroom/cask/brew-cask 2> /dev/null
brew install graphviz
  •  Windows 下安装:

  由于本人一直使用的是 mac 和 linux,故此次试验并没有在 windows 上做验证,请查看官方文档进行安装。

DOT 语言基础

  安装 Graphviz 之后,就可以开始使用工具创建图表,第一步是创建一个 DOT 文件。该 DOT 文件是一个文本文件,描述了图表的组成元素以及它们之间的关系,以便该工具可以生成这些组成元素和它们之间的关系的图形化表示。

  1. 编写以 dot 为后缀的源代码文件 hello.dot,内容如下:

  digraph {
     hello -> world;
  }

   2. 使用 dot 命令编译

  dot hello.dot -T png -o hello.png

   完整的命令格式为:

  <cmd> <inputfile> -T <format> -o <outputfile>

  其中graphviz 的 <cmd> 有好几种,每种使用方法都完全相同,差别只在于渲染出来的图片效果不一样。 man 中的简介是这样的  

<cmd>介绍
dot 渲染的图具有明确方向性。
neato 渲染的图缺乏方向性。
twopi 渲染的图采用放射性布局。
circo 渲染的图采用环型布局。
fdp 渲染的图缺乏方向性。
sfdp 渲染大型的图,图片缺乏方向性。

  可以透过 man <cmd> 取得进一步说明。但还是亲自用用比较容易理解。在本文中,凡没有说明的图,预设都是以 dot 渲染出来的。

  3. 查看效果

  运行 2 中的命令后会生成对应的 png 文件,双击打开查看效果如下所示:

    

基础应用

1. 编写一个案例 dot 文件为:example1.dot

graph example1 {
    Server1 -- Server2
    Server2 -- Server3
    Server3 -- Server1
}

  上述内容中共有三个节点(Server1、Server2 和 Server3)。-- 定义了节点之间的联系。这个使用 DOT 语言的例子说明,Server2、 Server2 连接到 Server3,而 Server3 连接到 Server1。

  创建 DOT 文件后,可以运行 dot 命令来生成图表,如 2 中所示。T png 将输出格式指定为 PNG,而 –o example1.png 指定必须将输出保存到一个名叫 example1.png 的文件中。

2. 从 DOT 文件生成图像

$ dot example1.dot –T png –o example1.png

生成的图表如下:

    

  第一个示例 (example1.dot) 描述了一个无向图,即用不带箭头的直线表示节点之间的联系的图。

  有向图不仅能表示节点之间的联系,而且能用箭头表示节点之间流动的方向。要创建有向图,可以在 DOT 文件的第一行上指定 digraph 而非 graph,并在节点之间使用 ->,而不是 --

 3. 生成有向图,创建 dot 文件:example2.dot

digraph example2 {
    Server1 -> Server2
    Server2 -> Server3
    Server3 -> Server1
}

 使用 dot 命令生成对应的图表如下:

    

  你也可以轻松控制图中每个节点的形状、颜色和标签。具体方法是列出每个节点的名称,然后将选项放在名称后面的括号中。例如,代码行 

  Server1[shape=box, label="Server1\nWebServer", fillcolor="#ABACBA", style=filled]

  定义了 Server1 节点应该是一个长方形,有一个 Server1\nWeb Server 标签(\n 表示一个新行),颜色设为十六进制 (hex) 颜色 #ABACBA,而且该颜色应该填充了节点。

 4. 生成有颜色和形状的图表

 创建 dot 文件:example3.dot

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]
}

对应的生成相应图表如下:

    

编写脚本创建 Graphviz 图表

  在了解了 Graphviz DOT 语言的基础知识之后,您可以开始创建脚本,从而动态创建一个 DOT 文件。这允许您动态创建始终准确且保持最新的图表。

  以下示例是一个 bash shell 脚本(hmc_to_dot.sh),它连接到 Hardware Management Console (HMC),收集托管服务器和逻辑分区 (LPAR) 的相关信息,然后使用这些信息来创建 DOT 输出。

#!/bin/bash
 
HMC="$1"
serverlist=`ssh -q -o "BatchMode yes" $HMC lssyscfg -r sys -F "name" | sort`
 
echo "graph hmc_graph{"
for server in $serverlist; do
    echo " \"$HMC\" -- \"$server\" "
    lparlist=`ssh -q -o "BatchMode yes" $HMC lssyscfg -m $server -r lpar -F "name" | sort`
    for lpar in $lparlist; do
             echo "    \"$server\" -- \"$lpar\" "
    done
done
echo "}"

  通过提供一个 HMC 服务器名称作为参数传递给脚本,便可运行此脚本。该脚本将传递的第一个参数设置为 $HMC 变量。设置 $serverlist 变量的方法是连接到 HMC 并获得该 HMC 控制的所有托管服务器的清单。在这些托管服务器上进行循环,而脚本将为每台托管服务器打印一行 "HMC" -- "server" ,这表明 Graphviz 在每台 HMC 与其托管服务器之间绘制了一条直线。此外针对每台托管服务器,脚本再次连接到 HMC 并获得该托管系统上的 LPAR 清单,然后通过它们循环打印一行 "server" -- "LPAR"。这表明 Graphviz 在每台托管服务器与其 LPAR 之间都绘制了一条直线。(此脚本要求您在运行脚本的服务器与 HMC 之间设置 Secure Shell (SSH) 密钥身份验证)。

命令执行如下:

  ./hmc_to_dot.sh hmc_name

以上执行对应输出的内容如下所示:

graph hmc_graph{
    "hmc01" -- "test520"
        "test520" -- "lpar2"
        "test520" -- "lpar3"
    "hmc01" -- "test570"
        "test570" -- "aixtest01"
        "test570" -- "aixtest02"
        "test570" -- "aixtest03"
    "hmc01" -- "test510"
        "test510" -- "lpar1"
}

 你可以轻松从脚本生成图,具体方法是运行以下命令:

./hmc_to_dot.sh hmc_server_name | dot -T png -o hmc_graph.png

 运行脚本,该脚本会动态创建 DOT 语言,然后将这些输出传递给 dot 命令,以便让它创建一个文件名为 hmc_graph.png 的图表。下图显示了创建的图表。

     

基础语法总结(供有识之士参考)

具体的图说明内容包含属性(attr)、节点(node)、边(edge)和子图(subgraph)说明。

节点属性如下 :

NameDefaultValues
color black node shape color
comment   any string (format-dependent)
distortion 0.0 node distortion for shape=polygon
fillcolor lightgrey/black node fill color
fixedsize false label text has no affect on node size
fontcolor black type face color
fontname Times-Roman font family
fontsize 14 point size of label
group   name of node’s group
height .5 height in inches
label node name any string
layer overlay range all, id or id:id
orientation 0.0 node rotation angle
peripheries shape-dependent number of node boundaries
regular false force polygon to be regular
shape ellipse node shape; see Section 2.1 and Appendix E
shapefile   external EPSF or SVG custom shape file
sides 4 number of sides for shape=polygon
skew 0.0 skewing of node for shape=polygon
style   graphics options, e.g. bold, dotted, filled; cf. Section 2.3
URL   URL associated with node (format-dependent)
width .75 width in inches
z 0.0 z coordinate for VRML output

边框属性:

NameDefaultValues
arrowhead normal style of arrowhead at head end
arrowsize 1.0 scaling factor for arrowheads
arrowtail normal style of arrowhead at tail end
color black edge stroke color
comment   any string (format-dependent)
constraint true use edge to affect node ranking
decorate   if set, draws a line connecting labels with their edges
dir forward forward, back, both, or none
fontcolor black type face color
fontname Times-Roman font family
fontsize 14 point size of label
headlabel   label placed near head of edge
headport   n,ne,e,se,s,sw,w,nw
headURL   URL attached to head label if output format is ismap
label   edge label
labelangle -25.0 angle in degrees which head or tail label is rotated off edge
labeldistance 1.0 scaling factor for distance of head or tail label from node
labelfloat false lessen constraints on edge label placement
labelfontcolor black type face color for head and tail labels
labelfontname Times-Roman font family for head and tail labels
labelfontsize 14 point size for head and tail labels
layer overlay range all, id or id:id
lhead   name of cluster to use as head of edge
ltail   name of cluster to use as tail of edge
minlen 1 minimum rank distance between head and tail
samehead   tag for head node; edge heads with the same tag are
sametail   merged onto the same port
style   tag for tail node; edge tails with the same tag are merged onto the same port
taillabel   graphics options, e.g. bold, dotted, filled; cf. Section 2.3
tailport   label placed near tail of edge n,ne,e,se,s,sw,w,nw
tailURL   URL attached to tail label if output format is ismap
weight 1 integer cost of stretching an edge

图属性如下:

NameDefaultValues
bgcolor   background color for drawing, plus initial fill color
center false center drawing on page
clusterrank local may be global or none
color black for clusters, outline color, and fill color if fillcolor not defined
comment   any string (format-dependent)
compound false allow edges between clusters
concentrate false enables edge concentrators
fillcolor black cluster fill color
fontcolor black type face color
fontname Times-Roman font family
fontpath   list of directories to search for fonts
fontsize 14 point size of label
label   any string
labeljust centered ”l” and ”r” for left- and right-justified cluster labels, respectively
labelloc top ”t” and ”b” for top- and bottom-justified cluster labels, respectively
layers   id:id:id…
margin .5 margin included in page, inches
mclimit 1.0 scale factor for mincross iterations
nodesep .25 separation between nodes, in inches.
nslimit   if set to f, bounds network simplex iterations by (f)(number of nodes) when setting x-coordinates
nslimit1   if set to f, bounds network simplex iterations by (f)(number of nodes) when ranking nodes
ordering   if out out edge order is preserved
orientation portrait if rotate is not used and the value is landscape, use landscape orientation
page   unit of pagination, e.g. “8.5,11”
pagedir BL traversal order of pages
quantum   if quantum ¿ 0.0, node label dimensions will be rounded to integral multiples of quantum
rank   same, min, max, source or sink
rankdir TB LR (left to right) or TB (top to bottom)
ranksep .75 separation between ranks, in inches.
ratio   approximate aspect ratio desired, fill or auto
remincross   if true and there are multiple clusters, re-run crossing minimization
rotate   If 90, set orientation to landscape
samplepoints 8 number of points used to represent ellipses and circles on output (cf. Appendix C
searchsize 30 maximum edges with negative cut values to check when looking for a minimum one during network simplex
size   maximum drawing size, in inches
style   graphics options, e.g. filled for clusters
URL   URL associated with graph (format-dependent)

具体实例

  这里列举几个常用到的例子,还有更加详细的例子还可以参考 官网例子中心

 下面是一个二叉树代码:

digraph g {
    node [shape = record,height=.1];
    node0[label = "<f0> |<f1> G|<f2> "];
    node1[label = "<f0> |<f1> E|<f2> "];
    node2[label = "<f0> |<f1> B|<f2> "];
    node3[label = "<f0> |<f1> F|<f2> "];
    node4[label = "<f0> |<f1> R|<f2> "];
    node5[label = "<f0> |<f1> H|<f2> "];
    node6[label = "<f0> |<f1> Y|<f2> "];
    node7[label = "<f0> |<f1> A|<f2> "];
    node8[label = "<f0> |<f1> C|<f2> "];
    "node0":f2 -> "node4":f1;
    "node0":f0 -> "node1":f1;
    "node1":f0 -> "node2":f1;
    "node1":f2 -> "node3":f1;
    "node2":f2 -> "node8":f1;
    "node2":f0 -> "node7":f1;
    "node4":f2 -> "node6":f1;
    "node4":f0 -> "node5":f1;
}

    

python 调用

  除了可以使用 dot 文件编写图形外,也可以使用python编写相关的代码,生成图形文件, 安装 python 对应的 graphviz 相应的模块

  pip install pygraphviz

   引用 pygraphviz

  import pygraphviz as pgv

   初始化图类

  G = pgv.AGraph()

   增加节点和边

  G.add_node('a')       # adds node 'a'
  G.add_edge('b','c')   # adds edge 'b'-'c' (and also nodes 'b', 'c')

  设置属性

  G.graph_attr['label'] = 'test graphf'
  G.node_attr['shape'] = 'circle'
  G.edge_attr['color'] = 'red'

   设置输出的格式

  G.layout()              # default to neato
  G.layout(prog='dot')    # use do

   输出到文件

  G.draw('file.png')              # write previously positioned graph to PNG file
  G.draw('file.ps',prog='circo')  # use circo to position, write PS file

   效果如下图所示:

    

结束语

  由于当今的动态虚拟环境的存在,系统在不断变化,很难保持文档与图表的时效性和准确性。本文介绍了 Graphviz 的基础知识,以及如何通过编写脚本,利用 Graphviz 的强大功能来创建最新、最准确的自动化环境图表。您可以从 cron 运行这些脚本,并将图表放在 Web 服务器上,这样就可以在您需要时随时重新创建图表,并通过 Web 浏览器轻松访问它们。

总结使用案例

案例一:

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="下载完成"]
}

        

案例二:

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;
}

        

 

posted @ 2017-10-09 17:26  星火燎原智勇  阅读(4127)  评论(1编辑  收藏