# graphviz基本使用及常见问题

### 三、使用graphviz

#### 3.1 基本信息

graphviz中包含了众多的布局器：

dot            默认布局方式，主要用于有向图
neato       基于spring-model(又称force-based)算法
twopi        径向布局
circo        圆环布局
fdp           用于无向图

#### 基本语法

 关系 有向图 无向图 一对一 a->b; a–b; 一对多 a->{b;c;d}; a–{b;c;d}; 多对一 {b;c;d}->a; {b;c;d}–a; 多对多 {m,n,p,q}->{b;c;d}; {m,n,p,q}->{b;c;d}

#### 3.2 基本使用

（1）建立一个first.dot脚本：

digraph first1{
a;
b;
c;
d;
a->b;
b->d;
c->d;
}


digraph first2{
a->b;
b->d;
c->d;
}


（2）画图

dot -Tpng first.dot -o first.png

（3）画图结果

digraph demo1 {

//解决中文乱码
node[fontname = "Microsoft YaHei"];
edge[fontname = "Microsoft YaHei"];
graph[fontname = "Microsoft YaHei"];

label="一对多示例"

{你} -> {
a;
b;
c;
};

}

关系

graph gvDemo1 {
a -- b
a -- b
b -- a [color=blue]
}

digraph gvDemo2 {
a -> b
a -> b
b -> a [color=blue style=filled]
}

digraph gvABC {
{ a b c } -> { d e f }
}

digraph gvDemo {
main -> parse -> execute;
main -> init;
main -> cleanup;
execute -> make_string;
execute -> printf
init -> make_string;
main -> printf;
execute -> compare;
}

digraph gvDemo{
node [peripheries=2 style=filled color="#eecc80"]
edge [color="sienna" fontcolor="green"]
main -> parse -> execute;
main -> init [arrowhead = box];
main -> cleanupi -> main;
make_string[label = once shape=parallelogram style=filled ]
execute -> make_string[label=Go style=dashed arrowtail=diamond];
execute -> printf [shape=box];
init -> make_string;
main -> printf[dir=none];
execute -> compare[dir=both];
}


digraph gvDemo3 {
edge[fontname="Microsoft YaHei"]
node[fontname="Microsoft YaHei"]
graph[fontname="Microsoft YaHei"]
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="下载完成"]
}


graph gvDemo4{
"黑海" -- "亚速海";
"黑海" -- "博斯普鲁斯海峡"
"达达尼尔海峡" -- "爱琴海"
subgraph cluster_T{//新东西
label = "黑海海峡";//新东西
"达达尼尔海峡" -- "马尔马拉海" -- "博斯普鲁斯海峡";
}
subgraph cluster_M{
label = "地中海海域";
"中部地中海" -- {"爱琴海" "爱奥尼亚海" "西西里海峡"}; //也是新东西
"西部地中海" -- {"西西里海峡" "第勒尼安海" "利古里亚海" "伊比利海" "阿尔沃兰海"};
"爱奥尼亚海" -- "亚得里亚海";
"阿尔沃兰海" -- "直布罗陀海峡";
}
}

digraph G{
Country->Narrow;
Country->Middle;
Country->Large;
Narrow->small_underpopulated[label="small population"];
Narrow->small_overpopulated[label="large population"];顦
Large->vast_overpopulated[label="large population"];
Large->vast_underpopulated[label="small population"];
Middle->small_overpopulated[label="More than average"];顦
Middle->vast_underpopulated[label="Less than average"];
small_overpopulated->A[label="GDP:HIGH"];
small_overpopulated->B[label="GDP:LOW"];
vast_overpopulated->C[label="GDP:HIGH"];
vast_overpopulated->D[label="GDP:LOW"];
vast_underpopulated->E[label="GDP:HIGH"];
vast_underpopulated->F[label="GDP:LOW"];
F->F[label="NOT EXIST",color="red"];
small_underpopulated->G[label="GDP:HIGH"];
small_underpopulated->H[label="GDP:LOW"];
D->I[label="RoadScattered"];
D->Y[label="HighCoveraged"];
C->J[label="HighCoveraged"];
A->K[label="HighCoveraged"];
B->L[label="RoadScattered"];
G->M[label="HighCoveraged"];
H->H[label="Number Too Less",color="red"];
E->O[label="RoadScattered"];
E->P[label="HighCoveraged"];
M->Q[label="Both"];
K->R[label="Both"];
L->S[label="Oil More"];
O->T[label="Oil More"];
P->U[label="Both"];
J->V[label="Oil More"];
J->W[label="Both"];
K->X[label="Electricity More"];
I->Z[label="Neither"];
Y->1[label="Neither"];
Q->Q[color="red"];
R->R[color="red"];
X->X[color="red"];
S->S[color="red"];
T->T[color="red"];
U->U[color="red"];
V->V[color="red"];
W->W[color="red"];
Z->Z[color="red"];
1->1[label="Number Too Less",color="red"];
}


digraph G {
A -> B;
A -> C -> B;
A -> D -> B;
}

节点形状

digraph G {
size = "4, 4";
main [shape=box]; /* 这是注释 */
main -> parse [weight=8];
parse -> execute;
main -> init [style=dotted];
main -> cleanup;
execute -> { make_string; printf}
init -> make_string;
edge [color=red]; // 设置生效
main -> printf [style=bold,label="100 times"];
make_string [label="make a\n字符串"];
node [shape=box,style=filled,color=".7 .3 1.0"];
execute -> compare;
}


标签

digraph G {
a -> b -> c;
b -> d;
a [shape=polygon,sides=5,peripheries=3,color=lightblue,style=filled];
c [shape=polygon,sides=4,skew=.4,label="hello world"]
d [shape=invtriangle];
e [shape=polygon,sides=4,distortion=.7];
}


类似HTML的样式

 digraph html {
abc [shape=none, margin=0, label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
<TR>
<TD ROWSPAN="3"><FONT COLOR="red">hello</FONT><BR/>world</TD>
<TD COLSPAN="3">b</TD>
<TD ROWSPAN="3" BGCOLOR="lightgrey">g</TD>
<TD ROWSPAN="3">h</TD>
</TR>
<TR>
<TD>c</TD>
<TD PORT="here">d</TD>
<TD>e</TD>
</TR>
<TR>
<TD COLSPAN="3">f</TD>
</TR>
</TABLE>>];
}

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

示例


//定义节点属性
digraph g {
//==========定义节点关系============
a->b;
b->c;
c->a;
c->d->e->f;
d->g;
e->h;
//==========定义节点属性============
//定义a节点为长方形, 样式为填充, 填充颜色为#ABACBA
a[shape=box,label="Server1\nWebServer",fillcolor="#ABACBA",style=filled];
//定义b为5边形, 标签为"bb", 样式为填充, 填充色为red
b[shape=polygon,sides=5,label="bb",style=filled,fillcolor=red];
//c, 默认为椭圆
d[shape=circle]; //园
e[shape=triangle]; //三角形
f[shape=polygon, sides=4, skew=0.5]; //平行四边形
g[shape=polygon, distortion=0.5]; //梯形, 上边长
h[shape=polygon, distortion=-.5]; //梯形, 下边长
}

digraph g {
//edge[style=dashed]; //定义边的样式, 虚线
node[peripheries=2, style=filled, color="#eecc80"];
a->b [color=red, style=dashed]; //定义边的颜色, 红色 (b和方括号之间必须有空格)
b->c; //箭头, 三角形; 箭尾, 菱形
b->d [arrowhead=box]; //箭头, 长方形
b->e [dir=none]; //没有箭头
d->f [dir=both]; //双向箭头
f->h [label=go]; //定义edge的标签
f->k [arrowhead=diamond]; //更改箭头形状
k->y [headlabel="哈哈", taillabel="洗洗"];
}


graph g {
edge[style=dashed]; //定义边的样式, 虚线
a -- b [color=red]; //定义边的颜色, 红色 (b和方括号之间必须有空格)
}

ER图

digraph g {
graph [
rankdir = "LR"
];
node [
fontsize = "16"
shape = "ellipse"
];
edge [
];

"user" [
label = "User| <id> id|username|password|last|status"
shape = "record"
];

"profile" [
label = "Profile| <id> id | name | sex | age | address | icq | msn"
shape = "record"
];

user:id->profile:id [label="1:1"];

"category" [
label = "Category| <id> id | <pid> pid | name | status"
shape = "record"
];

category:pid->category:id [label="1:n"];

"article" [
label = "Article| <id> id| <user_id> user_id | <cid> category_id | title | content | datetime | status"
shape = "record"
];

article:user_id->user:id [label="1:n"];
article:cid->category:id [label="1:n"];

"feedback" [
label = "Feedback| <id> id| <user_id> user_id | <article_id> article_id | title | content | datetime | status"
shape = "record"
];

feedback:user_id->user:id [label="1:n"];
feedback:article_id->article:id [label="1:n"];

}

复杂标签

digraph graphname{
/* 把节点的形状设置为 record，默认的是圆角矩形 */
node [shape = record];

root [label = "left|middle|right"];
left [label = "one|two"];
right [label = "hello\nworld|{b|{c|d|e}|f}|g|h"];

root -> left;
root -> right;
}

#### 3.3 高级使用

Node, Edge and Graph Attributes（属性）：https://graphviz.gitlab.io/_pages/doc/info/attrs.html
Node Shapes（节点形状）：https://graphviz.gitlab.io/_pages/doc/info/shapes.html
Arrow Shapes（箭头形状）：https://graphviz.gitlab.io/_pages/doc/info/arrows.html

### 四、常见问题

image.png

1、编码问题。

2、对于文件格式的识别。

#### 4.2 中文问题

（1）文件修改

<dir>#WINDOWSFONTDIR#</dir>
<dir>~/.fonts</dir>


<dir>C:\WINDOWS\Fonts</dir>
<dir>~/.fonts</dir>


（2）文本设置
2.1 文本保存的编码为"utf-8"
2.2 文本中的字体设置

fontname="Microsoft YaHei"
edge [fontname="Microsoft YaHei"];
node [fontname="Microsoft YaHei"];

digraph gvDemo3 {
edge[fontname="Microsoft YaHei"]
node[fontname="Microsoft YaHei"]
graph[fontname="Microsoft YaHei"]

}

黑体：SimHei



2.3 dot文件中的中文前后加空格

#### Java中使用

//GraphViz.java - a simple API to call dot from Java programs
/*$Id$*/
/*
******************************************************************************
*                                                                            *
*              (c) Copyright 2003 Laszlo Szathmary                           *
*                                                                            *
* This program is free software; you can redistribute it and/or modify it    *
* under the terms of the GNU Lesser General Public License as published by   *
* the Free Software Foundation; either version 2.1 of the License, or        *
* (at your option) any later version.                                        *
*                                                                            *
* This program is distributed in the hope that it will be useful, but        *
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
* or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public    *
* License for more details.                                                  *
*                                                                            *
* You should have received a copy of the GNU Lesser General Public License   *
* along with this program; if not, write to the Free Software Foundation,    *
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.                              *
*                                                                            *
******************************************************************************
*/

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.InputStreamReader;

/**
* <dl>
* <dt>Purpose: GraphViz Java API
* <dd>
*
* <dt>Description:
* <dd> With this Java class you can simply call dot
*      from your Java programs
* <dt>Example usage:
* <dd>
* <pre>
*    GraphViz gv = new GraphViz();
*    gv.addln(gv.start_graph());
*    gv.addln("A -> B;");
*    gv.addln("A -> C;");
*    gv.addln(gv.end_graph());
*    System.out.println(gv.getDotSource());
*
*    String type = "gif";
*    File out = new File("out." + type);   // out.gif in this example
*    gv.writeGraphToFile( gv.getGraph( gv.getDotSource(), type ), out );
* </pre>
* </dd>
*
* </dl>
*
* @version v0.4, 2011/02/05 (February) -- Patch of Keheliya Gallaba is added. Now you
* can specify the type of the output file: gif, dot, fig, pdf, ps, svg, png, etc.
* @version v0.3, 2010/11/29 (November) -- Windows support + ability
* to read the graph from a text file
* @version v0.2, 2010/07/22 (July) -- bug fix
* @version v0.1, 2003/12/04 (December) -- first release
* @author  Laszlo Szathmary (<a href="jabba.laci@gmail.com">jabba.laci@gmail.com</a>)
*/
public class Graphviz
{
/**
* The dir. where temporary files will be created.
*/
//private static String TEMP_DIR = "/tmp"; // Linux
private static String TEMP_DIR = "c:/temp"; // Windows

/**
* Where is your dot program located? It will be called externally.
*/
// private static String DOT = "/usr/bin/dot"; // Linux
private static String DOT = "C:\\Program Files (x86)\\Graphviz2.38\\bin\\dot.exe"; // Windows

/**
* The source of the graph written in dot language.
*/
private StringBuilder graph = new StringBuilder();

/**
* Constructor: creates a new GraphViz object that will contain
* a graph.
*/
public Graphviz() {
}

/**
* Returns the graph's source description in dot language.
* @return Source of the graph in dot language.
*/
public String getDotSource() {
return graph.toString();
}

/**
* Adds a string to the graph's source (without newline).
*/
public void add(String line) {
graph.append(line);
}

/**
* Adds a string to the graph's source (with newline).
*/
public void addln(String line) {
graph.append(line + "\n");
}

/**
* Adds a newline to the graph's source.
*/
public void addln() {
graph.append('\n');
}

/**
* Returns the graph as an image in binary format.
* @param dot_source Source of the graph to be drawn.
* @param type Type of the output image to be produced, e.g.: gif, dot, fig, pdf, ps, svg, png.
* @return A byte array containing the image of the graph.
*/
public byte[] getGraph(String dot_source, String type)
{
File dot;
byte[] img_stream = null;

try {
dot = writeDotSourceToFile(dot_source);
if (dot != null)
{
img_stream = get_img_stream(dot, type);
if (dot.delete() == false)
System.err.println("Warning: " + dot.getAbsolutePath() + " could not be deleted!");
return img_stream;
}
return null;
} catch (java.io.IOException ioe) { return null; }
}

/**
* Writes the graph's image in a file.
* @param img   A byte array containing the image of the graph.
* @param file  Name of the file to where we want to write.
* @return Success: 1, Failure: -1
*/
public int writeGraphToFile(byte[] img, String file)
{
File to = new File(file);
return writeGraphToFile(img, to);
}

/**
* Writes the graph's image in a file.
* @param img   A byte array containing the image of the graph.
* @param to    A File object to where we want to write.
* @return Success: 1, Failure: -1
*/
public int writeGraphToFile(byte[] img, File to)
{
try {
FileOutputStream fos = new FileOutputStream(to);
fos.write(img);
fos.close();
} catch (java.io.IOException ioe) { ioe.printStackTrace();return -1; }
return 1;
}

/**
* It will call the external dot program, and return the image in
* binary format.
* @param dot Source of the graph (in dot language).
* @param type Type of the output image to be produced, e.g.: gif, dot, fig, pdf, ps, svg, png.
* @return The image of the graph in .gif format.
*/
private byte[] get_img_stream(File dot, String type)
{
File img;
byte[] img_stream = null;

try {
img = File.createTempFile("graph_", "."+type, new File(Graphviz.TEMP_DIR));
Runtime rt = Runtime.getRuntime();

// patch by Mike Chenault
String[] args = {DOT, "-T"+type, dot.getAbsolutePath(), "-o", img.getAbsolutePath()};
Process p = rt.exec(args);

p.waitFor();

FileInputStream in = new FileInputStream(img.getAbsolutePath());
img_stream = new byte[in.available()];
in.read(img_stream);
// Close it if we need to
if( in != null ) in.close();

if (img.delete() == false)
System.err.println("Warning: " + img.getAbsolutePath() + " could not be deleted!");
}
catch (java.io.IOException ioe) {
System.err.println("Error:    in I/O processing of tempfile in dir " + Graphviz.TEMP_DIR+"\n");
System.err.println("       or in calling external command");
ioe.printStackTrace();
}
catch (java.lang.InterruptedException ie) {
System.err.println("Error: the execution of the external program was interrupted");
ie.printStackTrace();
}

return img_stream;   }
/**
* Writes the source of the graph in a file, and returns the written file
* as a File object.
* @param str Source of the graph (in dot language).
* @return The file (as a File object) that contains the source of the graph.
*/
public File writeDotSourceToFile(String str) throws java.io.IOException
{
File temp;
try {
temp = File.createTempFile("graph_", ".dot.tmp", new File(Graphviz.TEMP_DIR));
FileWriter fout = new FileWriter(temp);
fout.write(str);
fout.close();
}
catch (Exception e) {
System.err.println("Error: I/O error while writing the dot source to temp file!");
return null;
}
return temp;
}

/**
* Returns a string that is used to start a graph.
* @return A string to open a graph.
*/
public String start_graph() {
return "digraph G {" ;
}

/**
* Returns a string that is used to end a graph.
* @return A string to close a graph.
*/
public String end_graph() {
return "}";
}

/**
* Read a DOT graph from a text file.
*
* @param input Input text file containing the DOT graph
* source.
*/
public void readSource(String input)
{
StringBuilder sb = new StringBuilder();

try
{
FileInputStream fis = new FileInputStream(input);
DataInputStream dis = new DataInputStream(fis);
BufferedReader br = new BufferedReader(new InputStreamReader(dis));
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
}
dis.close();
}
catch (Exception e) {
System.err.println("Error: " + e.getMessage());
}

this.graph = sb;
}

} // end of class GraphViz
import java.io.File;

public class GTest {
public static void main(String[] args){
GTest gtest = new GTest();
String[] nodes = {"A","B","C","D","E","F","G"};
String[] preline = {"B -> A","D -> B","E -> D","C -> E","G -> C","F -> G"};
gtest.start(nodes, preline);
}
private void start(String[] nodes,String[] preline){

Graphviz gv = new Graphviz();
//定义每个节点的style
String nodesty = "[shape = polygon, sides = 6, peripheries = 2, color = lightblue, style = filled]";
//String linesty = "[dir=\"none\"]";

gv.addln(gv.start_graph());//SATRT
gv.addln("edge[fontname=\"DFKai-SB\" fontsize=15 fontcolor=\"black\" color=\"brown\" style=\"filled\"]");
gv.addln("size =\"8,8\";");
//设置节点的style
for(int i=0;i<nodes.length;i++){
gv.addln(nodes[i]+" "+nodesty);
}
for(int i=0;i<preline.length;i++){
gv.addln(preline[i]+" "+" [dir=\"none\"]");
}
gv.addln(gv.end_graph());//END
//节点之间的连接关系输出到控制台
System.out.println(gv.getDotSource());
//输出什么格式的图片(gif,dot,fig,pdf,ps,svg,png,plain)
String type = "png";
//输出到文件夹以及命名
File out = new File("C:/Users/fanghui/Desktop/GraphTest/test." + type);   // Linux
//File out = new File("c:/eclipse.ws/graphviz-java-api/out." + type);    // Windows
gv.writeGraphToFile( gv.getGraph( gv.getDotSource(), type ), out );
}
}

https://www.jianshu.com/p/6d9bbbbf38b1

https://www.cnblogs.com/fanghuiplus/p/10072937.html

• 5
点赞
• 24
收藏
觉得还不错? 一键收藏
• 打赏
• 2
评论
05-24 792
12-03 1万+
12-25 7653
03-12 1万+
08-04 5676
09-29 403
06-22 506
01-07 2417

¥1 ¥2 ¥4 ¥6 ¥10 ¥20

1.余额是钱包充值的虚拟货币，按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载，可以购买VIP、付费专栏及课程。