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?
Asked
Active
Viewed 3.2k times
14
-
This opensource project might help you a lot: https://github.com/talcs/tals_python_task_diary – SomethingSomething Jan 16 '17 at 11:53
-
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 so – theRaven Oct 22 '16 at 22:10
1 Answers
29
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()

A. Rodas
- 20,171
- 8
- 62
- 72
-
Assuming that the questioner was really doing mapping of the filesystem, that's a good solution. – Donal Fellows May 25 '13 at 17:29
-
@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 Sep 19 '14 at 17:15
-
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? – ArtOfWarfare Dec 24 '16 at 14:52
-
@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. – A. Rodas Dec 24 '16 at 17:59
-
Great. But its not horizontally scrollable. Sub directories are overflowed from the treeview. – Al Mahdi Jun 28 '22 at 06:44