列をクリックしてTkツリービューのエントリを並べ替える方法はありますか?驚いたことに、私はこれに関するドキュメント/チュートリアルを見つけることができませんでした。
6 に答える
からのpatthoyts#tclは、TreeViewTkデモプログラムにソート機能があることを指摘しました。これに相当するPythonは次のとおりです。
def treeview_sort_column(tv, col, reverse):
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):
tv.move(k, '', index)
# reverse sort next time
tv.heading(col, command=lambda: \
treeview_sort_column(tv, col, not reverse))
[...]
columns = ('name', 'age')
treeview = ttk.TreeView(root, columns=columns, show='headings')
for col in columns:
treeview.heading(col, text=col, command=lambda: \
treeview_sort_column(treeview, col, False))
[...]
これはpython3では機能しませんでした。変数は参照によって渡されたため、すべてのラムダは列内の同じ最後の要素を参照することになりました。
これは私のためのトリックをしました:
for col in columns:
treeview.heading(col, text=col, command=lambda _col=col: \
treeview_sort_column(treeview, _col, False))
マドニウスは正しいですが、ここに完全な例と適切で理解しやすい説明があります
Sridhar Ratnakumarが提供する答えはpython3(および明らかにpython2.7)では機能しません。変数は参照によって渡されるため、すべてのラムダは列内の同じ最後の要素を参照することになります。
これを変更する必要がありますfor loop:
for col in columns:
treeview.heading(col, text=col, command=lambda _col=col: \
treeview_sort_column(treeview, _col, False))
また、treeview_sort_column内のラムダ関数にも同じ変更を適用する必要があります
したがって、完全なソリューションは次のようになります。
def treeview_sort_column(tv, col, reverse):
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):
tv.move(k, '', index)
# reverse sort next time
tv.heading(col, text=col, command=lambda _col=col: \
treeview_sort_column(tv, _col, not reverse))
[...]
columns = ('name', 'age')
treeview = ttk.TreeView(root, columns=columns, show='headings')
for col in columns:
treeview.heading(col, text=col, command=lambda _col=col: \
treeview_sort_column(treeview, _col, False))
[...]
Sridhar Ratnakumar Answerに
触発されて、DBのビューを作成しようとしたときに同じ問題が発生しました
。彼が行ったのと同じ原則を採用し、クラスTreeviewをアップグレードします。
class MyTreeview(ttk.Treeview):
def heading(self, column, sort_by=None, **kwargs):
if sort_by and not hasattr(kwargs, 'command'):
func = getattr(self, f"_sort_by_{sort_by}", None)
if func:
kwargs['command'] = partial(func, column, False)
return super().heading(column, **kwargs)
def _sort(self, column, reverse, data_type, callback):
l = [(self.set(k, column), k) for k in self.get_children('')]
l.sort(key=lambda t: data_type(t[0]), reverse=reverse)
for index, (_, k) in enumerate(l):
self.move(k, '', index)
self.heading(column, command=partial(callback, column, not reverse))
def _sort_by_num(self, column, reverse):
self._sort(column, reverse, int, self._sort_by_num)
def _sort_by_name(self, column, reverse):
self._sort(column, reverse, str, self._sort_by_name)
def _sort_by_date(self, column, reverse):
def _str_to_datetime(string):
return datetime.strptime(string, "%Y-%m-%d %H:%M:%S")
self._sort(column, reverse, _str_to_datetime, self._sort_by_date)
...
# Some code
...
treeview.heading('number', text='number', sort_by='num')
treeview.heading('name', text='name', sort_by='name')
treeview.heading('date', text='date', sort_by='date')
これをここに置くだけです:)
テーブルに整数がある場合は、関数にこの小さな変更を加えると、次のようになります。
def treeview_sort_column(treeview: ttk.Treeview, col, reverse: bool):
"""
to sort the table by column when clicking in column
"""
try:
data_list = [
(int(treeview.set(k, col)), k) for k in treeview.get_children("")
]
except Exception:
data_list = [(treeview.set(k, col), k) for k in treeview.get_children("")]
data_list.sort(reverse=reverse)
# rearrange items in sorted positions
for index, (val, k) in enumerate(data_list):
treeview.move(k, "", index)
# reverse sort next time
treeview.heading(
column=col,
text=col,
command=lambda _col=col: treeview_sort_column(
treeview, _col, not reverse
),
)
これは、文字列、数値、千の区切り記号を含む数値、複数の10進値を含むコードを含む列をソートするための作業コードです。
import tkinter as objTK
from tkinter import ttk as objTTK
from functools import partial
import datetime as objDateTime
class MyTreeview(objTTK.Treeview):
def heading(self, column, sort_by=None, **kwargs):
if sort_by and not hasattr(kwargs, 'command'):
func = getattr(self, f"_sort_by_{sort_by}", None)
if func:
kwargs['command'] = partial(func, column, False)
# End of if
# End of if
return super().heading(column, **kwargs)
# End of heading()
def _sort(self, column, reverse, data_type, callback):
l = [(self.set(k, column), k) for k in self.get_children('')]
l.sort(key=lambda t: data_type(t[0]), reverse=reverse)
for index, (_, k) in enumerate(l):
self.move(k, '', index)
# End of for loop
self.heading(column, command=partial(callback, column, not reverse))
# End of _sort()
def _sort_by_num(self, column, reverse):
self._sort(column, reverse, int, self._sort_by_num)
# End of _sort_by_num()
def _sort_by_name(self, column, reverse):
self._sort(column, reverse, str, self._sort_by_name)
# End of _sort_by_num()
def _sort_by_date(self, column, reverse):
def _str_to_datetime(string):
return objDateTime.datetime.strptime(string, "%Y-%m-%d")
# End of _str_to_datetime()
self._sort(column, reverse, _str_to_datetime, self._sort_by_date)
# End of _sort_by_num()
def _sort_by_multidecimal(self, column, reverse):
def _multidecimal_to_str(string):
arrString = string.split(".")
strNum = ""
for iValue in arrString:
strValue = f"{int(iValue):02}"
strNum = "".join([strNum, str(strValue)])
# End of for loop
strNum = "".join([strNum, "0000000"])
return int(strNum[:8])
# End of _multidecimal_to_str()
self._sort(column, reverse, _multidecimal_to_str, self._sort_by_multidecimal)
# End of _sort_by_num()
def _sort_by_numcomma(self, column, reverse):
def _numcomma_to_num(string):
return int(string.replace(",", ""))
# End of _numcomma_to_num()
self._sort(column, reverse, _numcomma_to_num, self._sort_by_numcomma)
# End of _sort_by_num()
# End of class MyTreeview
objWindow = objTK.Tk()
arrlbHeader = ["Type" , "Description", "C. Name", "C. code", "Amount", "Day", "Month ", "Year", "Date", "Comments"]
treeview = MyTreeview(columns=arrlbHeader, show="headings")
arrRows = [["Expenses", "Curds milk", "Dairy products", "2.5.2.1", "456", "31", "8", "2021", "2021-08-31", ""],
["Expenses", "Aug", "Maid", "2.12.4", "1,000", "31", "8", "2021", "2021-08-31", ""],
["Expenses", "Aug", "Water", "2.12.8", "200", "31", "8", "2021", "2021-08-31", "AAA"],
["Income", "Aug", "Electricity", "2.12.2", "190", "31", "8", "2021", "2021-08-31", "OMG"],
["Expenses", "Aug - garbage collection", "Miscellaneous", "2.12.9", "20", "31", "8", "2021", "2021-08-31", "Test1"],
["Expenses", "Bread", "Bakery", "2.5.1.1", "10", "29", "8", "2021", "2021-08-29", ""],
["Income", "Veggies", "Vegetables", "2.5.2.7", "21", "28", "8", "2021", "2021-08-28", ""],
["Expenses", "Groceries", "Grains", "2.5.2.3", "76", "28", "8", "2021", "2021-08-28", "Test"],
["Expenses", "Phenyl", "Toiletries", "2.16", "34", "28", "8", "2021", "2021-08-28", ""]]
arrColWidth = [57, 53, 85, 69, 55, 30, 45, 33, 68, 100]
arrColAlignment = ["center", "e", "w", "w", "e", "center", "center", "center", "center", "w"]
arrSortType = ["name", "name", "name", "multidecimal", "numcomma", "num", "num", "num", "date", "name"]
for iCount in range(len(arrlbHeader)):
strHdr = arrlbHeader[iCount]
treeview.heading(strHdr, text=strHdr.title(), sort_by=arrSortType[iCount])
treeview.column(arrlbHeader[iCount], width=arrColWidth[iCount], stretch=True, anchor=arrColAlignment[iCount])
# End of for loop
treeview.pack()
for iCount in range(len(arrRows)):
treeview.insert("", "end", values=arrRows[iCount])
# End of for loop
objWindow.bind("<Escape>", lambda funcWinSer: objWindow.destroy())
objWindow.mainloop()
