用tkinter Treeview小部件显示目录内容

14

我正在开发一个程序,它有自己的项目文件,里面包含了一些子文件。我想知道如何使用树形视图小部件来显示项目文件中的所有子文件,你有什么想法吗?


导入可以在第二行和第三行中以以下方式支持Python 3.4及以上版本进行更改:import tkinter as tk import tkinter.ttk as ttk 小写t代替Tkinter中的大写T,因为Python 3.4及以上版本不再识别Tkinter;消除了“未识别的引用”错误。新版本Python中的完全限定导入指令对点符号表示更加严格,因此需要使用tkinter.ttk as ttk,从而消除了重复的完全限定引用的需要。注意:人们会认为import tk.ttk就足够了,但是这样做会产生一个问题。 - theRaven
这个开源项目可能会帮助你很多:https://github.com/talcs/tals_python_task_diary - SomethingSomething
2个回答

29

在CPython的源代码中有一个示例,展示了如何使用递归填充Treeview以显示目录内容。基本上就是这个样子(我已经移除了事件绑定并将其封装成类,以获得更好的可读性):

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

更新: @ArtOfWarfare 提到,使用 <<TreeviewOpen>> 事件可以实现树的惰性填充。为了模拟关闭的节点,我使用了一个空的子项,在目录被打开时将其移除:

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

假设提问者真的在进行文件系统的映射,那是一个好的解决方案。 - Donal Fellows
@DonalFellows - 这是一个很好的答案,无论提问者真正想做什么,因为它确实回答了他们所问的问题。这正是我想要的(一个 Treeview 的示例 - 我刚刚开始使用 tk/ttk,并且需要看一些更多的示例,然后才能准备自己从头开始编写东西)。 - ArtOfWarfare
两年多过去了,我对此有了不同的想法 - 这样做不会非常低效吗?你将填充用户可能永远不会展开的目录。肯定有一种方法可以推迟调用 process_directory 直到用户实际打开它吧? - ArtOfWarfare
@ArtOfWarfare 当然可以,看看我的更新答案。如果目录树很大,懒加载可能是更好的解决方案 - 不过,在我看来,这个例子会变得更加复杂。 - A. Rodas
很好,但它不能水平滚动。子目录从树视图中溢出。 - Al Mahdi

0

在以下方式中,第二行和第三行支持Python 3.4及以上版本,可以更改导入:

import tkinter as tk
import tkinter.ttk as ttk

在Python 3.4及以上版本中,小写的t替换了Tkinter中的大写T,因为Python不再识别Tkinter,这样可以消除“未识别引用”错误。

在较新的Python版本中,完全限定的导入指令对点符号更加严格,因此需要使用tkinter.ttk作为ttk,并消除了重复的完全限定引用。注意:人们可能会认为import tk.ttk就足够了,但是它会引发引用错误;消除过多的指针和条件宏处理使我选择了上述格式--还有其他可能性,但这是最容易使用的形式。

path_to_my_project = # ...会引发错误,但只是占位符(仍然可用),可以改为以下内容:

path_to_my_project = "" # ...

请记住,如果在Windows中运行脚本,使用反斜杠的字面文件路径会引发错误;您必须在文件路径URL中用前导反斜杠(双反斜杠)转义反斜杠,如下所示:
path_to_my_project = "C:\\Users\\userName\\Desktop\\projDir" #Windows file paths

在文件路径中使用反斜杠转义反斜杠可以消除“Unicode 转义字符”错误,其中“C:\Users”会将控制字符 U 转义为 Users,这不是我们想要的结果。将路径强制转换为字符串也无法解决同样的错误,因此应该这样处理:

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

...是无效的。

上面的脚本示例经过这些修改后可以在运行于Windows 10 64位的Python 3.4 64位上正常工作,而且非常干净、稳定。

我喜欢它——只需简单更新就能轻松运行,没有错误、警告或无法解释的变通方法、胡说八道和黑客技巧。点赞。


不是答案,只是对上面实际答案的一条冗长评论。建议修改应在评论中完成。 - Chris Collett

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接