如何让KP_Down等小键盘按键实现ttk.Treeview的默认滚动效果?
解决ttk.Treeview小键盘按键无法滚动的问题
在tkinter中,
<KP_Down>、<KP_Up>、<KP_Next>、<KP_Prior>小键盘按键默认无法像<KeyPress-Down>、<KeyPress-Up>、<KeyPress-Next>、<KeyPress-Prior>普通按键那样滚动ttk.Treeview组件,示例代码如下:
import random import tkinter as tk import tkinter.ttk as ttk class App(ttk.Frame): def __init__(self, master, **kwargs): super().__init__(master, **kwargs) self.master = master self.tree = None self.ysb = None self.xsb = None self.create_widgets() self.create_bindings() def create_widgets(self): self.tree = ttk.Treeview( self.master, height=10, selectmode='extended', takefocus=True, columns=("x", "y", "z"), displaycolumns=["x", "y", "z"]) self.tree.heading('#0', text="Points") self.tree.heading('x', text='X', anchor='center') self.tree.heading('y', text='Y', anchor='center') self.tree.heading('z', text='Z', anchor='center') self.tree.column('#0', stretch=True, width=100) self.tree.column('x', stretch=True, anchor='center', width=50) self.tree.column('y', stretch=True, anchor='center', width=50) self.tree.column('z', stretch=True, anchor='center', width=50) self.ysb = ttk.Scrollbar( self.master, orient='vertical', command=self.tree.yview) self.xsb = ttk.Scrollbar( self.master, orient='horizontal', command=self.tree.xview) self.tree.configure( yscrollcommand=self.ysb.set, xscrollcommand=self.xsb.set) self.columnconfigure(0, weight=1) self.rowconfigure(0, weight=1) self.tree.grid(row=0, column=0, sticky='nsew') self.ysb.grid(row=0, column=1, sticky='ns') self.grid(row=1, column=0, sticky='ew') groups = ["Local", "Remote"] for n, group in enumerate(groups): self.tree.insert( "", 'end', iid=f'G{n}', text=group, open=True) for i in range(10): self.tree.insert( f'G{n}', 'end', iid=f'G{n}-D{i}', text=f'G{n}-D{i}', values=[ random.randrange(0, 20), random.randrange(0, 20), random.randrange(0, 20) ] ) def create_bindings(self): self.tree.bind('<KeyPress-Down>', self.kp_down) self.tree.bind('<KeyPress-Up>', self.kp_up) self.tree.bind('<KeyPress-Next>', self.kp_down) self.tree.bind('<KeyPress-Prior>', self.kp_up) self.tree.bind('<KP_Down>', self.kp_down) self.tree.bind('<KP_Up>', self.kp_up) self.tree.bind('<KP_Next>', self.kp_down) self.tree.bind('<KP_Prior>', self.kp_up) def kp_down(self, event): print(f"{event.keysym}") def kp_up(self, event): print(f"{event.keysym}") if __name__ == '__main__': root = tk.Tk() app = App(root) root.columnconfigure(0, weight=1) root.rowconfigure(0, weight=1) app.grid(row=0, column=0, sticky='nsew') root.mainloop()
解决方案
我们可以手动实现小键盘按键对应的滚动和选中逻辑,让小键盘的上下键、翻页键和普通按键表现一致。修改kp_down和kp_up函数,分别处理单步滚动和翻页滚动的逻辑:
修改后的完整代码
import random import tkinter as tk import tkinter.ttk as ttk class App(ttk.Frame): def __init__(self, master, **kwargs): super().__init__(master, **kwargs) self.master = master self.tree = None self.ysb = None self.xsb = None self.create_widgets() self.create_bindings() def create_widgets(self): self.tree = ttk.Treeview( self.master, height=10, selectmode='extended', takefocus=True, columns=("x", "y", "z"), displaycolumns=["x", "y", "z"]) self.tree.heading('#0', text="Points") self.tree.heading('x', text='X', anchor='center') self.tree.heading('y', text='Y', anchor='center') self.tree.heading('z', text='Z', anchor='center') self.tree.column('#0', stretch=True, width=100) self.tree.column('x', stretch=True, anchor='center', width=50) self.tree.column('y', stretch=True, anchor='center', width=50) self.tree.column('z', stretch=True, anchor='center', width=50) self.ysb = ttk.Scrollbar( self.master, orient='vertical', command=self.tree.yview) self.xsb = ttk.Scrollbar( self.master, orient='horizontal', command=self.tree.xview) self.tree.configure( yscrollcommand=self.ysb.set, xscrollcommand=self.xsb.set) self.columnconfigure(0, weight=1) self.rowconfigure(0, weight=1) self.tree.grid(row=0, column=0, sticky='nsew') self.ysb.grid(row=0, column=1, sticky='ns') self.grid(row=1, column=0, sticky='ew') groups = ["Local", "Remote"] for n, group in enumerate(groups): self.tree.insert( "", 'end', iid=f'G{n}', text=group, open=True) for i in range(10): self.tree.insert( f'G{n}', 'end', iid=f'G{n}-D{i}', text=f'G{n}-D{i}', values=[ random.randrange(0, 20), random.randrange(0, 20), random.randrange(0, 20) ] ) def create_bindings(self): self.tree.bind('<KeyPress-Down>', self.kp_down) self.tree.bind('<KeyPress-Up>', self.kp_up) self.tree.bind('<KeyPress-Next>', self.kp_down) self.tree.bind('<KeyPress-Prior>', self.kp_up) self.tree.bind('<KP_Down>', self.kp_down) self.tree.bind('<KP_Up>', self.kp_up) self.tree.bind('<KP_Next>', self.kp_down) self.tree.bind('<KP_Prior>', self.kp_up) def kp_down(self, event): tree_height = self.tree.cget('height') all_items = self.tree.get_children() if event.keysym in ('Next', 'KP_Next'): # 处理翻页向下逻辑 current_visible = self.tree.identify_row(self.tree.winfo_height() - 1) if current_visible and current_visible in all_items: current_idx = all_items.index(current_visible) target_idx = current_idx + tree_height if target_idx < len(all_items): target_item = all_items[target_idx] self.tree.selection_set(target_item) self.tree.focus(target_item) self.tree.see(target_item) else: # 处理单步向下逻辑 current_item = self.tree.focus() if not current_item and all_items: current_item = all_items[0] next_item = self.tree.next(current_item) if next_item: self.tree.selection_set(next_item) self.tree.focus(next_item) self.tree.see(next_item) return "break" def kp_up(self, event): tree_height = self.tree.cget('height') all_items = self.tree.get_children() if event.keysym in ('Prior', 'KP_Prior'): # 处理翻页向上逻辑 current_visible = self.tree.identify_row(0) if current_visible and current_visible in all_items: current_idx = all_items.index(current_visible) target_idx = current_idx - tree_height if target_idx >= 0: target_item = all_items[target_idx] self.tree.selection_set(target_item) self.tree.focus(target_item) self.tree.see(target_item) else: # 处理单步向上逻辑 current_item = self.tree.focus() if not current_item and all_items: current_item = all_items[-1] prev_item = self.tree.prev(current_item) if prev_item: self.tree.selection_set(prev_item) self.tree.focus(prev_item) self.tree.see(prev_item) return "break" if __name__ == '__main__': root = tk.Tk() app = App(root) root.columnconfigure(0, weight=1) root.rowconfigure(0, weight=1) app.grid(row=0, column=0, sticky='nsew') root.mainloop()
代码说明
- 单步滚动(KP_Down/KP_Up):获取当前选中项,通过
tree.next()/tree.prev()获取下一个/上一个项,然后设置选中、焦点并滚动到视图中。如果没有选中项,默认选中第一个或最后一个可见项。 - 翻页滚动(KP_Next/KP_Prior):通过
tree.identify_row()获取当前可见区域的第一个/最后一个项,计算翻页后的目标索引,跳转到对应项并更新选中和视图。 - 返回
"break"是为了阻止事件继续传递,避免触发其他默认行为。
内容的提问来源于stack exchange,提问作者Sun Bear
相关产品推荐
相关产品推荐

