Tkinter Treeview定时更新循环刷新时行高亮消失如何解决
问题现象
使用Treeview组件加载高频变动的MongoDB集合数据,通过定时循环刷新表格内容时,原本鼠标悬浮触发的橙色行高亮效果会在每次刷新后消失。
以下代码可复现该问题:
from Tkinter import * import Tkinter import ttk as TTK root = Tk() style = TTK.Style() tv = TTK.Treeview() def highlight_row(event): tree = event.widget item = tree.identify_row(event.y) tree.tk.call(tree, "tag", "remove", "highlight") tree.tk.call(tree, "tag", "add", "highlight", item) tv.grid(row=1, column=0) tv.tag_configure('highlight', background='orange') tv.bind("<Motion>", highlight_row) style.configure("Treeview", font=(None, 12)) style.configure("Treeview", foreground= '#3F3F3F', background= '#3F3F3F', fieldbackground='#3F3F3F') tv['columns'] = ('a','b','c') tv.heading('a', text='a') tv.column('a', anchor='center', width=100) tv.heading('b', text='b') tv.column('b', anchor='center', width=175) tv.heading('c', text='c') tv.column( 'c', anchor='center', width=90) external_db = [{'x':1,'y':2, 'z':3}, {'x':4,'y':5, 'z':6}, {'x':7,'y':8, 'z':9}] def update(): try: for j in tv.get_children(): tv.delete(j) for j, k in enumerate(external_db): tv.insert('', 'end', text=str(j), values=(k['x'],k['y'],k['z']) ,tags = ('col')) tv.tag_configure('col', foreground = 'white') except Exception, e: print e, 'Update Exception' pass root.after(1500,update) update() root.mainloop()
触发故障的逻辑:每次root.after(1500,update)执行全量删行重绘后,高亮状态直接丢失。
配置root.configure(background="#3F3F3F")后的初始运行效果:
问题根因
高亮效果依赖绑定在表格行item上的highlight标签,而现有更新逻辑每次刷新都会删除所有旧行、创建全新行对象,旧行上绑定的标签会随行对象一起被销毁,新插入的行默认不带高亮标签,自然无法保留高亮效果。
修复方案
- 新增独立变量存储当前高亮行的唯一标识(不要把状态只存在行对象上),生产环境对接MongoDB时建议用文档自带的
_id作为唯一标识,避免行顺序变动导致高亮错位,演示场景用行序号即可。 - 鼠标悬浮触发行高亮时,同步更新这个独立存储的高亮标识。
- 每次表格刷新完成、所有新行插入完毕后,根据存储的高亮标识找到对应新行,重新绑定
highlight标签。
修复后的可运行代码:
from Tkinter import * import Tkinter import ttk as TTK root = Tk() root.configure(background="#3F3F3F") style = TTK.Style() tv = TTK.Treeview() # 独立存储当前高亮行的序号 current_highlight_row = None def highlight_row(event): global current_highlight_row tree = event.widget item = tree.identify_row(event.y) if not item: return # 清除所有现有高亮 tree.tk.call(tree, "tag", "remove", "highlight") # 给当前悬浮行加高亮 tree.tk.call(tree, "tag", "add", "highlight", item) # 同步记录高亮行的序号 current_highlight_row = int(tree.item(item, 'text')) tv.grid(row=1, column=0) tv.tag_configure('highlight', background='orange') tv.bind("<Motion>", highlight_row) style.configure("Treeview", font=(None, 12)) style.configure("Treeview", foreground= '#3F3F3F', background= '#3F3F3F', fieldbackground='#3F3F3F') tv['columns'] = ('a','b','c') tv.heading('a', text='a') tv.column('a', anchor='center', width=100) tv.heading('b', text='b') tv.column('b', anchor='center', width=175) tv.heading('c', text='c') tv.column( 'c', anchor='center', width=90) external_db = [{'x':1,'y':2, 'z':3}, {'x':4,'y':5, 'z':6}, {'x':7,'y':8, 'z':9}] def update(): global current_highlight_row try: # 清空旧行 for j in tv.get_children(): tv.delete(j) row_map = {} for j, k in enumerate(external_db): # 插入新行时记录行序号和行ID的映射 item_id = tv.insert('', 'end', text=str(j), values=(k['x'],k['y'],k['z']) ,tags = ('col')) row_map[j] = item_id tv.tag_configure('col', foreground = 'white') # 刷新完成后恢复高亮 if current_highlight_row is not None and current_highlight_row in row_map: tv.tk.call(tv, "tag", "remove", "highlight") tv.tk.call(tv, "tag", "add", "highlight", row_map[current_highlight_row]) except Exception, e: print e, 'Update Exception' pass root.after(1500,update) update() root.mainloop()
优化建议
- 不要每次刷新全量删除重建所有行:数据量较大时全量重绘性能很差,可以提前对比本地已有数据和最新拉取的MongoDB数据,仅对内容有变动、新增、删除的行做增量更新,从根源上减少状态丢失的概率。
- 高亮匹配优先用业务唯一标识:不要依赖行序号、行ID这类会随重绘变动的属性,用MongoDB文档
_id这类固定唯一值匹配,避免数据排序、插入新行后高亮错位到其他行。
内容的提问来源于stack exchange,提问作者ajsp
相关产品推荐
相关产品推荐

