您的位置:首页 > 编程语言 > Ruby

ruby-graphviz 入门

2009-05-26 14:44 197 查看
自从写了一个关于Graphviz 的教程
以来,我一直觉得它使用起来还不够方便。最近终于找到Graphviz 的Ruby 扩展了——ruby-graphviz


首先你得安装Graphviz
,记得装在默认目录下,不然ruby-graphviz 找不到(我还不知道怎么设置PATH,请指教)。

然后,gem 一下:

gem install ruby-graphviz


接下来将见证神奇的一刻。

require 'graphviz'  # this loads the ruby-graphviz gem
# initialize new Graphviz graph
g = GraphViz::new( "hello_world", "type" => "graph")
hello = g.add_node( "hello" )
world = g.add_node( "world" )
g.add_edge( hello, world )
g.output( "output" => "png", :file => "hello_world.png" )


Run 一下,你会在源文件所在目录下发现一个hello_world.png 文件。


老规矩,下面来解释一下。

可以用new 创建一个graph 对象。new 的时候有这么几个选项:

:output : Output format (Constants::FORMATS) (default : dot)

:file : Output file name (default : none)

:use : Program to use (Constants::PROGRAMS) (default : dot)

:path : Program PATH

:parent : Parent graph (default : none)

:type : Graph type (Constants::GRAPHTYPE) (default : digraph)

用output (有个别名save)生成图像。同样,output 也有几个可选项:

:output : Output format (Constants::FORMATS)

:file : Output file name

:use : Program to use (Constants::PROGRAMS)

:path : Program PATH



用add_node 来加点。可以像这样来设置属性:

n = g.add_node( "node" )
n.set { |_n|
_n.shape = "box"
_n.style = "filled"
_n.color = ".7 .3 1.0"
}


这样,n 将会是一个用<.7, .3, 1.0> 填充的box。

用node_count 来对点计数。

用[]= 来设置属性值:

g.node["shape"] = "ellipse"
g.node["sides"] = "4"
g.node["peripheries"] = ""
g.node["color"] = "black"
g.node["style"] = ""
g.node["skew"] = "0.0"
g.node["distortion"] = "0.0"





用add_edge 加边。

用edge_count 来对边计数。

也可以像点那样设置属性:

e = g.add_edge( n1, n2, "label" => "an edge" )
e.set { |_e|
_e.color = "blue"
_e.fontcolor = "red"
}


这样,e 将成为一条带红色标注“an edge” 的蓝线。

Record Nodes

记录是一个盒子,被分割成横的或竖的格子。格子之前可以加标记,只需把标记放在尖括号<> 之间。横格子之间用竖杠| 隔开,竖格子就是外加一对大括号{}。看下面的例子:



require "graphviz"
g = GraphViz::new( "G" )
g.node["shape"] = "record"
struct1 = g.add_node( "struct1", "shape" => "record", "label" => "<f0> left|<f1> mid/ dle|<f2> right" )
struct2 = g.add_node( "struct2", "shape" => "record", "label" => "<f0> one|<f1> two" )
struct3 = g.add_node( "struct3", "shape" => "record", "label" => 'hello/nworld |{ b |{c|<here> d|e}| f}| g | h' )
g.add_edge( struct1, struct2 )
g.add_edge( struct1, struct3 )
g.output( :output => "png" , :file => "structs.png" )
再来看一个复杂一点的例子:

require "graphviz"
GraphViz::options( :output => "png", :use => "dot" )
g = GraphViz::new( "structs", :output => "png", :rankdir => "LR" )
g.node["shape"] = "record"
node0 = g.add_node( "node0", :label => "<f0> 0x10ba8| <f1>" )
node1 = g.add_node( "node1", :label => "<f0> 0xf7fc4380| <f1> | <f2> |-1" )
node2 = g.add_node( "node2", :label => "<f0> 0xf7fc44b8| | |2" )
node3 = g.add_node( "node3", :label => "<f0> 3.43322790286038071e-06|44.79998779296875|0" )
node4 = g.add_node( "node4", :label => "<f0> 0xf7fc4380| <f1> | <f2> |2" )
node5 = g.add_node( "node5", :label => "<f0> (nil)| | |-1" )
node6 = g.add_node( "node6", :label => "<f0> 0xf7fc4380| <f1> | <f2> |1" )
node7 = g.add_node( "node7", :label => "<f0> 0xf7fc4380| <f1> | <f2> |2" )
node8 = g.add_node( "node8", :label => "<f0> (nil)| | |-1" )
node9 = g.add_node( "node9", :label => "<f0> (nil)| | |-1" )
node10 = g.add_node( "node10", :label => "<f0> (nil)| <f1> | <f2> |-1" )
node11 = g.add_node( "node11", :label => "<f0> (nil)| <f1> | <f2> |-1" )
node12 = g.add_node( "node12", :label => "<f0> 0xf7fc43e0| | |1" )

g.add_edge( "node0:f0", "node1:f0" )
g.add_edge( "node0:f1", "node2:f0" )
g.add_edge( "node1:f0", "node3:f0" )
g.add_edge( "node1:f1", "node4:f0" )
g.add_edge( "node1:f2", "node5:f0" )
g.add_edge( "node4:f0", "node3:f0" )
g.add_edge( "node4:f1", "node6:f0" )
g.add_edge( "node4:f2", "node10:f0" )
g.add_edge( "node6:f0", "node3:f0" )
g.add_edge( "node6:f1", "node7:f0" )
g.add_edge( "node6:f2", "node9:f0" )
g.add_edge( "node7:f0", "node3:f0" )
g.add_edge( "node7:f1", "node1:f0" )
g.add_edge( "node7:f2", "node8:f0" )
g.add_edge( "node10:f1", "node11:f0" )
g.add_edge( "node10:f2", "node12:f0" )
g.add_edge( "node11:f2", "node1:f0" )
g.output( :file => "complicated_sample.png" )




子图

代码说明一切(注意add_graph):

require "graphviz"
graph = GraphViz::new( "G", "output" => "png" )
graph["compound"] = "true"
graph.edge["lhead"] = ""
graph.edge["ltail"] = ""
c0 = graph.add_graph( "cluster0" )
a = c0.add_node( "a" )
b = c0.add_node( "b" )
c = c0.add_node( "c" )
d = c0.add_node( "d" )
c0.add_edge( a, b )
c0.add_edge( a, c )
c0.add_edge( b, d )
c0.add_edge( c, d )
c1 = graph.add_graph( "cluster1" )
e = c1.add_node( "e" )
f = c1.add_node( "f" )
g = c1.add_node( "g" )
c1.add_edge( e, g )
c1.add_edge( e, f )
h = graph.add_node( "h" )
graph.add_edge( b, f, "lhead" => "cluster1" )
graph.add_edge( d, e )
graph.add_edge( c, g, "ltail" => "cluster0", "lhead" => "cluster1" )
graph.add_edge( c, e, "ltail" => "cluster0" )
graph.add_edge( d, h )
graph.output( :file => "subgraph.png" )






高级技巧

同一个图的三种不同写法:

require "graphviz"
g = GraphViz::new( "G", "output" => "png" )
g.node["shape"] = "ellipse"
g.node["color"] = "black"
g["color"] = "black"
c0 = g.add_graph( "cluster0" )
c0["label"] = "process #1"
c0["style"] = "filled"
c0["color"] = "lightgrey"
a0 = c0.add_node( "a0", "style" => "filled", "color" => "white" )
a1 = c0.add_node( "a1", "style" => "filled", "color" => "white" )
a2 = c0.add_node( "a2", "style" => "filled", "color" => "white" )
a3 = c0.add_node( "a3", "style" => "filled", "color" => "white" )
c0.add_edge( a0, a1 )
c0.add_edge( a1, a2 )
c0.add_edge( a2, a3 )
c1 = g.add_graph( "cluster1", "label" => "process #2" )
b0 = c1.add_node( "b0", "style" => "filled", "color" => "blue" )
b1 = c1.add_node( "b1", "style" => "filled", "color" => "blue" )
b2 = c1.add_node( "b2", "style" => "filled", "color" => "blue" )
b3 = c1.add_node( "b3", "style" => "filled", "color" => "blue" )
c1.add_edge( b0, b1 )
c1.add_edge( b1, b2 )
c1.add_edge( b2, b3 )
start = g.add_node( "start", "shape" => "Mdiamond" )
endn  = g.add_node( "end",   "shape" => "Msquare" )
g.add_edge( start, a0 )
g.add_edge( start, b0 )
g.add_edge( a1, b3 )
g.add_edge( b2, a3 )
g.add_edge( a3, a0 )
g.add_edge( a3, endn )
g.add_edge( b3, endn )
g.output( :file => "the_same_sample1.png" )


Rubyist 更喜欢的风格:

require "graphviz"
g = GraphViz::new( "G", "output" => "png" )
g.node[:shape] = "ellipse"
g.node[:color] = "black"
g[:color] = "black"
g.cluster0( ) do |cluster|
cluster[:label] = "process #1"
cluster[:style] = "filled"
cluster[:color] = "lightgrey"

cluster.a0 :style => "filled", :color => "white"
cluster.a1 :style => "filled", :color => "white"
cluster.a2 :style => "filled", :color => "white"
cluster.a3 :style => "filled", :color => "white"

cluster.a0 << cluster.a1
cluster.a1 << cluster.a2
cluster.a2 << cluster.a3
end
g.cluster1( :label => "process #2" ) do |cluster|
cluster.b0 :style => "filled", :color => "blue"
cluster.b1 :style => "filled", :color => "blue"
cluster.b2 :style => "filled", :color => "blue"
cluster.b3 :style => "filled", :color => "blue"

cluster.b0 << cluster.b1
cluster.b1 << cluster.b2
cluster.b2 << cluster.b3
end
g.start :shape => "Mdiamond"
g.endn :shape => "Msquare", :label => "end"
g.start << g.cluster0.a0
g.start << g.cluster1.b0
g.cluster0.a1 << g.cluster1.b3
g.cluster1.b2 << g.cluster0.a3
g.cluster0.a3 << g.cluster0.a0
g.cluster0.a3 << g.endn
g.cluster1.b3 << g.endn
g.output( :file => "the_same_sample2.png" )


Ruby geek 的写法:

require "graphviz"
GraphViz::new( "G", "output" => "png" ) { |graph|
graph.node[:shape] = "ellipse"
graph.node[:color] = "black"

graph[:color] = "black"

graph.cluster0( ) do |cluster|
cluster[:label] = "process #1"
cluster[:style] = "filled"
cluster[:color] = "lightgrey"

cluster.a0 :style => "filled", :color => "white"
cluster.a1 :style => "filled", :color => "white"
cluster.a2 :style => "filled", :color => "white"
cluster.a3 :style => "filled", :color => "white"

cluster.a0 << cluster.a1
cluster.a1 << cluster.a2
cluster.a2 << cluster.a3
end

graph.cluster1( :label => "process #2" ) do |cluster|
cluster.b0 :style => "filled", :color => "blue"
cluster.b1 :style => "filled", :color => "blue"
cluster.b2 :style => "filled", :color => "blue"
cluster.b3 :style => "filled", :color => "blue"

cluster.b0 << cluster.b1
cluster.b1 << cluster.b2
cluster.b2 << cluster.b3
end

graph.start :shape => "Mdiamond"
graph.endn :shape => "Msquare", :label => "end"

graph.start << graph.cluster0.a0
graph.start << graph.cluster1.b0
graph.cluster0.a1 << graph.cluster1.b3
graph.cluster1.b2 << graph.cluster0.a3
graph.cluster0.a3 << graph.cluster0.a0
graph.cluster0.a3 << graph.endn
graph.cluster1.b3 << graph.endn
}.output( :path => 'C:/Program Files/Graphviz2.20/bin', :file => "the_same_sample3.png" )




小结

ruby-graphviz 和graphviz 的原生DOT 文件之间存在一一对应的关系。一个典型的graphviz DOT 文件有三个主要部分:

数据头,用来设置点、边、图的属性

点定义

边定义

所以ruby-graphviz 文件也是由这三部分组成。我们用一个对比例子来结束本教程。

这个graphviz 例子来自Mark A. McBride

digraph graph_example {
/***** GLOBAL SETTINGS *****/
graph          [rotate=0, rankdir="LR"]
node           [color="#333333", style=filled,
shape=box, fontname="Trebuchet MS"]
edge           [color="#666666", arrowhead="open",
fontname="Trebuchet MS", fontsize="11"]
node           [fillcolor="#294b76", fontcolor="white"]

/***** Nodes *****/
a              [label="Node A"]
b              [label="Node B"]
c              [label="Node C"]
d              [label="Node D", fillcolor="#116611"]
/***** Edges *****/
a              -> b
b              -> c
c              -> d
b              -> d
a              -> d [label="direct path"]
}


我用Ruby 改写了这个例子:

require "graphviz"
g = GraphViz::new( "G", "output" => "png" )
# ***** GLOBAL SETTINGS *****
g[:rotate] = "0"
g[:rankdir] = "LR"
g.node[:color] = "#333333"
g.node[:style] = "filled"
g.node[:shape] = "box"
g.node[:fontname] = "Trebuchet MS"
g.node[:fillcolor] = "#294b76"
g.node[:fontcolor] = "white"
g.edge[:color] = "#666666"
g.edge[:arrowhead] = "open"
g.edge[:fontname] = "Trebuchet MS"
g.edge[:fontsize] = "11"
# ***** Nodes *****
a = g.add_node( "a", :label => "Node A" )
b = g.add_node( "b", :label => "Node B" )
c = g.add_node( "c", :label => "Node C" )
d = g.add_node( "d", :label => "Node D", :fillcolor => "#116611" )
# ***** Edges *****
g.add_edge( a, b )
g.add_edge( b, c )
g.add_edge( c, d )
g.add_edge( b, d )
g.add_edge( a, d, :label => "direct path" )
g.output( :file => "f:/a.png" )




要想了解更多,请看官方文档
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: