使用DOT图作为树GUI的基础

时间:2013-07-24 17:19:10

标签: python wxpython dot pygraphviz pydot

我想使用由DOT(python中的pyDot)生成的图形作为交互式树形结构GUI的基础,其中树中的每个节点都可以是小部件。

树基本上是一个二进制莫尔斯代码树,它从顶部节点开始,向下导航到所需的字母并选择它。他们想要选择的节点应该是可突出显示的,并且应该能够根据用户输入更改内容(字母)。

基本上我希望将节点转换为满量程对象,其可调参数随着接口的使用而变化。为了做到这一点,有人能指出我正确的方向吗?

1 个答案:

答案 0 :(得分:0)

我开始使用演示代码:http://wiki.wxpython.org/AnotherTutorial#wx.TreeCtrl。我添加了build_tree,_build_tree_helper和build_conn_dict方法。 dot_parser库中感兴趣的关键方法是edge.get_source()和edge.get_destination(),它们用于创建“连接”字典。

点图存储在dot_data变量中。重要的是,点图不得循环;也就是说,它必须是生成树,否则_build_tree_helper方法将无限循环(并且在TreeControl中没有意义)。

我还必须根据https://github.com/nlhepler/pydot-py3/issues/1#issuecomment-15999052修补dot_parser才能使其正常工作。

import wx

from dot_parser import parse_dot_data

class MyFrame(wx.Frame):
    def __init__(self, parent, id, title, **kwargs):
        self.parsed_dot = kwargs.pop("parsed_dot", None)
        wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(450, 350))

        hbox = wx.BoxSizer(wx.HORIZONTAL)
        vbox = wx.BoxSizer(wx.VERTICAL)
        panel1 = wx.Panel(self, -1)
        panel2 = wx.Panel(self, -1)

        self.tree = wx.TreeCtrl(panel1, 1, wx.DefaultPosition, (-1,-1), wx.TR_HAS_BUTTONS | wx.TR_LINES_AT_ROOT )
        self.build_tree(self.tree)
        self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, id=1)
        self.display = wx.StaticText(panel2, -1, "",(10,10), style=wx.ALIGN_CENTRE)
        vbox.Add(self.tree, 1, wx.EXPAND)
        hbox.Add(panel1, 1, wx.EXPAND)
        hbox.Add(panel2, 1, wx.EXPAND)
        panel1.SetSizer(vbox)
        self.SetSizer(hbox)
        self.Centre()

    def build_conn_dict(self):
        conn_dict = {}
        if(self.parsed_dot):
            for edge in self.parsed_dot.get_edges():
                conn_dict.setdefault(edge.get_source(), []).append(edge.get_destination())
        return conn_dict

    def build_tree(self, tree):
        if(self.parsed_dot):
            conn_dict = self.build_conn_dict()
            outs = set(conn_dict.keys())
            ins = reduce(lambda x, y: x | set(y), conn_dict.values(), set([]))
            roots = list(outs - ins)
            roots = dict([(root, tree.AddRoot(root)) for root in roots])
            self._build_tree_helper(tree, conn_dict, roots)

    def _build_tree_helper(self, tree, conn_dict = {}, roots = {}):
        new_roots = {}
        for root in roots:
            if(conn_dict.has_key(root)):
                for sub_root in conn_dict[root]:
                    new_roots[sub_root] = tree.AppendItem(roots[root], sub_root)
        if(new_roots):
            self._build_tree_helper(tree, conn_dict, new_roots)

    def OnSelChanged(self, event):
        item =  event.GetItem()
        self.display.SetLabel(self.tree.GetItemText(item))
        child_text = self.tree.GetItemText(item)
        parent_text = ""
        try:
            parent = self.tree.GetItemParent(item)
            parent_text = self.tree.GetItemText(parent)
        except wx._core.PyAssertionError:
            pass
        print "child: %s, parent: %s" % (child_text, parent_text)

class MyApp(wx.App):
    def OnInit(self):
        dot_data = \
        '''
        graph ""
        {
                label="(% (EXP (% (X) (% (X) (X)))) (EXP (SIN (X))))"
                n039 ;
                n039 [label="%"] ;
                n039 -> n040 ;
                n040 [label="EXP"] ;
                n040 -> n041 ;
                n041 [label="%"] ;
                n041 -> n042 ;
                n042 [label="X"] ;
                n041 -> n043 ;
                n043 [label="%"] ;
                n043 -> n044 ;
                n044 [label="X"] ;
                n043 -> n045 ;
                n045 [label="X"] ;
                n039 -> n046 ;
                n046 [label="EXP"] ;
                n046 -> n047 ;
                n047 [label="SIN"] ;
                n047 -> n048 ;
                n048 [label="X"] ;
        }
        '''        
        parsed_dot = parse_dot_data(dot_data)
        frame = MyFrame(None, -1, "treectrl.py", parsed_dot = parsed_dot)
        frame.Show(True)
        self.SetTopWindow(frame)
        return True

app = MyApp(0)
app.MainLoop()