【问题标题】:Need to control pydot edge length需要控制pydot边长
【发布时间】:2014-01-08 22:14:31
【问题描述】:

有没有办法使用 pydot 生成一个看起来像“普通树图”的有向树图,其中相同深度的每个节点都显示在相同的深度,并且每条边都处于某个角度(不是笔直向下)?

当我使用这些默认值时:

graph = pydot.Dot(graph_type='digraph', nodesep=.75)
graph.set_node_defaults(style="filled", fillcolor="grey")
graph.set_edge_defaults(color="blue", arrowhead="vee", weight="0")

节点 2 和 1 之间的边的长度太长。见:

我可以使用“1”的边权重来校正节点的长度,例如:

graph.set_edge_defaults(color="blue", arrowhead="vee", weight="1")

但现在两条边在 2 和 3 以及 7 和 6 之间直接指向下方。请参阅:

笔直指向下方的边缘被证明是一个真正的问题。如果左右兄弟都没有绘制,那么就不清楚是哪个节点(左还是右)被绘制了。

PS:在我之前关于同一个项目的问题中,我询问了如何控制边缘的角度,以便 pydot 生成的树形图可以避免边缘直接指向下方。这个问题被“盖伊”很好地回答了。在使用不可见节点实施 Guy 的解决方案后,出现了一个新的副作用:某些图上的某些边的长度过长。以下是上一个问题的链接供参考:Is there a way to control line angle when producing graphs with pyDot

【问题讨论】:

  • 不是重复的。我之前的问题是关于以一定角度而不是笔直向下绘制边缘。现在的新问题是如何管理边长。
  • 感谢您的编辑PK。对于这个问题,我确实有一个可以在 graphviz 中手动工作的答案。我将在几天内使用 pydot 和 python 的示例代码发布我的答案。

标签: python binary-tree graphviz pydot


【解决方案1】:

要正确控制 pydot 有向图的节点位置和边长,以便在指向同一方向时边相互平行,没有边指向直下,并且所有节点都绘制到适当的水平 - 必须使用不可见节点。

在之前关于“如何控制线条角度...”这一主题的 SO Q/A 会话中,我了解到不可见节点用于填充缺失的叶节点。这适用于某些树木,但有一些副作用。

现在,我学会了为每个节点添加第三个不可见节点,同时具有左右节点。请注意,不可见节点不会添加到树中,而只是被绘制到图中。所以树仍然可以用于搜索、插入、删除等方法。

这是一个演示解决方案的graphviz示例:

digraph {
  nodesep=0.35
  ordering=out
  node[style="filled", fillcolor="grey"]
  edge[color="blue", arrowhead="vee"]
  { node[shape=point style=invis] m5 m2 m8 m6 m4 }

  5 ->  2
  5 ->  m5 [weight=100 style=invis]
  5 ->  8
  2 -> 1
  2 -> m2 [weight=100 style=invis]
  2 -> 4
  8 -> 6
  8 -> m8 [weight=100 style=invis]
  4 -> 3
  4 -> m4 [weight=100 style=invis]
  6 -> m6 [weight=100 style=invis]
  6 ->  7
}

这是我更新的 python 代码的 sn-p,用于使用典型的树类自动执行此过程:

    vT = visualizeTree(0, fileDir, 'bst_graph','.png',1) # instantiate the visualizeTree Object
    graph = pydot.Dot(graph_type='digraph', nodesep=.5, pad=.3, size="19.2, 10.1")
    graph.set_node_defaults(style="filled", fillcolor="grey")
    graph.set_edge_defaults(color="blue", arrowhead="vee")
    vT.searchTree(root, sketchTree)
    vT.updateGraph()

class visualizeTree(object):
    # more code and comments located at project home
    def __init__(self, fileCount, fileDir, fileName, fileExt, vidFrames):

    def sketchTree(node, stack, find=None, draw=None):
        if node.getLeftBranch():
            draw(str(node), str(node.getLeftBranch()))
            stack.append(node.getLeftBranch()) 
            if node.getRightBranch():
                # insert invisible third node in-between left and right nodes
                draw(str(node), ":"+str(node), style_type="invisible")
        elif node.getRightBranch():
            # draw any missing left branches as invisible nodes/edges with dummy unique labels 
            draw(str(node), ":"+str(node), style_type="invisible")
        if node.getRightBranch():
            draw(str(node), str(node.getRightBranch()))
            stack.append(node.getRightBranch())      
        elif node.getLeftBranch():
            # draw any missing right branches as invisible nodes/edges with dummy unique labels 
            draw(str(node), ";"+str(node), style_type="invisible")

    def draw(self, parent_name, child_name, fill_color="grey", style_type='filled'):                  
        if style_type=="invisible":
            # save original edge defaults
            weight_ = "100"
            saveEdgeDefaults = graph.get_edge_defaults()[0]
            graph.set_edge_defaults(style=style_type, color="white", arrowhead="none") 
        else:
            weight_ = "3"
        edge = pydot.Edge(parent_name, child_name, style=style_type, weight=weight_)
        graph.add_edge(edge)  
        if style_type=="invisible":
            graph.set_edge_defaults(**saveEdgeDefaults)        
        if not self.nodeNames:
            self.nodeNames[parent_name] = pydot.Node(parent_name, label=parent_name, fillcolor=fill_color, style=style_type)
            graph.add_node(self.nodeNames[parent_name]) 
        if (parent_name not in self.nodeNames):    
            self.nodeNames[parent_name] = pydot.Node(parent_name, label=parent_name, fillcolor=fill_color, style=style_type)
            graph.add_node(self.nodeNames[parent_name])
        if child_name not in self.nodeNames:
            self.nodeNames[child_name] = pydot.Node(child_name, label=child_name, fillcolor=fill_color, style=style_type)
            graph.add_node(self.nodeNames[child_name])

可以在项目主页找到完整的源代码: http://www.embeddedcomponents.com/blogs/2013/12/visualizing-software-tree-structures/

现在可以按照我的意愿绘制典型的小图像:

即使是大树也可以用相似的几何图形绘制:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-08-24
    • 2013-02-26
    • 2013-04-27
    • 1970-01-01
    • 2011-07-18
    • 2015-07-01
    • 2014-05-28
    • 1970-01-01
    相关资源
    最近更新 更多