Display directory content with tkinter Treeview widget

29,008

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 os
import tkinter as tk
import tkinter.ttk as ttk

class 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 os
import tkinter as tk
import tkinter.ttk as ttk


class 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()
Share:
29,008
user2213550
Author by

user2213550

Updated on July 24, 2022

Comments

  • user2213550
    user2213550 almost 2 years

    At the moment I am working on a program that has its own project files and inside that are like sub-files and I want to know how to use the treeview widget to display all the sub-files inside the project file, Any Ideas?

    Thanks in advance!

  • Donal Fellows
    Donal Fellows almost 11 years
    Assuming that the questioner was really doing mapping of the filesystem, that's a good solution.
  • ArtOfWarfare
    ArtOfWarfare over 9 years
    @DonalFellows - This is a good answer no matter what the questioner was really trying to do, because it really answers what they asks. It's exactly what I wanted (an example of Treeview - I'm just getting started with tk/ttk and need to see some more examples before I'm ready to write stuff from scratch on my own.)
  • ArtOfWarfare
    ArtOfWarfare over 7 years
    Over two years later, I have a different thought on this - won't it be really inefficient? You're going to populate directories the user may never end up expanding. Surely there's a way to defer calling process_directory until the user actually opens it?
  • A. Rodas
    A. Rodas over 7 years
    @ArtOfWarfare Sure it's possible, see my updated answer. If the directory tree is large, the lazy load may be a better solution - however, the example gets a little more complex IMO.
  • Chris Collett
    Chris Collett over 2 years
    Not an answer, just a lengthy comment to the actual answer above. Recommending modifications should be done in comments.
  • Al Mahdi
    Al Mahdi almost 2 years
    Great. But its not horizontally scrollable. Sub directories are overflowed from the treeview.