Graphviz4S ---- 在Scala中使用DOT语言绘图的开源工具
2017-01-10 21:30
507 查看
前言
之前需要在Scala中用到类似python的graphviz库的功能,用来在Mxnet中可视化网络结构,但是在网上搜索了一下,没有找到好用的库,所以就自己去把python的graphviz库中的主要功能
用Scala实现了一下,尽量的保持接口和python库的一致,也方便从python移植相关的代码到
Scala,然后我把这个小项目开源了,地址是Graphviz4S,有兴趣的朋友可以去试用一下。
接下来我会结合代码,用几个例子来介绍如何使用这个小工具。
正文
接下来我会通过几个例子介绍Grapphviz4S,例子参考自这篇博客。1、简单例子
1.1、简单图例
首先来看第一个例子,Scala代码如下:import com.liangdp.graphviz4s.Graph val dot = new Graph(name = "graphname") dot.edges(Array( ("a", "b"), ("b", "c"), ("b", "d"), ("d", "a") )) println(dot.source()) // graph graphname { // a -- b // b -- c // b -- d // d -- a // } dot.render(fileName = "graphname.gv", directory = ".", view = true)
生成的结果如下:
1.2、简单图例2
第二个例子和上面的一样,但是布局不同,Scala代码如下:import com.liangdp.graphviz4s.Graph val dot = new Graph(name = "graphname") dot.body += "\t\trankdir=LR //Rank Direction Left to Right" dot.edges(Array( ("a", "b"), ("b", "c"), ("b", "d"), ("d", "a") )) println(dot.source()) // graph graphname { // rankdir=LR //Rank Direction Left to Right // a -- b // b -- c // b -- d // d -- a // } dot.render(fileName = "graphname.gv", directory = ".", view = true)
生成的结果如下:
1.3、简单有向图
第三个例子是一个简单的有向图,Scala代码如下:import com.liangdp.graphviz4s.Digraph val dot = new Digraph(name = "graphname") dot.edges(Array( ("a", "b"), ("b", "c"), ("a", "c") )) println(dot.source()) // digraph graphname { // a -> b // b -> c // a -> c // } dot.render(fileName = "graphname.gv", directory = ".", view = true)
生成的结果如下:
1.4、带标签的简单有向图
第四个例子给有向图的边加上标签,对应的Scala代码如下:import com.liangdp.graphviz4s.Digraph val dot = new Digraph(name = "graphname") dot.node(name = "T", label = "Teacher") dot.node(name = "P", label = "Pupil") import scala.collection.mutable.Map dot.edge(tailName = "T", headName = "P", label = "Instructions", attrs = Map("fontcolor" -> "darkgreen")) println(dot.source()) // digraph graphname { // "T" [label="Teacher" ] // "P" [label="Pupil" ] // T -> P [label="Instructions" fontcolor=darkgreen] // } dot.render(fileName = "graphname.gv", directory = ".", view = true)
生成的结果如下:
1.5、总结
Scala代码:import com.liangdp.graphviz4s.Digraph val dot = new Digraph(name = "summary") dot.node(name = "start", label = "Start with a Node") import scala.collection.mutable.Map dot.node(name = "next", label = "Choose your shape", attrs = Map("shape" -> "box")) dot.node(name = "warning", label = "Don't go overboard", attrs = Map("color" -> "Blue", "fontcolor" -> "Red", "fontsize" -> "24", "style" -> "filled", "fillcolor" -> "green", "shape" -> "octagon")) dot.node(name = "end", label = "Draw your graph!", attrs = Map("shape" -> "box", "style" -> "filled", "fillcolor" -> "yellow")) dot.edge(tailName = "start", headName = "next") dot.edge(tailName = "start", headName = "warning") dot.edge(tailName = "next", headName = "end", label = "Getting Better...") println(dot.source()) // digraph summary { // "start" [label="Start with a Node" ] // "next" [label="Choose your shape" shape=box] // "warning" [label="Don't go overboard" fontsize=24 color=Blue fillcolor=green shape=octagon fontcolor=Red style=filled] // "end" [label="Draw your graph!" fillcolor=yellow shape=box style=filled] // start -> next [] // start -> warning [] // next -> end [label="Getting Better..." ] // } dot.render(fileName = "graphname.gv", directory = ".", view = true)
生成的结果如下:
2、高级例子
2.1、少写一点代码
单独地去定义每一个节点的属性很浪费时间,下面这个技巧能够让你coding的速度快一点。Scala代码如下:
import com.liangdp.graphviz4s.Digraph val dot = new Digraph(name = "hierarchy") // increases the separation between nodes dot.body += "\tnodesep=1.0" import scala.collection.mutable.Map //All nodes will this shape and colour dot.attr("node", attrs = Map("color" -> "Red", "fontname" -> "Courier", "shape" -> "box")) //All the lines look like this dot.attr("edge", attrs = Map("color" -> "Blue", "style" -> "dashed")) dot.edges("Headteacher", Array("Deputy1", "Deputy2", "BusinessManager")) dot.edges("Deputy1", Array("Teacher1", "Teacher2")) dot.edge("BusinessManager", "ITManager") // Put them on the same level dot.body += "\t{rank=same;ITManager Teacher1 Teacher2}" println(dot.source()) // digraph hierarchy { // nodesep=1.0 // node [ fontname=Courier color=Red shape=box] // edge [ color=Blue style=dashed] // Headteacher -> Deputy1 // Headteacher -> Deputy2 // Headteacher -> BusinessManager // Deputy1 -> Teacher1 // Deputy1 -> Teacher2 // BusinessManager -> ITManager [] // {rank=same;ITManager Teacher1 Teacher2} // } dot.render(fileName = "graphname.gv", directory = ".", view = true)
结果如下:
2.2、html
Scala代码如下:import com.liangdp.graphviz4s.Digraph val dot = new Digraph(name = "structs") import scala.collection.mutable.Map dot.attr("node", attrs = Map("shape" -> "record")) dot.node("struct1", label = """"<f0> left|<f1> mid\ dle|<f2> right"""") dot.node("struct2", label = """"{<f0> one|<f1> two\n\n\n}"""", attrs = Map("shape" -> "Mrecord")) dot.node("struct3", label = """"hello\nworld |{ b |{c|<here> d|e}| f}| g | h"""") dot.edge("struct1:f1", "struct2:f0") dot.edge("struct1:f0", "struct3:here") println(dot.source()) // digraph structs { // node [ shape=record] // "struct1" [label="<f0> left|<f1> mid\ dle|<f2> right" ] // "struct2" [label="{<f0> one|<f1> two\n\n\n}" shape=Mrecord] // "struct3" [label="hello\nworld |{ b |{c|<here> d|e}| f}| g | h" ] // struct1:f1 -> struct2:f0 [] // struct1:f0 -> struct3:here [] // } dot.render(fileName = "graphname.gv", directory = ".", view = true)
结果如下:
2.3、有限状态机
Scala代码如下:import com.liangdp.graphviz4s.Digraph val dot = new Digraph(name = "structs") dot.body += "\trankdir=LR" dot.body += s"""${"\t"}size="8,5"""" import scala.collection.mutable.Map dot.attr("node", attrs = Map("shape" -> "circle")) dot.edge(tailName = "S0", headName = "S1", label = """"Lift Nozzle"""") dot.edge(tailName = "S1", headName = "S0", label = """"Replace Nozzle"""") dot.edge(tailName = "S1", headName = "S2", label = """"Authorize Pump"""") dot.edge(tailName = "S2", headName = "S0", label = """"Replace Nozzle"""") dot.edge(tailName = "S2", headName = "S3", label = """"Pull Trigger"""") dot.edge(tailName = "S3", headName = "S2", label = """"Release Trigger"""") println(dot.source()) // digraph structs { // rankdir=LR // size="8,5" // node [ shape=circle] // S0 -> S1 [label="Lift Nozzle" ] // S1 -> S0 [label="Replace Nozzle" ] // S1 -> S2 [label="Authorize Pump" ] // S2 -> S0 [label="Replace Nozzle" ] // S2 -> S3 [label="Pull Trigger" ] // S3 -> S2 [label="Release Trigger" ] // } dot.render(fileName = "graphname.gv", directory = ".", view = true)
结果如下:
2.4、数据流示意图
Scala代码如下:import com.liangdp.graphviz4s.Digraph val dot = new Digraph(name = "dfd") import scala.collection.mutable.Map dot.attr("node", attrs = Map("shape" -> "record")) dot.node("store1", label = """"<f0> left|<f1> Some data store"""") dot.node("proc1", label = """"{<f0> 1.0|<f1> Some process here\n\n\n}"""", attrs = Map("shape" -> "Mrecord")) dot.node("enti1", label = "Customer", attrs = Map("shape" -> "box")) dot.edge(tailName = "store1:f1", headName = "proc1:f0") dot.edge(tailName = "enti1", headName = "proc1:f0") println(dot.source()) // digraph dfd { // node [ shape=record] // "store1" [label="<f0> left|<f1> Some data store" ] // "proc1" [label="{<f0> 1.0|<f1> Some process here\n\n\n}" shape=Mrecord] // "enti1" [label="Customer" shape=box] // store1:f1 -> proc1:f0 [] // enti1 -> proc1:f0 [] // } dot.render(fileName = "graphname.gv", directory = ".", view = true)
结果如下:
2.5、数据流示意图2
Scala代码如下:import com.liangdp.graphviz4s.Digraph val dot = new Digraph(name = "dfd2") import scala.collection.mutable.Map dot.attr("node", attrs = Map("shape" -> "record")) val subgraph0 = new Digraph(name = "level0") subgraph0.node("enti1", label = "Customer", Map("shape" -> "box")) subgraph0.node("enti2", label = "Manager", Map("shape" -> "box")) val subgraph1 = new Digraph(name = "cluster_level1") subgraph1.body += s"""${"\t"}label ="Level 1"""" subgraph1.node("proc1", label = """"{<f0> 1.0|<f1> One process here\n\n\n}"""", attrs = Map("shape" -> "Mrecord")) subgraph1.node("proc2", label = """"{<f0> 2.0|<f1> Other process here\n\n\n}"""", attrs = Map("shape" -> "Mrecord")) subgraph1.node("store1", label = """"<f0> |<f1> Data store one"""") subgraph1.node("store2", label = """"<f0> |<f1> Data store two"""") subgraph1.body += "\t{rank=same; store1, store2}" dot.subGraph(subgraph0) dot.subGraph(subgraph1) dot.edge("enti1", "proc1") dot.edge("enti2", "proc2") dot.edge("store1", "proc1") dot.edge("store2", "proc2") dot.edge("proc1", "store2") dot.edge("store2", "proc1") println(dot.source()) // 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 [] // } dot.render(fileName = "graphname.gv", directory = ".", view = true)
结果如下:
2.6、对象继承
Scala代码如下:import com.liangdp.graphviz4s.Digraph val dot = new Digraph(name = "obj") import scala.collection.mutable.Map dot.attr("node", attrs = Map("shape" -> "record")) dot.body += s"""${"\t"}rankdir="BT"""" dot.node("teacher", label = """"{<f0> Teacher|<f1> \n |<f2> \n }"""") dot.node("course", label = """"{<f0> Course|<f1> \n |<f2> \n }"""") dot.node("student", label = """"{<f0> Student|<f1> \n |<f2> \n }"""") dot.node("lesson", label = """"{<f0> Lesson |<f1> \n |<f2> \n }"""") dot.node("tutorial", label = """"{<f0> Tutorial|<f1> \n |<f2> \n }"""") dot.node("assessment", label = """"{<f0> Assessment|<f1> \n |<f2> \n }"""") dot.node("coursework", label = """"{<f0> Coursework|<f1> \n |<f2> \n }"""") dot.node("exam", label = """"{<f0> Exam|<f1> \n |<f2> \n }"""") dot.body += "\t{rank=same; teacher course student}" dot.edge("teacher", "course", attrs = Map("dir" -> "forward", "arrowhead" -> "none", "arrowtail" -> "normal", "headlabel" -> """"1"""", "taillabel" -> """"1.."""")) dot.edge("student", "course", attrs = Map("dir" -> "forward", "arrowhead" -> "none", "arrowtail" -> "normal", "headlabel" -> """"1"""", "taillabel" -> """"1.."""")) dot.edge("lesson", "course", attrs = Map("dir" -> "forward", "arrowhead" -> "diamond", "arrowtail" -> "normal")) dot.edge("tutorial", "course", attrs = Map("dir" -> "forward", "arrowhead" -> "diamond", "arrowtail" -> "normal")) dot.edge("assessment", "course", attrs = Map("dir" -> "forward", "arrowhead" -> "diamond", "arrowtail" -> "normal")) dot.edge("coursework", "assessment") dot.edge("exam", "assessment") println(dot.source()) // digraph obj { // node [ shape=record] // rankdir="BT" // "teacher" [label="{<f0> Teacher|<f1> \n |<f2> \n }" ] // "course" [label="{<f0> Course|<f1> \n |<f2> \n }" ] // "student" [label="{<f0> Student|<f1> \n |<f2> \n }" ] // "lesson" [label="{<f0> Lesson |<f1> \n |<f2> \n }" ] // "tutorial" [label="{<f0> Tutorial|<f1> \n |<f2> \n }" ] // "assessment" [label="{<f0> Assessment|<f1> \n |<f2> \n }" ] // "coursework" [label="{<f0> Coursework|<f1> \n |<f2> \n }" ] // "exam" [label="{<f0> Exam|<f1> \n |<f2> \n }" ] // {rank=same; teacher course student} // teacher -> course [ arrowtail=normal dir=forward taillabel="1.." arrowhead=none headlabel="1"] // student -> course [ arrowtail=normal dir=forward taillabel="1.." arrowhead=none headlabel="1"] // lesson -> course [ arrowtail=normal dir=forward arrowhead=diamond] // tutorial -> course [ arrowtail=normal dir=forward arrowhead=diamond] // assessment -> course [ arrowtail=normal dir=forward arrowhead=diamond] // coursework -> assessment [] // exam -> assessment [] // } dot.render(fileName = "graphname.gv", directory = ".", view = true)
结果如下:
2.7、关系型实体
Scala代码如下:import com.liangdp.graphviz4s.Digraph val dot = new Digraph(name = "ER") import scala.collection.mutable.Map dot.attr("node", attrs = Map("shape" -> "box")) dot.node("Book") dot.node("Customer") dot.node("Loan") dot.body += "\t{rank=same;Book,Customer,Loan}" dot.edge("Book", "Loan", attrs = Map("dir" -> "forward", "arrowhead" -> "crow", "arrowtail" -> "normal")) dot.edge("Customer", "Loan", attrs = Map("dir" -> "forward", "arrowhead" -> "crow", "arrowtail" -> "normal")) println(dot.source()) // digraph ER { // node [ shape=box] // "Book" [] // "Customer" [] // "Loan" [] // {rank=same;Book,Customer,Loan} // Book -> Loan [ arrowtail=normal dir=forward arrowhead=crow] // Customer -> Loan [ arrowtail=normal dir=forward arrowhead=crow] // } dot.render(fileName = "graphname.gv", directory = ".", view = true)
结果:
结尾
通过以上例子的介绍,相信读者都能够了解如何使用这个小工具了,不过这个小工具还有很多需要完善的地方,也欢迎感兴趣的朋友一起来完善它。
相关文章推荐
- 绘图利器 — Graphviz 由AT&T实验室启动的开源工具包 — DOT 图形描述语言
- xlsxtools游戏配置表工具,开源跨平台,支持多语言,Unity3D等游戏引擎使用Excel做游戏配置表的解决方案
- 使用DOT语言和Graphviz绘图(翻译)
- android开发环境配置- 使用开源工具链
- 如何确保安全使用LAMP开源开发工具
- 使用xml语言自动导入测试需求到开源测试工具testlink
- 使用 Java 开源工具建立一个灵活的搜索引擎
- StatCVS 对使用CVS的项目进行深入统计的开源工具
- Qt语言翻译工具的使用(续)
- XtraBackup - 开源MySQL在线热备份工具安装使用
- 在.NET平台上使用Scala语言(上):初尝
- 也谈在 .NET 平台上使用 Scala 语言(下)
- 使用开源的.NET工具构造一个复杂的 自动构建 环境
- 也谈在 .NET 平台上使用 Scala 语言(续)
- 对比一下微软公司与开源软件社区中,开发测试管理人员使用的工具
- 在.NET平台上使用Scala语言(下):分析
- FCKeditor 一个开源的web页面文本编辑工具,在vs 2005中的使用方法
- 工欲善,必先利其器 -- (使用开源工具提高J2EE系统的质量)
- 开源绘图工具EasyDiagram
- 也谈在 .NET 平台上使用 Scala 语言(中)