Display directory content with tkinter Treeview widget Display directory content with tkinter Treeview widget tkinter tkinter

Display directory content with tkinter Treeview widget


There is an example in the source code of CPython of how to fill a Treeview recursively with the content of a directory, this is basically how it works (I have removed the event bindings and wrapped it in a class for better readability):

import osimport tkinter as tkimport tkinter.ttk as ttkclass App(tk.Frame):    def __init__(self, master, path):        tk.Frame.__init__(self, master)        self.tree = ttk.Treeview(self)        ysb = ttk.Scrollbar(self, orient='vertical', command=self.tree.yview)        xsb = ttk.Scrollbar(self, orient='horizontal', command=self.tree.xview)        self.tree.configure(yscroll=ysb.set, xscroll=xsb.set)        self.tree.heading('#0', text=path, anchor='w')        abspath = os.path.abspath(path)        root_node = self.tree.insert('', 'end', text=abspath, open=True)        self.process_directory(root_node, abspath)        self.tree.grid(row=0, column=0)        ysb.grid(row=0, column=1, sticky='ns')        xsb.grid(row=1, column=0, sticky='ew')        self.grid()    def process_directory(self, parent, path):        for p in os.listdir(path):            abspath = os.path.join(path, p)            isdir = os.path.isdir(abspath)            oid = self.tree.insert(parent, 'end', text=p, open=False)            if isdir:                self.process_directory(oid, abspath)root = tk.Tk()path_to_my_project = # ...app = App(root, path=path_to_my_project)app.mainloop()

Update: As @ArtOfWarfare mentions, it is possible to lazy populate the tree using the <<TreeviewOpen>> event. To simulate the closed nodes, I've used an empty child item that is removed when a directory is opened:

import osimport tkinter as tkimport tkinter.ttk as ttkclass App(object):    def __init__(self, master, path):        self.nodes = dict()        frame = tk.Frame(master)        self.tree = ttk.Treeview(frame)        ysb = ttk.Scrollbar(frame, orient='vertical', command=self.tree.yview)        xsb = ttk.Scrollbar(frame, orient='horizontal', command=self.tree.xview)        self.tree.configure(yscroll=ysb.set, xscroll=xsb.set)        self.tree.heading('#0', text='Project tree', anchor='w')        self.tree.grid()        ysb.grid(row=0, column=1, sticky='ns')        xsb.grid(row=1, column=0, sticky='ew')        frame.grid()        abspath = os.path.abspath(path)        self.insert_node('', abspath, abspath)        self.tree.bind('<<TreeviewOpen>>', self.open_node)    def insert_node(self, parent, text, abspath):        node = self.tree.insert(parent, 'end', text=text, open=False)        if os.path.isdir(abspath):            self.nodes[node] = abspath            self.tree.insert(node, 'end')    def open_node(self, event):        node = self.tree.focus()        abspath = self.nodes.pop(node, None)        if abspath:            self.tree.delete(self.tree.get_children(node))            for p in os.listdir(abspath):                self.insert_node(node, p, os.path.join(abspath, p))if __name__ == '__main__':    root = tk.Tk()    app = App(root, path='.')    root.mainloop()


The imports can be changed at the second and third lines supporting Python 3.4 and above in the following manner:

import tkinter as tkimport tkinter.ttk as ttk

Lowercase t in tkinter replacing capital T in Tkinter because Python 3.4 and above no longer recognize Tkinter; eliminates an "unrecognized reference" error.

Fully qualified import directives in newer Python releases are more strict about dot notation all around, therefore tkinter.ttk as ttk is required and eliminates the need for repetitive fully qualified references. Caveat: one would assume that import tk.ttk would suffice, but somehow raises a reference error; eliminating excessive pointers and conditional macro processing lead me to choose the above format --there are other possibilities, but this is the easiest form to use.

path_to_my_project = # ... raises an error, but is merely placeholder (still functional though) and can be changed to the following:

path_to_my_project = "" # ...

Do remember that if running the script in Windows that a literal file path using backslashes raises and error; you have to escape the backslashes in the file path url with a preceding backslash (double backslashes) like the following:

path_to_my_project = "C:\\Users\\userName\\Desktop\\projDir" #Windows file paths

Escaping backslashes in the file path with backslashes eliminates "Unicode escape character" errors where "C:\Users" resolves to escaping the control character U in Users and this is not what we want in the first place. Casting the path as a string will not work as the same errors are raised, so this:

path_to_my_project = str("C:\Users\userName\Desktop\projDir")

...is ineffective.

The script example (above) works with these modifications against Python 3.4 64bit run in Windows 10 64bit without issue and is very clean & solid.

I like it --runs with little effort (simple updates) without errors, warnings or unexplainable work-arounds, bs and hacks. Thumbs up.