You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tkinter中widget.destroy()内存释放异常问题咨询

Tkinter中widget.destroy()的内存原理解析

问题描述

我正在学习英语,表达能力有限,抱歉。我对Tkinter的widget.destroy()有疑问:我知道这个函数应该销毁不再使用的组件以释放内存,但实际使用时发现新创建的组件编号一直在递增(比如按钮从!button到!button19),看起来组件好像没从内存里移除。我的代码每次只创建10个按钮,但多次点击后编号到了20。我想了解背后的内存使用与优化逻辑,不是要解决组件显示/隐藏的问题。

测试代码

import tkinter as tk
from utils.geometrIrregular import FigurasGeometricas_irregulares

minpady = 10
minpadx = 10

class Root(tk.Tk):
    def __init__( self):
        super().__init__()
        # Menu Buttons
        tk.Button( self, text = "Geometria",
                        command = lambda: self.center_geometry(),
                        width = 10, height = 5, name = 'geometria'
                        ).place( relx = .01, rely = .2)
        tk.Button( self, text = "Calculadora",
                             command = lambda: self.center_calculate(),
                             width = 10, height = 5, name = 'calculadora'
                             ).place( relx = .01, rely = .4)      
        
    def center_geometry( self) -> None:
        self.clean_frame()
        tk.Label( self, text = "Inserta las coordenadas: "
                               ).place( relx = .12, rely = .1)
        
        tk.Entry( self,
                 ).place( relx = .23, rely = .1)
        """ bind options:
            FocusIn, FocusOut, <Button-1>, <KeyPress>... -> some options
        """        
        tk.Button( self, text = "Crear polinomio"
                  ).place( relx = .2, rely = .15)        
             
    def center_calculate( self) -> None:
        """ we are going to create the tk.widgets in the central part
        """
        self.clean_frame()
        self.memory_lst = []
        self.calculate_history = tk.StringVar()
        self.value = tk.StringVar()
        self.result = tk.StringVar()
        self.memory = tk.StringVar()
        
        tk.Label( self,
                 text = 'Last operation:'
                 ).place( relx = .2, rely = .1)
        tk.Entry( self,
                background = 'light grey',
                textvariable = self.calculate_history
                ).place( relx = .25, rely = .1)
        
        tk.Label( self,
                 text = 'Result:'
                ).place( relx = .2, rely = .15)
        tk.Label( self,
                 background = 'snow',
                 textvariable = self.result
                ).place( relx = .25, rely = .15)
        
        tk.Label( self,
                 text = 'Operation:'
                ).place( relx = .20, rely = .2)
        tk.Entry( self,
                 background = 'white',
                 textvariable = self.value
                ).place( relx = .25, rely = .2)
        
        # First buttons row
        tk.Button( self, text = " + "
                  ).place( relx = .25, rely = .25)
        tk.Button( self, text = " - "
                  ).place( relx = .27, rely = .25)
        tk.Button( self, text = " / "
                  ).place( relx = .29, rely = .25)
        tk.Button( self, text = " * "
                  ).place( relx = .31, rely = .25)
        
        # Second buttons row
        tk.Button( self, text = 'M+'
                            ).place( relx = .25, rely = .28)
        tk.Button( self, text = 'M'
                            ).place( relx = .27, rely = .28)
        tk.Button( self, text = 'M-'
                            ).place( relx = .29, rely = .28)
        tk.Button( self, text = ' ^ '
                            ).place( relx = .31, rely = .28)
        
        # Thrid buttons row
        tk.Button( self, text = ' √ '
                            ).place( relx = .25, rely = .31)
        # Memory Lst_Box
        self.memory_lstbox = tk.Listbox( self, background = 'white', listvariable = self.memory,
                              width = 50, height = 50, selectmode = tk.SINGLE
                              ).place( relx = .6, rely = .1)
    # CLEAN BLOCK
    def clean_frame( self):
        for widget in self.winfo_children():
            print( widget.winfo_id)
            if not ('geometria' in str(widget) or 'calculadora' in str(widget)):
                widget.destroy()
                
            
if __name__ == "__main__":
    App = Root()
    App.mainloop()

运行输出

<bound method Misc.winfo_id of <tkinter.Button object .geometria>>
<bound method Misc.winfo_id of <tkinter.Button object .calculadora>>
<bound method Misc.winfo_id of <tkinter.Button object .geometria>>
<bound method Misc.winfo_id of <tkinter.Button object .calculadora>>
<bound method Misc.winfo_id of <tkinter.Label object .!label>>
<bound method Misc.winfo_id of <tkinter.Entry object .!entry>>
<bound method Misc.winfo_id of <tkinter.Label object .!label2>>
<bound method Misc.winfo_id of <tkinter.Label object .!label3>>
<bound method Misc.winfo_id of <tkinter.Label object .!label4>>
<bound method Misc.winfo_id of <tkinter.Entry object .!entry2>>
<bound method Misc.winfo_id of <tkinter.Button object .!button>>
<bound method Misc.winfo_id of <tkinter.Button object .!button2>>
<bound method Misc.winfo_id of <tkinter.Button object .!button3>>
<bound method Misc.winfo_id of <tkinter.Button object .!button4>>
<bound method Misc.winfo_id of <tkinter.Button object .!button5>>
<bound method Misc.winfo_id of <tkinter.Button object .!button6>>
<bound method Misc.winfo_id of <tkinter.Button object .!button7>>
<bound method Misc.winfo_id of <tkinter.Button object .!button8>>
<bound method Misc.winfo_id of <tkinter.Button object .!button9>>
<bound method Misc.winfo_id of <tkinter.Listbox object .!listbox>>
<bound method Misc.winfo_id of <tkinter.Button object .geometria>>
<bound method Misc.winfo_id of <tkinter.Button object .calculadora>>
<bound method Misc.winfo_id of <tkinter.Label object .!label5>>
<bound method Misc.winfo_id of <tkinter.Entry object .!entry3>>
<bound method Misc.winfo_id of <tkinter.Button object .!button10>>
<bound method Misc.winfo_id of <tkinter.Button object .geometria>>
<bound method Misc.winfo_id of <tkinter.Button object .calculadora>>
<bound method Misc.winfo_id of <tkinter.Label object .!label6>>
<bound method Misc.winfo_id of <tkinter.Entry object .!entry4>>
<bound method Misc.winfo_id of <tkinter.Label object .!label7>>
<bound method Misc.winfo_id of <tkinter.Label object .!label8>>
<bound method Misc.winfo_id of <tkinter.Label object .!label9>>
<bound method Misc.winfo_id of <tkinter.Entry object .!entry5>>
<bound method Misc.winfo_id of <tkinter.Button object .!button11>>
<bound method Misc.winfo_id of <tkinter.Button object .!button12>>
<bound method Misc.winfo_id of <tkinter.Button object .!button13>>
<bound method Misc.winfo_id of <tkinter.Button object .!button14>>
<bound method Misc.winfo_id of <tkinter.Button object .!button15>>
<bound method Misc.winfo_id of <tkinter.Button object .!button16>>
<bound method Misc.winfo_id of <tkinter.Button object .!button17>>
<bound method Misc.winfo_id of <tkinter.Button object .!button18>>
<bound method Misc.winfo_id of <tkinter.Button object .!button19>>
<bound method Misc.winfo_id of <tkinter.Listbox object .!listbox2>>

原理解析

1. 组件编号递增不代表内存未释放

你看到的.!button1、.!button10这类编号,是Tkinter内部维护的组件命名计数器,它的规则是单调递增——只要创建新组件,计数器就加1,不会因为旧组件被销毁而回退。这个编号只是Tk用来标识组件的唯一名称,和内存是否释放没有直接关系,完全不影响内存回收。

2. widget.destroy()的实际作用

destroy()做了两件核心事情:

  • 销毁Tkinter底层(C语言实现的)组件资源,包括窗口系统的控件对象、事件绑定等,这部分资源会立即被释放;
  • 断开Python层面的组件对象和Tk底层资源的关联,让Python的垃圾回收器可以处理这个对象。

但要注意:Python对象的回收需要满足没有任何引用指向它的条件。比如你代码里的self.memory_lstbox,虽然你调用了destroy(),但这个变量还持有Listbox对象的引用,Python垃圾回收器就不会回收它,直到你把self.memory_lstbox设为None或者这个Root实例被销毁。

3. 内存回收的验证方式

如果想确认内存是否真的被释放,不要看组件编号,应该用以下方法:

  • 使用Python的gc模块,统计特定类型对象的数量(比如len(gc.get_objects(types=[tk.Button]))),对比销毁前后的数量变化;
  • 用内存分析工具(如memory_profiler)监控程序的内存占用情况。

4. 你的代码里的小问题

你在clean_frame里用'geometria' in str(widget)判断是否保留组件,这个方法不可靠——因为str(widget)的输出可能随Python版本变化。正确的做法是用widget.winfo_name()直接获取组件的名称:

if widget.winfo_name() not in ('geometria', 'calculadora'):
    widget.destroy()

内容的提问来源于stack exchange,提问作者HombreconNombre

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 12:09:51