在网上有很多tkinter相关的教学,也有很多treeview相关方法的介绍,但是treeview表格选中相关的介绍比较少,这里分享一下个人使用过程中的经验。
与选中相关有两个函数,一个是selection,一个是selection_set。
可以用selection函数获取集合,包含了所有选中的单元的iid。
在treeview中,选中单位可以用selection_set函数设定,这个函数的对象是treeview类,方法的参数是目标的iid。如果传入参数是一个iid,只选择一个目标,如果传入列表等,就会选中多行目标。
全选表格
我们可以遍历整个表格,把每一行的iid保存到一个列表中,然后将列表传递给selection_set函数,就可以选中整个表格。
import tkinter as tk
from tkinter import ttk
def select_all_items(tree):
select = []
for item in tree.get_children():
select.append(item)
tree.selection_set(select)
print(tree.selection()) #打印查看选中行的iid
root = tk.Tk()
tree = ttk.Treeview(root)
tree.pack(fill=tk.BOTH, expand=True)
# 假设你已经填充了treeview,下面是添加条目的示例代码
for i in range(5):
tree.insert('', 'end', text=f"Item {i}")
# 全选按钮
select_all_button = ttk.Button(root, text="Select All", command=lambda: select_all_items(tree))
select_all_button.pack()
root.mainloop()
这段代码基于百度ai,但是ai直接生成的代码有问题。他每次传入一个位置,运行的时候速度太快,人是看不见的,最后结果是选中了最后一个选项。
鼠标拖拽选中
绑定事件
鼠标拖拽主要有三个动作,按下鼠标、移动、松开,我们可以用三个函数处理对应的操作并绑定到事件。
# 绑定事件
tree.bind("<ButtonPress-1>", self.on_mouse_click)
tree.bind("<ButtonRelease-1>", self.on_mouse_release)
tree.bind("<B1-Motion>", self.on_mouse_motion)
选中区域管理
我们在选中区域的时候,会有一个起点位置,这个位置无论如何最终都会被选中。当我们把鼠标远离起点位置时,会加入更多的行,反过来靠近时则会去掉多余的行。在行的进出过程中,其顺序符合先入后出的原则,所以我们用一个类似队列的结构去管理我们要选中的区域。
按下鼠标
按下鼠标后进入事件处理函数,首先判断事件是否和我们的表格相关。将鼠标位置的行作为第一个元素加入队列中,然后用一个布尔类记录启动信号,表示已经开始拖拽。
# 如果是按下左键,开始拖拽选择
tree = self.tree
if event.widget == tree:
self.selection_trace = [tree.identify_row(event.y)]
tree.selection_set(tree.identify_row(event.y))
self.drag_start.set(True)
移动鼠标
每次移动鼠标,我们首先获取当前所在行的iid。当我们移动到新的一行,就把新的一行加入到队列,如果回到上一行,就把队尾的元素踢出。这样的话,我们的队列中,队尾是上一次加入的新元素,队尾的前一个是上次加入的元素(如果有的话)。
如果鼠标在同一行移动,每次获取的都是同一行,应当与队尾为同一行,因此要判断是否在同一行移动。如果在同一行,就不做任何处理。
if next_row == self.selection_trace[-1]: #原来行没变
return
假设起点在上方,鼠标往下移动移动到新的行,往上移动到原来行位置。原来行对应队列中倒数第二个元素,新的行在队列中没有元素,需要比较当前行的位置判断移动方向。由于最开始的时候队列中只有一个元素,使用下标访问[-2]会越界,因此先判断队列的长度。
if len(self.selection_trace) == 1 or next_row != self.selection_trace[-2]:
self.selection_trace.append(next_row)
如果队列中倒数第二个元素与当前行相同,说明鼠标往上走也就是回去了,这时候就抛出队尾。
tree.selection_set(self.selection_trace)
这部分代码合起来
# 如果已经开始拖拽,更新选择区域
tree = self.tree
if event.widget == tree and self.drag_start.get():
next_row = tree.identify_row(event.y)
if next_row == self.selection_trace[-1]: #原来行没变
return
if len(self.selection_trace) == 1 or next_row != self.selection_trace[-2]:
self.selection_trace.append(next_row)
else: #回去一行
self.selection_trace.pop(-1)
tree.selection_set(self.selection_trace)
松开鼠标
将开始的标志赋值为否,表示拖拽选中已经停止。
def on_mouse_release(self, event):
# 如果是释放左键,结束拖拽选择
tree = self.tree
if event.widget == tree and self.drag_start.get():
self.drag_start.set(False)
完整的代码如下,额外添加了右键选中。
import tkinter as tk
from tkinter import ttk
class drag_select_tree:
def __init__(self, root):
self.root = root
self.drag_start = tk.BooleanVar() # 开始拖拽选择的标志
self.drag_start.set(False)
self.create_tree()
def create_tree(self):
tree = ttk.Treeview(root, columns=("Size", "Modified"), selectmode='extended')
tree.pack(fill="both", expand=True)
self.tree = tree
# 创建示例数据
for i in range(10):
tree.insert("", "end", text=f"Item {i}", values=(f"Size {i}", f"Modified {i}"))
# 绑定事件
tree.bind("<ButtonPress-1>", self.on_mouse_click)
tree.bind("<ButtonPress-3>", self.on_mouse_click)
tree.bind("<ButtonRelease-1>", self.on_mouse_release)
tree.bind("<ButtonRelease-3>", self.on_mouse_release)
tree.bind("<B1-Motion>", self.on_mouse_motion)
tree.bind("<B3-Motion>", self.on_mouse_motion)
def on_mouse_click(self, event):
# 如果是按下左键,开始拖拽选择
tree = self.tree
if event.widget == tree:
self.selection_trace = [tree.identify_row(event.y)]
tree.selection_set(tree.identify_row(event.y))
self.drag_start.set(True)
def on_mouse_release(self, event):
# 如果是释放左键,结束拖拽选择
tree = self.tree
if event.widget == tree and self.drag_start.get():
self.drag_start.set(False)
def on_mouse_motion(self, event):
# 如果已经开始拖拽,更新选择区域
tree = self.tree
if event.widget == tree and self.drag_start.get():
next_row = tree.identify_row(event.y)
if next_row == self.selection_trace[-1]: #原来行没变
return
if len(self.selection_trace) == 1 or next_row != self.selection_trace[-2]:
self.selection_trace.append(next_row)
else: #回去一行
self.selection_trace.pop(-1)
tree.selection_set(self.selection_trace)
root = tk.Tk()
drag_select_tree(root)
root.mainloop()