简体   繁体   中英

Display directory content with tkinter Treeview widget

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!

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()

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM