はじめに
tkinterのTreeviewを使用すると,データを表形式のGUIで表示することが可能です.ただ,これらのデータは追加した順に表示されます.
今回はヘッダーをクリックすることで,項目ごとにデータをソートする機能を追加した際の備忘録です.
動作環境
- Python 3.12.0
- tkinter 8.6.12
ソースコード
sample.py
import tkinter as tk
from tkinter import ttk
def select_header(tree, reverse_flag):
# クリックされたヘッダー列取得
x = tree.winfo_pointerx() - tree.winfo_rootx()
select_column_str = tree.identify_column(x)
select_column_int = int(select_column_str[1:]) - 1
l = [(tree.set(k, select_column_int), k) for k in tree.get_children("")]
l.sort(reverse=reverse_flag)
for index, (val, k) in enumerate(l):
tree.move(k, "", index)
tree.heading(select_column_int, command=lambda:select_header(tree, not reverse_flag))
main = tk.Tk()
style = ttk.Style()
style.theme_use("clam")
datas = [
["1", "4", "b"],
["2", "3", "d"],
["3", "2", "a"],
["4", "1", "c"],
]
columns = 0
for data in datas:
if columns < len(data):
columns = len(data)
clmn = tuple(range(columns))
tree = ttk.Treeview(main, columns=clmn, show="headings")
tree.grid(row=0, column=0, sticky="nsew")
for i in range(columns):
tree.heading(i, text="Data"+str(i+1), command=lambda:select_header(tree, False))
for data in datas:
tree.insert("", "end", value=(data))
main.mainloop()
クリックしたヘッダー列取得
「どの列をソートするか」を判別するために,クリックしたヘッダーを識別します.
x = tree.winfo_pointerx() - tree.winfo_rootx()
select_column_str = tree.identify_column(x)
select_column_int = int(select_column_str[1:]) - 1
画面に表示しているx座標をもとにidentify_column()
でヘッダ名を#~の形で特定します.select_column_int
の値は左のヘッダーから順に0,1,2...となります.
データをソート
l = [(tree.set(k, select_column_int), k) for k in tree.get_children("")]
l.sort(reverse=reverse_flag)
get_childran()
でクリックしたヘッダー列のデータのリストを取得します.sort()
で取得したデータをソートします.オプションのreverse
は,True
が降順ソート,False
が昇順ソートとなります.
Treeviewにデータをソート順に表示
for index, (val, k) in enumerate(l):
tree.move(k, "", index)
Treeviewのmove
メソッドはk
をindex
の位置に移動させます.
今回のソースコードでは,関数内のheading
でselect_header(tree, not revers_flag)
を設定することで,クリックするごとに昇順ソート,降順ソートを繰り返すようになっています.
おわりに
Treeviewでソートは基本装備していい気がするので便利なメソッドがあればいいなと切実に思いました.
参考文献