0

I want to use a graph that is generated by DOT (pyDot in python) as the basis for an interactive Tree-structured GUI in which each of the nodes in the Tree could be widgets.

The tree will basically be a binary Morse Code tree which start at the top node and navigate down the tree to their desired letter and select it. The node they want to select should be highlightable, and the contents (letters) of should be able to be changed based on user input.

Basically I want the nodes to be turned into full scale objects with tunable parameters that change as the interface is used. Can anyone point me in the right direction in order to do this?

1 Answers1

0

I started with the demo code at: http://wiki.wxpython.org/AnotherTutorial#wx.TreeCtrl. I've added the build_tree, _build_tree_helper and build_conn_dict methods. The key methods of interest from the dot_parser library are edge.get_source() and edge.get_destination() which are used to make the "connection" dictionary.

The dot graph is stored in the dot_data variable. Importantly, the dot graph must not loop; that is, it must be a spanning tree otherwise the _build_tree_helper method will loop infinitely (and it doesn't make sense in a TreeControl).

I also had to patch dot_parser according to https://github.com/nlhepler/pydot-py3/issues/1#issuecomment-15999052 to get it to work.

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()
dilbert
  • 3,008
  • 1
  • 25
  • 34
  • though this is interesting, it's not what i'm looking for. I'm looking to interact with the physical tree as an object (e.g. click on nodes and move them around, etc.) not just visualize them as treectrl – Rishi Sharma Jul 25 '13 at 08:14
  • In that case, you want https://gephi.org/. I think it can read dot files, amongst others. It is written in Java, however you can get a Jython interpreter for it and control it using Python if need be. – dilbert Jul 25 '13 at 08:38