0

I am trying to create an explorer in tkinter. I just heard about an hierarchical Treeview. Therefore, I am trying to implement it.

Here, I tried using recursion, because for a for loop, one cannot know the number of subdirectories and files inside a particular folder.

Here is my recursive function.

def new_folder(path,arrs):

    for arr in arrs:
        if os.path.isdir(path+"\\"+arr):
            try:
                a1=os.listdir(path+"\\"+arr)
                new_folder(path+"\\"+arr,a1)

            except PermissionError:
                pass
        else:
            print(path+"\\"+arr)

path="C:\\Users\\91996\\Documents"
arrss=os.listdir(path)
new_folder(path,arrss)

Whenever I run this code, it gives me this error:

Traceback (most recent call last):
  File "c:/Users/91996/Documents/Visual studio code/focd.py", line 47, in <module>
    new_folder(path,arrss,'item'+str(2))
  File "c:/Users/91996/Documents/Visual studio code/focd.py", line 37, in new_folder
    new_folder(path+"\\"+arr,a1,values[:-1]+str(3))
  File "c:/Users/91996/Documents/Visual studio code/focd.py", line 44, in new_folder
    treeview.move(values,'item1','end')
  File "C:\Program Files\Python3.8.6\lib\tkinter\ttk.py", line 1388, in move
    self.tk.call(self._w, "move", item, parent, index)
_tkinter.TclError: Item item3 not found

Here is my code.

from tkinter import *
import os
# Importing ttk from tkinter
from tkinter import ttk

# Creating app window
app = Tk()

# Defining title of the app
app.title("GUI Application of Python")

# Defining label of the app and calling a geometry
# management method i.e, pack in order to organize
# widgets in form of blocks before locating them
# in the parent widget
ttk.Label(app, text ="Treeview(hierarchical)").pack()

# Creating treeview window
treeview = ttk.Treeview(app)

# Calling pack method on the treeview
treeview.pack()

# Inserting items to the treeview
# Inserting parent
treeview.insert('', '0', 'item1',
                text ='Documents')

# Inserting child
i=3
def new_folder(path,arrs,values):
    global i
    for arr in arrs:
        if os.path.isdir(path+"\\"+arr):
            try:
                a1=os.listdir(path+"\\"+arr)
                new_folder(path+"\\"+arr,a1,values[:-1]+str(i))
                i+=1
            except PermissionError:
                pass
        else:
            treeview.insert(values,'end',text=arr)
            print(path+"\\"+arr)
    treeview.move(values,'item1','end')
path="C:\\Users\\91996\\Documents"
arrss=os.listdir(path)
new_folder(path,arrss,'item'+str(i))
app.mainloop()

Currently, this is the Treeview which I made using a for loop. However, I feel this is not good because it will not fetch files and folders. I had to make 8 nested for loops to get all files and folders up to the 8th sub-folder. But recursion can solve all the problem. The only problem lies in implementing it with ttk.Treeview.

enter image description here

martineau
  • 119,623
  • 25
  • 170
  • 301
  • You should initialized `i` to 1 instead of 3. – acw1668 Jun 22 '21 at 06:50
  • The code shown in the traceback doesn't match what's in your question. Please provide a runnable [mre]. – martineau Jun 22 '21 at 07:14
  • Off topic, but you can use [`os.walk()`](https://docs.python.org/3/library/os.html#os.walk) to get all subdirectories and files in a folder. – 8349697 Jun 22 '21 at 09:28
  • @8349697, Thanks for your solution. But can I create a hierarchy like the one given in the picture –  Jun 23 '21 at 07:42
  • @Sujay [`treeview.insert()`](https://anzeljg.github.io/rin2/book2/2405/docs/tkinter/ttk-Treeview.html) accepts two required positional arguments - parent and index. The program analyzes your code as follows: `treeview.insert(parent=values, index='end', text=arr)`. You must keep track of the current parent of the files in order to [create a hierarchy](https://stackoverflow.com/questions/16746387/display-directory-content-with-tkinter-treeview-widget). – 8349697 Jun 23 '21 at 14:28
  • @8349697, Thank you so much! Please post it as an answer. I will upvote and accept it –  Jun 23 '21 at 16:36

1 Answers1

2

You start with:


i=3
...
new_folder(path,arrss,'item'+str(i))

If you add these lines to your function, you will see that item3 does not exist yet.


def new_folder(path,arrs,values):
    print('item1 ->', treeview.exists('item1'), treeview.item('item1'))
    print(values, '->', treeview.exists(values))
    global i
    for arr in arrs:
        print(f"action: treeview.insert(values,'end',text=arr)")
        print(f"insert arguments: parent='{values}', index='end', text='{arr}'")

Output:


item1 -> True {'text': 'Documents', 'image': '', 'values': '', 'open': 0, 'tags': ''}
item3 -> False
action: treeview.insert(values,'end',text=arr)
insert arguments: parent='item3', index='end', text='file.ext'
Traceback (most recent call last):
...
    res = self.tk.call(self._w, "insert", parent, index, *opts)
_tkinter.TclError: Item item3 not found

You need to get the iid's of all found folders in the loop and use them as the current parents.


from tkinter import *
import os
from tkinter import ttk


app = Tk()
app.title("GUI Application of Python")

ttk.Label(app, text="Treeview(hierarchical)").pack()

treeview = ttk.Treeview(app, show='tree')
treeview.pack(fill='both', expand=True)


def new_folder(parent_path, directory_entries,
               parent_iid, f_image, d_image):
    """Creates a graphical representation of the structure
    of subdirectories and files in the specified parent_path.

    Recursive tree building for large directories can take some time.

    :param parent_path: directory path, string
    :param directory_entries: List[str]
           a list containing the names of the entries in the parent_path
           obtained using os.listdir()
    :param parent_iid: unique identifier for a treeview item, string
    :param f_image: file icon, tkinter.PhotoImage
    :param d_image: directory icon, tkinter.PhotoImage
    :return: None
    """
    for name in directory_entries:
        item_path = parent_path+os.sep+name
        # optional: file does not exist or broken symbolic link
        #if not os.path.exists(item_path):
            #print("Skipped:", item_path)
            #continue
        if os.path.isdir(item_path):
            # set subdirectory node
            subdir_iid = treeview.insert(parent=parent_iid,
                                         index='end',
                                         text=name,
                                         image=d_image)
            try:
                # pass the iid of the subdirectory as parent iid
                # all files/folders found in this subdirectory
                # will be attached to this subdirectory node
                subdir_entries = os.listdir(item_path)
                new_folder(parent_path=item_path,
                           directory_entries=subdir_entries,
                           parent_iid=subdir_iid,
                           f_image=f_image,
                           d_image=d_image)
            except PermissionError:
                pass
        else:
            treeview.insert(parent=parent_iid,
                            index='end',
                            text=name,
                            image=f_image)
        print(item_path)


# png 16x16 -> base64 strings for tkinter.PhotoImage
file_img = """
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAABGdB
TUEAALGPC/xhBQAAAAlwSFlzAAAScwAAEnMBjCK5BwAAAalJREFU
OE99kUtLAmEUhg38Af6a6he06SJIF8FFREVtqkVEkBB2IcJIRDAk
80KrNq3CWihuuuh4GVIwM9BSa0bHUXGc8X7p1Bc6jtLLxzAwz3Pm
nPMNcaWySCR6CYVoOgMvzWaz3W63Wq1GowFPsVg8PDIqkUjg019A
gEOSJHAowEHAhDidzmAwGI3FEPZTXSDwafiJ3W5nWRbH8TSVGSAI
aBDi8TiGYT6fz2QyCwU+Dc0ADanX67XfWK3W/4QOjYRqtWqxWIQC
mhKh/NpAVyoVs/GcclwzabI7NF/odILocrnsPFwvGRcS6uUeob8T
RDOxMGuYz2vkfsdtVxhIg1AqMqnTRUYrD+iU2Vy+R+jvBMpTN+aC
Zi6umo2+RXouDmgkFJ4fyLNNNvUFdJFM0kfTuQOpfk9ZZLmuQBBE
Z4Np/VrtapVSKwqf7wmlLLc/iR9vGAyGnrWCgC4ImmZpKqVbKeoV
xK4sq5pI7kgjAfzCZBIK/PWX8jRxspRVjVPbY8FLLcdxfQKZ8vlx
j9eLebxuzOPGMO/j/YdyJro1dWezPblc4defieF8A+ZBma193+p6
AAAAAElFTkSuQmCC
"""
dir_img = """
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAABGdB
TUEAALGPC/xhBQAAAAlwSFlzAAAScgAAEnIBXmVb4wAAAihJREFU
OE990vtv0kAcAPDh4wd/8w/yp5n4i4kkmizoBMKcRIfG7YepWSSZ
MSTGSXQao8wtjIw5ZkJEJ4+O0CGvoWhSCmw0sFJaHoU+GEJbyDax
EwMjQS+Xy30v97m7791JOF4Y+FMOBEJsj508PXD8lERyoj3Yp4ig
XclNRSlyg0F0TFxLRbXFyAMqaarkQrXabmfO4eqdoBRWtgQnujGM
+DRVbIYnTU3Wvrf7hcubGRToTOsFvK3V/JZyy6KeCXL7CZc3CEVj
k7bTO85/A+FDgwr6rKNIQEtu6XnC0Ch/+j+wCSXXulke994vxh/X
sNkGaaXTfXfYVLbEIwk2gbQBpstxz0QOmq6mdXxxmUr1A8Wg4i8o
rLoWh2C3hvhxr5KcqhMLVMrRJ4eCX97irL/qFh6fdxkvQoAaj4yz
iTv17KsyYu8D8t7hg+q7fXahjr50GaWQawT7OkbDN3/u6JIflQxb
qXN8zzsQniv7tGGv9Czx/tz64gXIqcTCagoaqWxpcO/dKBzL4oTI
uu+QBYaaBX2DeBgyDoJmKQzIMyEVE5Wz8HXMO7W29tnnD8TiiS5A
HbIGPi1kJn3zZzdWLh2CoIKGZHRUlXJPzs29XVoy40SuC6p0lgyo
+PS4e/aM8835GHALDWvY2LVybAxcVs881XtAUEyjC8SEGPx7bfu2
4/mgzfwiEgSz4dcJixRxjq5YVtEM1r6oHiDGuP9RwHS1TOaP/tCj
/d+VL1STn8NNZQAAAABJRU5ErkJggg==
"""
file_image = PhotoImage(data=file_img)
dir_image = PhotoImage(data=dir_img)

# adds a parent item to the tree
# and returns the item's iid value (generated automatically)
parent_iid = treeview.insert(parent='',
                             index='0',
                             text='Documents',
                             open=True,
                             image=dir_image)

start_path = os.path.expanduser(r"~\Documents")
start_dir_entries = os.listdir(start_path)

# inserting items to the treeview
new_folder(parent_path=start_path,
           directory_entries=start_dir_entries,
           parent_iid=parent_iid,
           f_image=file_image,
           d_image=dir_image)

app.mainloop()

ttk.Treeview Example directory tree on Windows

8349697
  • 415
  • 1
  • 6
  • 11
  • Just a question, what is that code instead of the file path like what is ```dir_img``` –  Jun 24 '21 at 14:03
  • The [explanation of what it is](https://en.wikipedia.org/wiki/Base64) is a bit long. I just converted a small png image to a base64 string for demonstration purposes. If you run this code, you can see something like icons for files and directories. They are, as it were, part of the program. Python has a standard [base64 module](https://docs.python.org/3/library/base64.html). – 8349697 Jun 24 '21 at 16:14
  • So by this, we don't actually need to download an image? By this I mean that if we once convert the image to a base64 string, then we can delete the original image and the code won't throw an error? –  Jun 24 '21 at 16:15
  • It depends on your specific situation and your needs. PNG images (base64 data) are supported in [Tcl/Tk since version 8.6](https://tcl.tk/man/tcl8.6/TkCmd/photo.htm#M6). – 8349697 Jun 24 '21 at 16:30