sortingtkintertreeviewpython-3.9

Sorting 1st column of treeview app sent failure


I used an exapmple from a tutorial to sort columns in treeview. It works for 2nd and 3rd column, but not for the 1st column. If I click on the heading I get this Error:

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python311\Lib\tkinter\__init__.py", line 1967, in __call__
    return self.func(*args)
           ^^^^^^^^^^^^^^^^
  File "D:\...\Test_Treeview_book.py", line 20, in <lambda>
    tv.heading('#0', text='Name', command=lambda: sort(tv, '#0'))
                                                  ^^^^^^^^^^^^^^
  File "D:\...\Test_Treeview_book.py", line 14, in sort
    itemlist.sort(key=lambda x: tv.set(x, col))
  File "D:\...\Test_Treeview_book.py", line 14, in <lambda>
    itemlist.sort(key=lambda x: tv.set(x, col))
                                ^^^^^^^^^^^^^^
  File "C:\Python311\Lib\tkinter\ttk.py", line 1434, in set
    res = self.tk.call(self._w, "set", item, column, value)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_tkinter.TclError: Display column #0 cannot be set

I have this small test program as reference and couldn't find the failure. I am thankful for any hint to solve this issue or is it not possible to sort 1st column at all, because it's a tree?

import tkinter as tk
from tkinter import ttk
from pathlib import Path

root = tk.Tk()

root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)

paths = Path('.').glob('**/*')

def sort(tv, col): # Doesn't work on 1st column '#0'
    itemlist = list(tv.get_children(''))
    itemlist.sort(key=lambda x: tv.set(x, col))
    for index, iid in enumerate(itemlist):
        tv.move(iid, tv.parent(iid), index)

tv = ttk.Treeview(root, columns=['size','modified'], selectmode=None)

tv.heading('#0', text='Name', command=lambda: sort(tv, '#0'))
tv.heading('size', text='Size', anchor='center', command=lambda: sort(tv, 'size'))
tv.heading('modified', text='Modifies', anchor='center', command=lambda: sort(tv, 'modified'))

tv.column('#0', stretch = True, anchor='w')
tv.column('size', width=100, anchor='center') 
tv.column('modified',anchor='center')

tv.grid_rowconfigure(0, weight=1)
tv.grid_columnconfigure(0, weight=1) 
tv.grid(row=0, column=0, sticky='nsew')
#tv.pack(expand=True, fill='both')

for path in paths:
    meta = path.stat()
    parent = str(path.parent)
    if parent == '.':
        parent = ''
               
    tv.insert(parent, 'end', iid=str(path), text=str(path.name), values=[meta.st_size, meta.st_mtime])

scrollbar = ttk.Scrollbar(root, orient=tk.VERTICAL, command=tv.yview)
tv.configure(yscrollcommand=scrollbar.set)
scrollbar.grid(row=0, column=1, sticky='nse') #scrollbar goes not to the left!
        
root.mainloop()

Solution

  • As the error said, you cannot use .set() on column "#0" (tree column).

    Use .item() instead:

    def sort(tv, col):
        itemlist = list(tv.get_children(''))
        if col == '#0':
            itemlist.sort(key=lambda x: tv.item(x, 'text'))
        else:
            itemlist.sort(key=lambda x: tv.set(x, col))
        for index, iid in enumerate(itemlist):
            tv.move(iid, tv.parent(iid), index)