I have an example Python program that sets the text of a tkinter label, and the use of Label.config(text=string) is leaking memory. Other techniques for setting the text don't. Is something wrong here?
I was asked to look at a Python program using tkinter that was leaking memory. I've traced this to its use of Label.config(text=string) to set the text of a label in a watchdog routine that runs frequently. I'm not a Python expert, but there is example code for this on the very useful GeeksforGeeks website at https://www.geeksforgeeks.org/how-to-change-the-tkinter-label-text I assume this code should be reasonably OK, but it seems to demonstrate the leak too.
I have a slightly modified version of that example code with a tracemalloc() call to show the leak. If you run this as shown below, each time you click on the button it resets the text of the label (after the first click it just keeps setting to the same message, but that doesn't matter here). I added the code to print out the memory usage, and it simply goes up and up each time you click and it executes the line:
my_label.config(text = my_text)
Now if you comment out that line and use
my_label["text"] = my_text
it no longer leaks memory. Using a text variable works too.
Obviously, there are potential solutions here, but I'd like to understand why this happens. I'm running this on OS X, but I'm told the same thing works on Windows and on the Raspberry Pi used for the original program, where memory is at more of a premium. It seems connected with garbage collection, because if I import gc and add code like
if (tracemalloc.get_traced_memory()[0] > 2000) : gc.collect()
then it memory leakage gets capped at that 2000 value.
Here's my example program: all I've done is import tracemalloc, add the call to print the memory usage and add the alternative call to set the label text. Both calls set the label text as intended, but one leaks and the other doesn't. Is something wrong here?
# importing everything from tkinter
from tkinter import *
import tracemalloc
# creating the tkinter window
Main_window = Tk()
# variable
my_text = "GeeksforGeeks updated !!!"
# function define for
# updating the my_label
# widget content
def counter():
# use global variable
global my_text
# configure - one of these leaks, the other doesn't...
my_label.config(text = my_text)
#my_label["text"] = my_text
print (tracemalloc.get_traced_memory()[0])
# create a button widget and attached
# with counter function
my_button = Button(Main_window,
text = "Please update",
command = counter)
# create a Label widget
my_label = Label(Main_window,
text = "geeksforgeeks")
# place the widgets
# in the gui window
my_label.pack()
my_button.pack()
tracemalloc.start()
# Start the GUI
Main_window.mainloop()
my_label["text']is just syntactic sugar. Eventually, tkinter will call the configure method on the object.__setitem__for a tkinter widget directly calls the configure method._configure) method.my_label["text"] = my_text(ormy_label.configure({"text": my_text})) passes them as a dictionary incnf, whilemy_label.configure(text=my_text)passes them inkwinstead. This leads to an extra step in_cnfmergewhere an extra dictcnfis created.dictobject for a while which should become a real problem.