Python 3.3.2 tkinter ttk TreeView per-column sorting only sorts by last column?
python, sorting, tkinter, treeview, ttk
Solution
You should use lambda in other way. In your code every lambda gets a pointer to the same variable, that is why all functions do the same thing. You should, e.g, copy variable to make lambda unique. An example how to do it:
for each in ('name', 'path', 'time','pb'):
listbox.heading(each,text=each.capitalize(),command=lambda each_=each: treeview_sort_column(listbox, each_, False))
Simple example:
funcs = []
for item in ('abc', 'def', 'ghi'):
funcs.append(lambda : print(item))
for f in funcs:
f()
It prints:
ghi
ghi
ghi
But version with fixed lambda:
funcs = []
for item in ('abc', 'def', 'ghi'):
funcs.append(lambda item_=item: print(item_))
for f in funcs:
f()
prints
abc
def
ghi
Problem
I am trying to get a simplistic ttk TreeView table going with per-column sorting using the heading 'command' tag, but it doesn't seem to work right. I'm using the answer to this question for implementing the functionality: Tk treeview column sort My code: ``` import tkinter as tk from tkinter import ttk def treeview_sort_column(tv, col, reverse): print('sorting %s!' % col) l = [(tv.set(k, col), k) for k in tv.get_children('')] l.sort(reverse=reverse) # rearrange items in sorted positions for index, (val, k) in enumerate(l): print('Moving Index:%r, Value:%r, k:%r' % (index, val, k)) tv.move(k, '', index) # reverse sort next time tv.heading(col, command=lambda: treeview_sort_column(tv, col, not reverse)) cols = ('name', 'path', 'time', 'pb') root = tk.Tk() root.geometry("700x500") listbox = ttk.Treeview(root, columns=cols, show="headings") for each in ('name', 'path', 'time','pb'): listbox.heading(each,text=each.capitalize(),command=lambda: treeview_sort_column(listbox, each, False)) listbox.column( each, width=tk.font.Font().measure(each.title() )) if not each == 'path': listbox.column(each,stretch=False) if not each == 'name': listbox.column( each, anchor='center') listbox.pack(expand=True, fill=tk.BOTH) root.mainloop() ``` The problem I am running into is every time I run this, it only sorts by the last column, not by the column your clicking on (verified with the print statement in the treeview_sort_column function). Output I get from clicking on any column in the resulting window: ``` sorting pb! sorting pb! sorting pb! sorting pb! sorting pb! sorting pb! sorting pb! sorting pb! ``` If I change from dynamic creation of each command in the for loop to explicit ones by adding this after the loop then it works as expected (i.e. each column sorts itself). ``` listbox.heading('name', command=lambda: treeview_sort_column(listbox, 'name', False)) listbox.heading('path', command=lambda: treeview_sort_column(listbox, 'path', False)) listbox.heading('time', command=lambda: treeview_sort_column(listbox, 'time', False)) listbox.heading('pb', command=lambda: treeview_sort_column(listbox, 'pb', False)) ``` produces: ``` sorting name! sorting name! sorting path! sorting path! sorting time! sorting time! sorting pb! sorting pb! ``` Obviously this is a very simplistic example, and my final application actually inserts data into the columns (also has more columns) but since I can't even get this simplified version to work, I'm at a loss. What is wrong with my loop that's causing the lambda functions to get screwed up? My system: - Windows 7, 64 bit - Python 3.3.2