Search code examples
pythontkinteroptimizationmemory-management

usage memory with widget.destroy()


first of all, sorry for my english communication skills, I am learning it. I have a little question about the widget.destroy() in Tkinter. I read that if you want destroy a widget, destroy = remove from memory because you are not going to reuse, you should use this function. But I am using and they are not removed from the memory, I saw it when I create news widgets and appear with the next numbers.

My code create only 10 differents buttons and, like you can see in the screenshots, I have 20 buttons. This is because I make twice click in the button. My question is not how I can remove/hide and create/show it,my question is for the usage memory and make it optimized. I want understand the logic behind it.

This is my code, it is not perfect but it's a little test, with some screenshots:

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>>

Solution

  • Your code only prints things in the clean_frame function. Every time the function is called it prints the root's content.

    Your output looks like that not because the .destroy() isn't working properly, but because the function is printing at the end of your output (where the last function finished printing) resulting a really long list.

    I only printed the the number of widgets in the root (in the beginning of your clean_frame function) so the output would be more readable and so you could follow the widgets in your app.

    You received that many widgets in your output because you didn't format the output at all, so the output is from all the clicking you have made.

    The modified function:

        def clean_frame( self):
            print(len(self.winfo_children()))
            for widget in self.winfo_children():
                print( widget.winfo_id)
                if not ('geometria' in str(widget) or 'calculadora' in str(widget)):
                    widget.destroy()
    

    This is what my output looks like:

    2
    <bound method Misc.winfo_id of <tkinter.Button object .geometria>>
    <bound method Misc.winfo_id of <tkinter.Button object .calculadora>>
    18
    <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>>
    5
    <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>>  
    18
    ...
    

    The reason you are seeing the id keep going up is because tkinter won't create the same id twice.

    tkinter doesn't reuse the ID of a destroyed object for a new one. Instead, it generates a new, unique ID.