Tkinter更新多组件GUI时出现闪烁,如何实现无闪烁更新?
问题
能否在更新Frame(销毁现有组件并创建新组件)时避免闪烁?
实际应用为动态表单(数十行选项,选项会引发表单内容变化),发现异常现象:刷新相同内容时会出现闪烁,但新增组件时同样执行销毁重建操作却无闪烁。点击示例中的A、B、A、A等按钮可观察到明显闪烁,期望实现类似图片切换的即时效果。
附简化示例代码:
import tkinter as tk class App(tk.Tk): def __init__(self): super().__init__() self.geometry('400x900') self.resizable(width=0, height=0) self.grid_rowconfigure(1, weight=1) self.grid_columnconfigure(0, weight=1, uniform='a') self.grid_columnconfigure(1, weight=1, uniform='a') self.grid_columnconfigure(2, weight=1, uniform='a') tk.Button(text='A', command=lambda: self.add(20)).grid(row=0, column=0, sticky='nsew') tk.Button(text='B', command=lambda: self.add(30)).grid(row=0, column=1, sticky='nsew') tk.Button(text='clear', command=lambda: self.add(0)).grid(row=0, column=2, sticky='nsew') self.form = tk.Frame() self.form.grid_columnconfigure(0, weight=1) self.form.grid_columnconfigure(1, weight=1) self.form.grid(row=1, column=0, columnspan=3, sticky='nsew') self.mainloop() def add(self, n): # self.form.grid_remove() # self.update() for child in self.form.winfo_children(): child.destroy() for i in range(0, 2 * n, 2): name = f'Row {i // 2 + 1}' tk.Label(master=self.form, text=name, bg='sky blue').grid(row=i, column=0, sticky='nsew') tk.Button(master=self.form, text=name, relief='flat', bg='white').grid(row=i, column=1, sticky='nsew') tk.Frame(master=self.form, height=1, bg='black').grid(row=i + 1, column=0, columnspan=2, sticky='nsew') # self.form.grid() if __name__ == '__main__': app = App()
解决方案
方法1:预创建组件再替换,消除空窗期
闪烁的核心原因是销毁旧组件后,新组件创建完成前Frame处于空白状态,导致界面刷新时出现闪烁。可以先在临时容器中创建好所有新组件,再一次性替换原Frame:
修改add方法如下:
def add(self, n): # 创建临时Frame存放新组件 temp_form = tk.Frame() temp_form.grid_columnconfigure(0, weight=1) temp_form.grid_columnconfigure(1, weight=1) for i in range(0, 2 * n, 2): name = f'Row {i // 2 + 1}' tk.Label(master=temp_form, text=name, bg='sky blue').grid(row=i, column=0, sticky='nsew') tk.Button(master=temp_form, text=name, relief='flat', bg='white').grid(row=i, column=1, sticky='nsew') tk.Frame(master=temp_form, height=1, bg='black').grid(row=i + 1, column=0, columnspan=2, sticky='nsew') # 销毁原Frame并替换为临时Frame self.form.destroy() self.form = temp_form self.form.grid(row=1, column=0, columnspan=3, sticky='nsew')
方法2:启用双缓冲(Windows系统适用)
Windows平台下,可通过设置窗口的双缓冲属性减少闪烁,在App初始化时添加:
self.attributes('-doublebuffer', True)
方法3:复用组件而非销毁重建
如果表单结构固定,仅内容变化,复用现有组件是最彻底的解决方式,避免频繁销毁重建带来的闪烁:
def __init__(self): super().__init__() self.geometry('400x900') self.resizable(width=0, height=0) self.grid_rowconfigure(1, weight=1) self.grid_columnconfigure(0, weight=1, uniform='a') self.grid_columnconfigure(1, weight=1, uniform='a') self.grid_columnconfigure(2, weight=1, uniform='a') tk.Button(text='A', command=lambda: self.add(20)).grid(row=0, column=0, sticky='nsew') tk.Button(text='B', command=lambda: self.add(30)).grid(row=0, column=1, sticky='nsew') tk.Button(text='clear', command=lambda: self.add(0)).grid(row=0, column=2, sticky='nsew') self.form = tk.Frame() self.form.grid_columnconfigure(0, weight=1) self.form.grid_columnconfigure(1, weight=1) self.form.grid(row=1, column=0, columnspan=3, sticky='nsew') self.rows = [] # 存储行组件的引用 self.mainloop() def add(self, n): # 移除多余的行组件 while len(self.rows) > n: label, button, separator = self.rows.pop() label.destroy() button.destroy() separator.destroy() # 更新或创建行组件 for i in range(n): name = f'Row {i + 1}' if i < len(self.rows): # 复用现有组件,更新内容 label, button, _ = self.rows[i] label.config(text=name) button.config(text=name) else: # 创建新行组件 label = tk.Label(master=self.form, text=name, bg='sky blue') button = tk.Button(master=self.form, text=name, relief='flat', bg='white') separator = tk.Frame(master=self.form, height=1, bg='black') label.grid(row=2*i, column=0, sticky='nsew') button.grid(row=2*i, column=1, sticky='nsew') separator.grid(row=2*i+1, column=0, columnspan=2, sticky='nsew') self.rows.append([label, button, separator])
内容的提问来源于stack exchange,提问作者BTjacker
相关产品推荐
相关产品推荐

