如何更新Tkinter按钮且避免内存泄漏?
问题
每次执行display_names(gender)时,gender_selected的值会决定哪些名称按钮被禁用。似乎该函数之前调用所占用的内存从未被释放,请问此判断是否正确?若正确,如何编写代码避免内存泄漏?
以下是示例代码:
from tkinter import * import tkinter as tk root = Tk() gender_selected = tk.StringVar() gender_selected.set('M') def display_names(gender): if gender == 'M': male_state = ACTIVE female_state = DISABLED else: male_state = DISABLED female_state = ACTIVE buttn1 = Radiobutton(root, text = 'Bill', value = 'Bill', state = male_state) buttn1.place(x=10, y=110) buttn2 = Radiobutton(root, text = 'Bob', value = 'Bob', state = male_state) buttn2.place(x=10, y=140) buttn3 = Radiobutton(root, text = 'Mary', value = 'Mary', state = female_state) buttn3.place(x=10, y=170) buttn4 = Radiobutton(root, text = 'Sue', value = 'Sue', state = female_state) buttn4.place(x=10, y=200) if gender == 'M': buttn1.select() else: buttn3.select() Radiobutton(root, text="Male", variable = gender_selected, value = 'M', command = lambda: display_names(gender_selected.get())).place(x=10, y=30) Radiobutton(root, text="Female", variable = gender_selected, value = 'F', command = lambda: display_names(gender_selected.get())).place(x=10, y=60) display_names(gender_selected.get()) root.geometry("150x250") mainloop()
回答
你的判断是正确的。每次调用display_names时,都会重新创建8个Radiobutton控件(4个名称按钮+2个性别按钮),这些旧控件虽然被新控件在视觉上覆盖,但仍然被父组件root持有引用,Python垃圾回收机制无法回收这些无用控件,最终导致内存泄漏。
解决思路
不要每次调用函数都创建新控件,而是提前创建所有需要的控件,在函数中仅修改控件的状态和选中状态,具体实现如下:
from tkinter import * import tkinter as tk root = Tk() root.geometry("150x250") gender_selected = tk.StringVar() gender_selected.set('M') # 提前创建所有名称单选按钮 buttn1 = Radiobutton(root, text='Bill', value='Bill') buttn1.place(x=10, y=110) buttn2 = Radiobutton(root, text='Bob', value='Bob') buttn2.place(x=10, y=140) buttn3 = Radiobutton(root, text='Mary', value='Mary') buttn3.place(x=10, y=170) buttn4 = Radiobutton(root, text='Sue', value='Sue') buttn4.place(x=10, y=200) # 提前创建性别单选按钮,仅创建一次 Radiobutton(root, text="Male", variable=gender_selected, value='M', command=lambda: display_names(gender_selected.get())).place(x=10, y=30) Radiobutton(root, text="Female", variable=gender_selected, value='F', command=lambda: display_names(gender_selected.get())).place(x=10, y=60) def display_names(gender): if gender == 'M': male_state = ACTIVE female_state = DISABLED buttn1.select() else: male_state = DISABLED female_state = ACTIVE buttn3.select() # 仅修改现有控件的状态 buttn1.config(state=male_state) buttn2.config(state=male_state) buttn3.config(state=female_state) buttn4.config(state=female_state) # 初始化控件状态 display_names(gender_selected.get()) mainloop()
额外优化:分组管理控件
可以将同类型按钮分组存储,让状态修改代码更简洁:
from tkinter import * import tkinter as tk root = Tk() root.geometry("150x250") gender_selected = tk.StringVar() gender_selected.set('M') # 分组创建并布局按钮 male_buttons = [ Radiobutton(root, text='Bill', value='Bill'), Radiobutton(root, text='Bob', value='Bob') ] female_buttons = [ Radiobutton(root, text='Mary', value='Mary'), Radiobutton(root, text='Sue', value='Sue') ] for idx, btn in enumerate(male_buttons): btn.place(x=10, y=110 + idx*30) for idx, btn in enumerate(female_buttons): btn.place(x=10, y=170 + idx*30) # 性别按钮仅创建一次 Radiobutton(root, text="Male", variable=gender_selected, value='M', command=lambda: display_names(gender_selected.get())).place(x=10, y=30) Radiobutton(root, text="Female", variable=gender_selected, value='F', command=lambda: display_names(gender_selected.get())).place(x=10, y=60) def display_names(gender): if gender == 'M': male_state = ACTIVE female_state = DISABLED male_buttons[0].select() else: male_state = DISABLED female_state = ACTIVE female_buttons[0].select() # 批量修改按钮状态 for btn in male_buttons: btn.config(state=male_state) for btn in female_buttons: btn.config(state=female_state) display_names(gender_selected.get()) mainloop()
这种方式每次调用函数只会修改现有控件的属性,不会生成新控件,从根源上避免了内存泄漏。
内容的提问来源于stack exchange,提问作者Ted Ersek
相关产品推荐
相关产品推荐

