Search code examples
jsonpython-3.xrecursiontreeview

Trying to recursively populate a ttk.treeview with json/dictionary in python


I am new to python, but have java experience. Pretty different animals. I have a method that creates a json/dictionary based on walking through a directory structure and I have it where it creates a json like the one below. I am trying to get another method to populate a treeview based on it. I have seen several examples here on stackoverflow and have attempted to follow them. Below is what I have come up with, but it always errs out after going through the first directory, like it lost track of where it was. the following errors are returned:

Traceback (most recent call last):
  File "temp.py", line 147, in <module>
    load_treeview_from_project_dictionary(myData, tree_view)
  File "temp.py", line 134, in load_treeview_from_project_dictionary
    load_treeview_from_project_dictionary(c, tree_view, int(c['index']))
  File "temp.py", line 136, in load_treeview_from_project_dictionary
    tree_view.insert(parent_id, "end", text=c['name'], values=("", ""))
  File "C:\Users\...\AppData\Local\Programs\Python\Python39\lib\tkinter\ttk.py", line 1364, in insert
    res = self.tk.call(self._w, "insert", parent, index, *opts)
_tkinter.TclError: Item 1 not found

I worked several hours trying to refactor and figure this out, but have been unsuccessful. I am now reaching out to this great community to point out the error of my ways! Please help.

import json
import tkinter
from tkinter import ttk
from tkinter.ttk import Label


myData = {
    "name": "MyRoot",
    "type": "directory",
    "index": 0,
    "children": [
        {
            "name": "SubDir1",
            "type": "directory",
            "index": 1,
            "children": [
                {
                    "name": "apic.png",
                    "type": "file"
                }
            ]
        },
        {
            "name": "SubDir2",
            "type": "directory",
            "index": 2,
            "children": [
                {
                    "name": "somefile.txt",
                    "type": "file"
                },
                {
                    "name": "anotherfile.txt",
                    "type": "file"
                }
            ]
        },
        {
            "name": "SubDir3",
            "type": "directory",
            "index": 3,
            "children": []
        },
        {
            "name": "SubDir4",
            "type": "directory",
            "index": 4,
            "children": [
                {
                    "name": "asomefile.txt",
                    "type": "file"
                },
                {
                    "name": "bsomefile.txt",
                    "type": "file"
                },
                {
                    "name": "csomefile.txt",
                    "type": "file"
                },
                {
                    "name": "dsomefile.txt",
                    "type": "file"
                },
                {
                    "name": "esomefile.txt",
                    "type": "file"
                }
            ]
        },
        {
            "name": "SubDir5",
            "type": "directory",
            "index": 5,
            "children": [
                {
                    "name": "NesterDir1",
                    "type": "directory",
                    "index": 6,
                    "children": []
                }
            ]
        },
        {
            "name": "SubDir6",
            "type": "directory",
            "index": 7,
            "children": []
        },
        {
            "name": "SubDir7",
            "type": "directory",
            "index": 8,
            "children": []
        },
        {
            "name": "SubDir8",
            "type": "directory",
            "index": 9,
            "children": []
        },
        {
            "name": "SubDir9",
            "type": "directory",
            "index": 10,
            "children": []
        },
        {
            "name": "SubDir10",
            "type": "directory",
            "index": 11,
            "children": []
        },
        {
            "name": "SubDir11",
            "type": "directory",
            "index": 12,
            "children": []
        }
    ]
}


def load_treeview_from_project_dictionary(data, my_tree_view, parent_id=None):
    print("this:" + data['name'] + " called function!")
    if parent_id is None:
        my_tree_view.insert("", "0", text=data['name'], values=("", ""))  # applies to first iteration only
    if data['children'] is not None:
        for c in data['children']:
            print("child: " + c['name'])
            if c['type'] == "directory":
                my_tree_view.insert('', int(c['index']), text=c['name'], values=("", ""))
                load_treeview_from_project_dictionary(c, my_tree_view, int(c['index']))
            else:
                my_tree_view.insert(parent_id, "end", text=c['name'], values=("", ""))
                load_treeview_from_project_dictionary(c, my_tree_view, parent_id)


root = tkinter.Tk()

main_label = Label(root, text="Directory Tree")
tree_view = ttk.Treeview(root, height=23)

tree_view.heading("#0", text="Directory Structure")

load_treeview_from_project_dictionary(myData, tree_view)

main_label.pack()
tree_view.pack()

root.mainloop()

Thanks in Advance!


Solution

  • So. After reviewing the tutorial link posted by D.L and then combing through my code and debugging over and over, I came to the conclusion that there was too much recursion going on. Watching the flow I found that the method always stopped after the first file was added to the tree. Taking the recursive call after the file insert fixed a large part of the issue. I scrutinized the insertion process and found that I could use the indices in the json for the treeview's iid's. I then decided that it would be more efficient to use the treeview.move to place the entries where I wanted them as they are being inserted. Below is what I came up with and it works great. I am posting this here for anyone else that runs into the same issue. After the code, there is a screenshot of the resulting treeview (or a link to it due to my rank- I will try to fix that later)

    import json
    import tkinter
    from tkinter import ttk
    from tkinter.ttk import Label
    
    myData = {
        "name": "MyRoot",
        "type": "directory",
        "index": 0,
        "children": [
            {
                "name": "SubDir1",
                "type": "directory",
                "index": 1,
                "children": [
                    {
                        "name": "apic.png",
                        "type": "file"
                    }
                ]
            },
            {
                "name": "SubDir2",
                "type": "directory",
                "index": 2,
                "children": [
                    {
                        "name": "somefile.txt",
                        "type": "file"
                    },
                    {
                        "name": "anotherfile.txt",
                        "type": "file"
                    }
                ]
            },
            {
                "name": "SubDir3",
                "type": "directory",
                "index": 3,
                "children": []
            },
            {
                "name": "SubDir4",
                "type": "directory",
                "index": 4,
                "children": [
                    {
                        "name": "asomefile.txt",
                        "type": "file"
                    },
                    {
                        "name": "bsomefile.txt",
                        "type": "file"
                    },
                    {
                        "name": "csomefile.txt",
                        "type": "file"
                    },
                    {
                        "name": "dsomefile.txt",
                        "type": "file"
                    },
                    {
                        "name": "esomefile.txt",
                        "type": "file"
                    }
                ]
            },
            {
                "name": "SubDir5",
                "type": "directory",
                "index": 5,
                "children": [
                    {
                        "name": "NestedDir1",
                        "type": "directory",
                        "index": 6,
                        "children": [
                            {
                                "name": "yetAnotherfile.txt",
                                "type": "file"
                            }
                        ]
                    }
                ]
            },
            {
                "name": "SubDir6",
                "type": "directory",
                "index": 7,
                "children": []
            },
            {
                "name": "SubDir7",
                "type": "directory",
                "index": 8,
                "children": []
            },
            {
                "name": "SubDir8",
                "type": "directory",
                "index": 9,
                "children": []
            },
            {
                "name": "SubDir9",
                "type": "directory",
                "index": 10,
                "children": []
            },
            {
                "name": "SubDir10",
                "type": "directory",
                "index": 11,
                "children": []
            },
            {
                "name": "SubDir11",
                "type": "directory",
                "index": 12,
                "children": []
            }
        ]
    }
    
    
    def load_treeview_from_project_dictionary(data, my_tree_view, parent_id=None):
        print('this:' + data['name'] + ' called function!')
        if parent_id is None:
            my_tree_view.insert('', '0', text=data['name'], iid=0)  # applies to first iteration only
        for c in data['children']:
            print('child: ' + c['name'])
            if c['type'] == 'directory':
                my_tree_view.insert('', 'end', text=c['name'], iid=c['index'])
                my_tree_view.move(c['index'], data['index'], 'end')
                load_treeview_from_project_dictionary(c, my_tree_view, data['index'])
            else:
                file_index = my_tree_view.insert('', 'end', text=c['name'])
                my_tree_view.move(file_index, data['index'], 'end')
    
    
    root = tkinter.Tk()
    
    main_label = Label(root, text='Directory Tree')
    tree_view = ttk.Treeview(root, height=23)
    
    tree_view.heading('#0', text='Directory Structure')
    
    load_treeview_from_project_dictionary(myData, tree_view)
    
    main_label.pack()
    tree_view.pack()
    
    root.mainloop()
    

    Treeview screenshot