Re: Tkinter long-running window freezes
On Wed, 24 Feb 2021 22:35:32 +1100 John O'Hagan wrote: > Hi list > > I have a 3.9 tkinter interface that displays data from an arbitrary > number of threads, each of which runs for an arbitrary period of time. > A frame opens in the root window when each thread starts and closes > when it stops. Widgets in the frame and the root window control the > thread and how the data is displayed. > > This works well for several hours, but over time the root window > becomes unresponsive and eventually freezes and goes grey. No error > messages are produced in the terminal. > > Here is some minimal, non-threaded code that reproduces the problem on > my system (Xfce4 on Debian testing): > > from tkinter import * > from random import randint > > root = Tk() > > def display(label): > label.destroy() > label = Label(text=randint(0, 9)) > label.pack() > root.after(100, display, label) > > display(Label()) > mainloop() > > This opens a tiny window that displays a random digit on a new label > every .1 second. (Obviously I could do this by updating the text > rather than recreating the label, but my real application has to > destroy widgets and create new ones). > > This works for 3-4 hours, but eventually the window freezes. Thanks to those that replied to this. To summarise my tentative and incomplete conclusions, it seems that tkinter doesn't like a lot of widgets being created and destroyed in a long running process and will/may eventually freeze if this is done, for a reason I haven't been able to fathom. Tk internally keeps names of all widgets created, and this will cause a slow memory-use increase if not prevented by manually re-using widget names, but this is not the cause of the freezing. Here is a simplified version of how I worked around the issue by recycling widgets: from tkinter import * class WidgetHandler: "Reuse tkinter widgets" widget_cache = [] def __init__(self, main_window, *args): self.datastream = MyDataStream(*args) self.main_window = main_window if self.widget_cache: self.widget = self.widget_cache.pop() self.clean(self.widget) else: self.widget = self.new_widget() #add functionality to widgets, e.g: self.widget.var.traceid = self.widget.var.trace('w', self.meth1) self.widget.button.configure(command=self.meth2) self.widget.button.pack() #etc self.widget.pack() def clean(self, widget): "Remove traces, clear text boxes, reset button states etc" #e.g: self.widget.var.trace_remove('write', var.traceid) self.widget.button['state'] = 'normal' #etc def new_widget(self): "Create widget and children without adding any commands, traces etc" widget = Frame(self.main_window) #e.g: widget.var = StringVar() widget.button = Checkbutton(widget, variable=widget.var) #etc return widget def meth1(self, *e): pass #do something with self.datastream def meth2(self): pass #do something else with self.datastream #etc def quit(self): self.datastream.quit() self.widget.pack_forget() self.widget_cache.append(self.widget) -- https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter long-running window freezes
On 2021-02-27 02:38, John O'Hagan wrote: On Sat, 27 Feb 2021 01:06:06 + MRAB wrote: On 2021-02-26 23:59, John O'Hagan wrote: > On Fri, 26 Feb 2021 08:19:14 +0100 > Christian Gollwitzer wrote: > >> Am 26.02.21 um 06:15 schrieb John O'Hagan: > [...] >> > >> > I've followed your suggestions as per my last post, and can >> > confirm the same freezing behaviour when running your code >> > directly as a tclsh script on Debian Testing, Tcl 8.6.11. >> >> You might report this as a bug to the Tcl bugtracker >> https://core.tcl-lang.org/tk/ticket >> >> I guess the problem is with the steady creation of widgets. Tk was >> not meant to be used like that. Tkinter creates new widget names >> for each widget with random numbers, just like the Tcl code above >> does, whereas in a usual Tcl/Tk program the names are given by the >> programmer. > > Thanks, I will make the bug report. However, based on your comments > above, it looks similar to this one, closed as invalid 16 years ago: > > https://core.tcl-lang.org/tk/tktview/1173484f > > This was also related to memory "creep" caused by Tk's cache of > names, which AIUI is a Tk design feature (but I don't know Tk!). > >> Can you also check this program, which reuses the same widget path >> name, albeit does the creation/destruction in cycles: >> >> == >> package require Tk >> >> proc randint {} { >> expr {int(rand()*1000)} >> } >> >> proc display {label} { >> destroy $label >> set label [label .l -text [randint]] >> pack $label >> after 100 [list display $label] >> } >> >> display [label .l] >> >> > > I have tried this overnight and it is still running, not frozen and > with no apparent increase in memory use. I guess that is likely the > issue. I don't know Tcl/Tk - is there a way to emulate the above > approach of re-using the widget name in tkinter? > >> As mentioned by others, typically you wouldn't continuously >> recreate new widgets, but either update the text of the widget >> (label['text']="New text") or attaching a StringVar() ) >> >> or, if you must rearrange the widgets, you pack_forget() them and >> then repack them. >> >> Christian > > This is possible of course, but will require more than a repack. In > my use case, each widget is an attribute of a Python object, > intended control and display data about that object, and there is an > indeterminate number of such objects at any given time. I had > assumed I could just destroy the widget and let the object go out of > scope to be garbage collected. I'll need to redesign this > altogether if I can't rely on Tk to manage memory. > > IMHO it's quite surprising if .destroy doesn't free all the > resources used by a widget! > I've look in Lib\tkinter\__init__.py and it appears that you can give it a name, so: from tkinter import * from random import randint root = Tk() def display(label): label.destroy() label = Label(name='my_label', text=randint(0, 9)) label.pack() root.after(1, display, label) display(Label(name='my_label')) mainloop() When I do that I'm not seeing a memory rise. I just did the exact same thing, also saw no memory rise, but the window still froze after a couple of hours. Did your window freeze? Maybe the memory rise and the freeze are unrelated after all. Also, I was mistaken about Christian's second version of the Tcl code above - there is no memory rise but the window also freezes after a while. Suggests the problem is in Tcl/Tk. I didn't run it for that long, only long enough to compare it with the previous version. (Both were started at the same time.) -- https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter long-running window freezes
On Sat, 27 Feb 2021 10:59:24 +1100 John O'Hagan wrote: > On Fri, 26 Feb 2021 08:19:14 +0100 > Christian Gollwitzer wrote: [...] > > > Can you also check this program, which reuses the same widget path > > name, albeit does the creation/destruction in cycles: > > > > == > > package require Tk > > > > proc randint {} { > > expr {int(rand()*1000)} > > } > > > > proc display {label} { > > destroy $label > > set label [label .l -text [randint]] > > pack $label > > after 100 [list display $label] > > } > > > > display [label .l] > > > > > > I have tried this overnight and it is still running, not frozen and > with no apparent increase in memory use. [...] Correction! The window did freeze after all, and again on a second attempt. No memory rise though, so those two issues seem to be independent. Thanks -- John -- https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter long-running window freezes
On Sat, 27 Feb 2021 01:06:06 + MRAB wrote: > On 2021-02-26 23:59, John O'Hagan wrote: > > On Fri, 26 Feb 2021 08:19:14 +0100 > > Christian Gollwitzer wrote: > > > >> Am 26.02.21 um 06:15 schrieb John O'Hagan: > > [...] > >> > > >> > I've followed your suggestions as per my last post, and can > >> > confirm the same freezing behaviour when running your code > >> > directly as a tclsh script on Debian Testing, Tcl 8.6.11. > >> > >> You might report this as a bug to the Tcl bugtracker > >> https://core.tcl-lang.org/tk/ticket > >> > >> I guess the problem is with the steady creation of widgets. Tk was > >> not meant to be used like that. Tkinter creates new widget names > >> for each widget with random numbers, just like the Tcl code above > >> does, whereas in a usual Tcl/Tk program the names are given by the > >> programmer. > > > > Thanks, I will make the bug report. However, based on your comments > > above, it looks similar to this one, closed as invalid 16 years ago: > > > > https://core.tcl-lang.org/tk/tktview/1173484f > > > > This was also related to memory "creep" caused by Tk's cache of > > names, which AIUI is a Tk design feature (but I don't know Tk!). > > > >> Can you also check this program, which reuses the same widget path > >> name, albeit does the creation/destruction in cycles: > >> > >> == > >> package require Tk > >> > >> proc randint {} { > >> expr {int(rand()*1000)} > >> } > >> > >> proc display {label} { > >> destroy $label > >> set label [label .l -text [randint]] > >> pack $label > >> after 100 [list display $label] > >> } > >> > >> display [label .l] > >> > >> > > > > I have tried this overnight and it is still running, not frozen and > > with no apparent increase in memory use. I guess that is likely the > > issue. I don't know Tcl/Tk - is there a way to emulate the above > > approach of re-using the widget name in tkinter? > > > >> As mentioned by others, typically you wouldn't continuously > >> recreate new widgets, but either update the text of the widget > >> (label['text']="New text") or attaching a StringVar() ) > >> > >> or, if you must rearrange the widgets, you pack_forget() them and > >> then repack them. > >> > >>Christian > > > > This is possible of course, but will require more than a repack. In > > my use case, each widget is an attribute of a Python object, > > intended control and display data about that object, and there is an > > indeterminate number of such objects at any given time. I had > > assumed I could just destroy the widget and let the object go out of > > scope to be garbage collected. I'll need to redesign this > > altogether if I can't rely on Tk to manage memory. > > > > IMHO it's quite surprising if .destroy doesn't free all the > > resources used by a widget! > > > I've look in Lib\tkinter\__init__.py and it appears that you can give > it a name, so: > > from tkinter import * > from random import randint > > root = Tk() > > def display(label): > label.destroy() > label = Label(name='my_label', text=randint(0, 9)) > label.pack() > root.after(1, display, label) > > display(Label(name='my_label')) > mainloop() > > When I do that I'm not seeing a memory rise. I just did the exact same thing, also saw no memory rise, but the window still froze after a couple of hours. Did your window freeze? Maybe the memory rise and the freeze are unrelated after all. Also, I was mistaken about Christian's second version of the Tcl code above - there is no memory rise but the window also freezes after a while. Suggests the problem is in Tcl/Tk. Thanks -- John -- https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter long-running window freezes
On 2021-02-26 23:59, John O'Hagan wrote: On Fri, 26 Feb 2021 08:19:14 +0100 Christian Gollwitzer wrote: Am 26.02.21 um 06:15 schrieb John O'Hagan: [...] > > I've followed your suggestions as per my last post, and can confirm > the same freezing behaviour when running your code directly as a > tclsh script on Debian Testing, Tcl 8.6.11. You might report this as a bug to the Tcl bugtracker https://core.tcl-lang.org/tk/ticket I guess the problem is with the steady creation of widgets. Tk was not meant to be used like that. Tkinter creates new widget names for each widget with random numbers, just like the Tcl code above does, whereas in a usual Tcl/Tk program the names are given by the programmer. Thanks, I will make the bug report. However, based on your comments above, it looks similar to this one, closed as invalid 16 years ago: https://core.tcl-lang.org/tk/tktview/1173484f This was also related to memory "creep" caused by Tk's cache of names, which AIUI is a Tk design feature (but I don't know Tk!). Can you also check this program, which reuses the same widget path name, albeit does the creation/destruction in cycles: == package require Tk proc randint {} { expr {int(rand()*1000)} } proc display {label} { destroy $label set label [label .l -text [randint]] pack $label after 100 [list display $label] } display [label .l] I have tried this overnight and it is still running, not frozen and with no apparent increase in memory use. I guess that is likely the issue. I don't know Tcl/Tk - is there a way to emulate the above approach of re-using the widget name in tkinter? As mentioned by others, typically you wouldn't continuously recreate new widgets, but either update the text of the widget (label['text']="New text") or attaching a StringVar() ) or, if you must rearrange the widgets, you pack_forget() them and then repack them. Christian This is possible of course, but will require more than a repack. In my use case, each widget is an attribute of a Python object, intended control and display data about that object, and there is an indeterminate number of such objects at any given time. I had assumed I could just destroy the widget and let the object go out of scope to be garbage collected. I'll need to redesign this altogether if I can't rely on Tk to manage memory. IMHO it's quite surprising if .destroy doesn't free all the resources used by a widget! I've look in Lib\tkinter\__init__.py and it appears that you can give it a name, so: from tkinter import * from random import randint root = Tk() def display(label): label.destroy() label = Label(name='my_label', text=randint(0, 9)) label.pack() root.after(1, display, label) display(Label(name='my_label')) mainloop() When I do that I'm not seeing a memory rise. -- https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter long-running window freezes
On Fri, 26 Feb 2021 08:19:14 +0100 Christian Gollwitzer wrote: > Am 26.02.21 um 06:15 schrieb John O'Hagan: [...] > > > > I've followed your suggestions as per my last post, and can confirm > > the same freezing behaviour when running your code directly as a > > tclsh script on Debian Testing, Tcl 8.6.11. > > You might report this as a bug to the Tcl bugtracker > https://core.tcl-lang.org/tk/ticket > > I guess the problem is with the steady creation of widgets. Tk was > not meant to be used like that. Tkinter creates new widget names for > each widget with random numbers, just like the Tcl code above does, > whereas in a usual Tcl/Tk program the names are given by the > programmer. Thanks, I will make the bug report. However, based on your comments above, it looks similar to this one, closed as invalid 16 years ago: https://core.tcl-lang.org/tk/tktview/1173484f This was also related to memory "creep" caused by Tk's cache of names, which AIUI is a Tk design feature (but I don't know Tk!). > Can you also check this program, which reuses the same widget path > name, albeit does the creation/destruction in cycles: > > == > package require Tk > > proc randint {} { > expr {int(rand()*1000)} > } > > proc display {label} { > destroy $label > set label [label .l -text [randint]] > pack $label > after 100 [list display $label] > } > > display [label .l] > > I have tried this overnight and it is still running, not frozen and with no apparent increase in memory use. I guess that is likely the issue. I don't know Tcl/Tk - is there a way to emulate the above approach of re-using the widget name in tkinter? > As mentioned by others, typically you wouldn't continuously recreate > new widgets, but either update the text of the widget > (label['text']="New text") or attaching a StringVar() ) > > or, if you must rearrange the widgets, you pack_forget() them and > then repack them. > > Christian This is possible of course, but will require more than a repack. In my use case, each widget is an attribute of a Python object, intended control and display data about that object, and there is an indeterminate number of such objects at any given time. I had assumed I could just destroy the widget and let the object go out of scope to be garbage collected. I'll need to redesign this altogether if I can't rely on Tk to manage memory. IMHO it's quite surprising if .destroy doesn't free all the resources used by a widget! Thanks -- John -- https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter long-running window freezes
Am 26.02.21 um 06:15 schrieb John O'Hagan: On Thu, 25 Feb 2021 21:57:19 +0100 Christian Gollwitzer wrote: I think it is not yet clear, if this is a bug in Tkinter or in Tcl/Tk, the underlying scripting language. It might also be platform dependent. Are you on Windows? Here is an equivalent Tcl program: == package require Tk proc randint {} { expr {int(rand()*1000)} } proc display {label} { destroy $label set id [randint] set label [label .l$id -text [randint]] pack $label after 100 [list display $label] } display [label .l] Can you run this and check that the freeze also occurs? If you can't execute the Tcl that is used by Python directly, you may also do something like root = Tk() root.eval('Here comes the Tcl code') root.mainloop() Can you also find out what version of Tcl/Tk you are using? Try root.eval('info patchlevel') Christian I've followed your suggestions as per my last post, and can confirm the same freezing behaviour when running your code directly as a tclsh script on Debian Testing, Tcl 8.6.11. You might report this as a bug to the Tcl bugtracker https://core.tcl-lang.org/tk/ticket I guess the problem is with the steady creation of widgets. Tk was not meant to be used like that. Tkinter creates new widget names for each widget with random numbers, just like the Tcl code above does, whereas in a usual Tcl/Tk program the names are given by the programmer. Can you also check this program, which reuses the same widget path name, albeit does the creation/destruction in cycles: == package require Tk proc randint {} { expr {int(rand()*1000)} } proc display {label} { destroy $label set label [label .l -text [randint]] pack $label after 100 [list display $label] } display [label .l] As mentioned by others, typically you wouldn't continuously recreate new widgets, but either update the text of the widget (label['text']="New text") or attaching a StringVar() ) or, if you must rearrange the widgets, you pack_forget() them and then repack them. Christian -- https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter long-running window freezes
On Thu, 25 Feb 2021 21:57:19 +0100 Christian Gollwitzer wrote: > Am 24.02.21 um 12:35 schrieb John O'Hagan: > > Hi list > > > > I have a 3.9 tkinter interface that displays data from an arbitrary > > number of threads, each of which runs for an arbitrary period of > > time. A frame opens in the root window when each thread starts and > > closes when it stops. Widgets in the frame and the root window > > control the thread and how the data is displayed. > > > > This works well for several hours, but over time the root window > > becomes unresponsive and eventually freezes and goes grey. No error > > messages are produced in the terminal. > > > > Here is some minimal, non-threaded code that reproduces the problem > > on my system (Xfce4 on Debian testing): > > > > from tkinter import * > > from random import randint > > > > root = Tk() > > > > def display(label): > > label.destroy() > > label = Label(text=randint(0, 9)) > > label.pack() > > root.after(100, display, label) > > > > display(Label()) > > mainloop() > > > > This opens a tiny window that displays a random digit on a new label > > every .1 second. (Obviously I could do this by updating the text > > rather than recreating the label, but my real application has to > > destroy widgets and create new ones). > > > > This works for 3-4 hours, but eventually the window freezes. > > > I think it is not yet clear, if this is a bug in Tkinter or in > Tcl/Tk, the underlying scripting language. It might also be platform > dependent. Are you on Windows? Here is an equivalent Tcl program: > > == > package require Tk > > proc randint {} { > expr {int(rand()*1000)} > } > > proc display {label} { > destroy $label > set id [randint] > set label [label .l$id -text [randint]] > pack $label > after 100 [list display $label] > } > > display [label .l] > > > > Can you run this and check that the freeze also occurs? If you can't > execute the Tcl that is used by Python directly, you may also do > something like > > > root = Tk() > root.eval('Here comes the Tcl code') > root.mainloop() > > Can you also find out what version of Tcl/Tk you are using? Try > > root.eval('info patchlevel') > > Christian > I've followed your suggestions as per my last post, and can confirm the same freezing behaviour when running your code directly as a tclsh script on Debian Testing, Tcl 8.6.11. I took the liberty of reducing the after delay to 10 milliseconds, which as MRAB suggests makes the issue more obvious. (If I use 1 millisecond the numbers don't display). Over the five hours or so it was running, the memory use increased from about 17 Mb to around 60. Thanks -- John -- https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter long-running window freezes
On 2021-02-25 20:57, Christian Gollwitzer wrote: Am 24.02.21 um 12:35 schrieb John O'Hagan: Hi list I have a 3.9 tkinter interface that displays data from an arbitrary number of threads, each of which runs for an arbitrary period of time. A frame opens in the root window when each thread starts and closes when it stops. Widgets in the frame and the root window control the thread and how the data is displayed. This works well for several hours, but over time the root window becomes unresponsive and eventually freezes and goes grey. No error messages are produced in the terminal. Here is some minimal, non-threaded code that reproduces the problem on my system (Xfce4 on Debian testing): from tkinter import * from random import randint root = Tk() def display(label): label.destroy() label = Label(text=randint(0, 9)) label.pack() root.after(100, display, label) display(Label()) mainloop() This opens a tiny window that displays a random digit on a new label every .1 second. (Obviously I could do this by updating the text rather than recreating the label, but my real application has to destroy widgets and create new ones). This works for 3-4 hours, but eventually the window freezes. I think it is not yet clear, if this is a bug in Tkinter or in Tcl/Tk, the underlying scripting language. It might also be platform dependent. Are you on Windows? Here is an equivalent Tcl program: == package require Tk proc randint {} { expr {int(rand()*1000)} } proc display {label} { destroy $label set id [randint] set label [label .l$id -text [randint]] pack $label after 100 [list display $label] } display [label .l] Can you run this and check that the freeze also occurs? If you can't execute the Tcl that is used by Python directly, you may also do something like root = Tk() root.eval('Here comes the Tcl code') root.mainloop() Can you also find out what version of Tcl/Tk you are using? Try root.eval('info patchlevel') I've just downloaded Tcl (Windows 10) and saw the same slow but steady rise in memory usage for that Tcl code (I reduced the timeout from 100 to 1 to speed it up). It looks like the leak, if that's what it is, is in Tcl. -- https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter long-running window freezes
On Thu, 25 Feb 2021 11:06:05 -0500 Richard Damon wrote: > On 2/24/21 6:35 AM, John O'Hagan wrote: > > Here is some minimal, non-threaded code that reproduces the problem > > on my system (Xfce4 on Debian testing): > > > > from tkinter import * > > from random import randint > > > > root = Tk() > > > > def display(label): > > label.destroy() > > label = Label(text=randint(0, 9)) > > label.pack() > > root.after(100, display, label) > > > > display(Label()) > > mainloop() > > > > This opens a tiny window that displays a random digit on a new label > > every .1 second. (Obviously I could do this by updating the text > > rather than recreating the label, but my real application has to > > destroy widgets and create new ones). > > > > This works for 3-4 hours, but eventually the window freezes. > > > > The process uses about 26 Mb of memory at first, and this gradually > > increases to around 30 or so by the time it freezes. > > > > Any ideas what could be causing this, or even how to approach > > debugging or workarounds? > > > > Thanks > > > > -- > > > > John > > One thought is that repeatedly destroying and recreating a label might > be leaking a resource. One option would be to change the code to just > update the label rather than recreating it each time. Simplest is > probably to link the Label to a StringVar instead of a fixed text and > updating the variable to change the text. You can also (I believe) go > into the Label and change the text it has with a configuration call. > Thanks for your reply. It's true that your suggested approach stops the leak (if that's what it is!) in the example code, but IMO there are valid use-cases where it's necessary (or at least desirable) to continually create new widgets and then destroy them in the course of running the application. In my use-case, the application reads YOLO object-recognition data from video, and displays data about each new object in a new frame. When the object ceases to be visible, the frame is destroyed. I suppose I could redesign, e.g. by withdrawing frames instead of destroying them, and keeping them in a pool to re-use later for new data streams as needed. I'll give that a try if I can't solve this issue. Thanks -- John -- https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter long-running window freezes
On Thu, 25 Feb 2021 09:54:15 -0500 Terry Reedy wrote: > On 2/24/2021 6:53 PM, John O'Hagan wrote: > > On Wed, 24 Feb 2021 11:03:30 -0500 > > Terry Reedy wrote: > > > >> On 2/24/2021 6:35 AM, John O'Hagan wrote: > > [...] > >> > >> I am trying this out on Windows 10, with a wider label (so I can > >> move the window) and a button that changes when pressed, and a > >> sequential counter. Will report when the Window freezes, or maybe > >> a day if not. > > Counter is up to 777000 with no problem. > > > Thank you! I've only run a few tests because of how long it takes > > to freeze, but so far it seems that the trigger for the window > > freezing is any attempt to interact with it, e.g. clicking on it, > > so try doing that from time to time. > > I clicked on Window, clicked on label, clicked on button > (collectively at least 100 times), moved window all over screen, > resized it multiple times, maximized it and restored it a couple of > times, and no problem. I conclude for not that your issue does not > occur on Windows. > > >> Plus you seem to have a memory leak and may need to replicate > >> that. > > > > Can you explain what you mean by "replicate"? > > I don't remember, so forget it. > Thanks for your efforts. So it seems to be something specific to my system, X11 or Tcl version, etc. -- https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter long-running window freezes
Am 24.02.21 um 12:35 schrieb John O'Hagan: Hi list I have a 3.9 tkinter interface that displays data from an arbitrary number of threads, each of which runs for an arbitrary period of time. A frame opens in the root window when each thread starts and closes when it stops. Widgets in the frame and the root window control the thread and how the data is displayed. This works well for several hours, but over time the root window becomes unresponsive and eventually freezes and goes grey. No error messages are produced in the terminal. Here is some minimal, non-threaded code that reproduces the problem on my system (Xfce4 on Debian testing): from tkinter import * from random import randint root = Tk() def display(label): label.destroy() label = Label(text=randint(0, 9)) label.pack() root.after(100, display, label) display(Label()) mainloop() This opens a tiny window that displays a random digit on a new label every .1 second. (Obviously I could do this by updating the text rather than recreating the label, but my real application has to destroy widgets and create new ones). This works for 3-4 hours, but eventually the window freezes. I think it is not yet clear, if this is a bug in Tkinter or in Tcl/Tk, the underlying scripting language. It might also be platform dependent. Are you on Windows? Here is an equivalent Tcl program: == package require Tk proc randint {} { expr {int(rand()*1000)} } proc display {label} { destroy $label set id [randint] set label [label .l$id -text [randint]] pack $label after 100 [list display $label] } display [label .l] Can you run this and check that the freeze also occurs? If you can't execute the Tcl that is used by Python directly, you may also do something like root = Tk() root.eval('Here comes the Tcl code') root.mainloop() Can you also find out what version of Tcl/Tk you are using? Try root.eval('info patchlevel') Christian -- https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter long-running window freezes
On 2/24/21 6:35 AM, John O'Hagan wrote: > Hi list > > I have a 3.9 tkinter interface that displays data from an arbitrary > number of threads, each of which runs for an arbitrary period of time. > A frame opens in the root window when each thread starts and closes > when it stops. Widgets in the frame and the root window control the > thread and how the data is displayed. > > This works well for several hours, but over time the root window > becomes unresponsive and eventually freezes and goes grey. No error > messages are produced in the terminal. > > Here is some minimal, non-threaded code that reproduces the problem on > my system (Xfce4 on Debian testing): > > from tkinter import * > from random import randint > > root = Tk() > > def display(label): > label.destroy() > label = Label(text=randint(0, 9)) > label.pack() > root.after(100, display, label) > > display(Label()) > mainloop() > > This opens a tiny window that displays a random digit on a new label > every .1 second. (Obviously I could do this by updating the text rather > than recreating the label, but my real application has to destroy > widgets and create new ones). > > This works for 3-4 hours, but eventually the window freezes. > > The process uses about 26 Mb of memory at first, and this gradually > increases to around 30 or so by the time it freezes. > > Any ideas what could be causing this, or even how to approach debugging > or workarounds? > > Thanks > > -- > > John One thought is that repeatedly destroying and recreating a label might be leaking a resource. One option would be to change the code to just update the label rather than recreating it each time. Simplest is probably to link the Label to a StringVar instead of a fixed text and updating the variable to change the text. You can also (I believe) go into the Label and change the text it has with a configuration call. -- Richard Damon -- https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter long-running window freezes
On 2/24/2021 6:53 PM, John O'Hagan wrote: On Wed, 24 Feb 2021 11:03:30 -0500 Terry Reedy wrote: On 2/24/2021 6:35 AM, John O'Hagan wrote: [...] I am trying this out on Windows 10, with a wider label (so I can move the window) and a button that changes when pressed, and a sequential counter. Will report when the Window freezes, or maybe a day if not. Counter is up to 777000 with no problem. Thank you! I've only run a few tests because of how long it takes to freeze, but so far it seems that the trigger for the window freezing is any attempt to interact with it, e.g. clicking on it, so try doing that from time to time. I clicked on Window, clicked on label, clicked on button (collectively at least 100 times), moved window all over screen, resized it multiple times, maximized it and restored it a couple of times, and no problem. I conclude for not that your issue does not occur on Windows. Plus you seem to have a memory leak and may need to replicate that. Can you explain what you mean by "replicate"? I don't remember, so forget it. -- Terry Jan Reedy -- https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter long-running window freezes
On Thu, 25 Feb 2021 00:27:33 + MRAB wrote: > On 2021-02-24 23:23, John O'Hagan wrote: [...] > > In case it's relevant, to clarify what I mean by "freeze": the > > window continues to display the digits indefinitely if no attempt > > is made to interact with the window, but after some hours have > > passed, if I click on the window the digits stop displaying, > > resizing causes fragmented images of the desktop to appear in the > > window, and it cannot be closed except by terminating the process > > (e.g, in a task manager). > Hmm. A memory leak perhaps? It's more noticeable if you reduce the > timeout from 100 to 1. > A workaround is to update the label's text instead: > > from tkinter import * > from random import randint > > root = Tk() > > def update(): > label.config(text=randint(0, 9)) > root.after(1, update) > > label = Label() > label.pack() > update() > mainloop() > > It's neater anyway. That does avoid the problem in the toy example AFAICT, but as I mentioned in my OP, in the real application there are multiple streams of data which come and go, and each stream is displayed and controlled by a frame containing multiple widgets. The number of streams and therefore frames varies over time, so it seems necessary to create and destroy them. I could think about redesigning that whole approach, but it seems logical to me and it really should work! Terry Reedy also suggested a memory leak, but where? Surely not in the example code? Dare I say, maybe a bug in tkinter? Thanks for your suggestions. -- john -- https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter long-running window freezes
On 2021-02-24 23:23, John O'Hagan wrote: On Wed, 24 Feb 2021 13:07:24 + MRAB wrote: On 2021-02-24 11:35, John O'Hagan wrote: [...] > > Here is some minimal, non-threaded code that reproduces the problem > on my system (Xfce4 on Debian testing): > > from tkinter import * > from random import randint > > root = Tk() > > def display(label): > label.destroy() > label = Label(text=randint(0, 9)) > label.pack() > root.after(100, display, label) > > display(Label()) > mainloop() > [...] > This works for 3-4 hours, but eventually the window freezes. > > The process uses about 26 Mb of memory at first, and this gradually > increases to around 30 or so by the time it freezes. > > Any ideas what could be causing this, or even how to approach > debugging or workarounds? > The problem might be that you're adding the label using .pack but not removing it with .pack_forget when you destroy it. Try doing both: from tkinter import * from random import randint root = Tk() def display(label): if label is not None: label.pack_forget() label.destroy() label = Label(text=randint(0, 9)) label.pack() root.after(100, display, label) display(None) mainloop() Thanks for the reply, I ran this overnight but unfortunately the result was the same. I'm not a tkinter expert at all, but I had gathered from the docs that calling destroy() on a widget was sufficient to cleanly remove the widget and all its children. Is that not the case? In case it's relevant, to clarify what I mean by "freeze": the window continues to display the digits indefinitely if no attempt is made to interact with the window, but after some hours have passed, if I click on the window the digits stop displaying, resizing causes fragmented images of the desktop to appear in the window, and it cannot be closed except by terminating the process (e.g, in a task manager). Hmm. A memory leak perhaps? It's more noticeable if you reduce the timeout from 100 to 1. A workaround is to update the label's text instead: from tkinter import * from random import randint root = Tk() def update(): label.config(text=randint(0, 9)) root.after(1, update) label = Label() label.pack() update() mainloop() It's neater anyway. -- https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter long-running window freezes
On Wed, 24 Feb 2021 11:03:30 -0500 Terry Reedy wrote: > On 2/24/2021 6:35 AM, John O'Hagan wrote: [...] > > I am trying this out on Windows 10, with a wider label (so I can move > the window) and a button that changes when pressed, and a sequential > counter. Will report when the Window freezes, or maybe a day if not. Thank you! I've only run a few tests because of how long it takes to freeze, but so far it seems that the trigger for the window freezing is any attempt to interact with it, e.g. clicking on it, so try doing that from time to time. > > from tkinter import * > > from random import randint > > > > root = Tk() > > > > def display(label): > > label.destroy() > > label = Label(text=randint(0, 9)) > > label.pack() > > root.after(100, display, label) > > > > display(Label()) > > mainloop() > > You could try with .grid instead. I used .pack in the example for simplicity, but in the real application where I first saw the problem, I am already using .grid. > > Plus you seem to have a memory leak and may need to replicate that. Can you explain what you mean by "replicate"? Each time I run the code snippet above, I get a similar gradual increase in memory use by the process. Surely it can't be that little bit of Python code that's causing it - it must be something in tkinter? Or X11? > > This works for 3-4 hours, but eventually the window freezes. > > > > The process uses about 26 Mb of memory at first, and this gradually > > increases to around 30 or so by the time it freezes. > Thanks -- John -- https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter long-running window freezes
On Wed, 24 Feb 2021 13:07:24 + MRAB wrote: > On 2021-02-24 11:35, John O'Hagan wrote: [...] > > > > Here is some minimal, non-threaded code that reproduces the problem > > on my system (Xfce4 on Debian testing): > > > > from tkinter import * > > from random import randint > > > > root = Tk() > > > > def display(label): > > label.destroy() > > label = Label(text=randint(0, 9)) > > label.pack() > > root.after(100, display, label) > > > > display(Label()) > > mainloop() > > [...] > > This works for 3-4 hours, but eventually the window freezes. > > > > The process uses about 26 Mb of memory at first, and this gradually > > increases to around 30 or so by the time it freezes. > > > > Any ideas what could be causing this, or even how to approach > > debugging or workarounds? > > > The problem might be that you're adding the label using .pack but not > removing it with .pack_forget when you destroy it. > > Try doing both: > > from tkinter import * > from random import randint > > root = Tk() > > def display(label): > if label is not None: > label.pack_forget() > label.destroy() > > label = Label(text=randint(0, 9)) > label.pack() > root.after(100, display, label) > > display(None) > mainloop() Thanks for the reply, I ran this overnight but unfortunately the result was the same. I'm not a tkinter expert at all, but I had gathered from the docs that calling destroy() on a widget was sufficient to cleanly remove the widget and all its children. Is that not the case? In case it's relevant, to clarify what I mean by "freeze": the window continues to display the digits indefinitely if no attempt is made to interact with the window, but after some hours have passed, if I click on the window the digits stop displaying, resizing causes fragmented images of the desktop to appear in the window, and it cannot be closed except by terminating the process (e.g, in a task manager). Thanks -- John -- https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter long-running window freezes
On 2/24/2021 6:35 AM, John O'Hagan wrote: Hi list I have a 3.9 tkinter interface that displays data from an arbitrary number of threads, each of which runs for an arbitrary period of time. A frame opens in the root window when each thread starts and closes when it stops. Widgets in the frame and the root window control the thread and how the data is displayed. This works well for several hours, but over time the root window becomes unresponsive and eventually freezes and goes grey. No error messages are produced in the terminal. Here is some minimal, non-threaded code that reproduces the problem on my system (Xfce4 on Debian testing): I am trying this out on Windows 10, with a wider label (so I can move the window) and a button that changes when pressed, and a sequential counter. Will report when the Window freezes, or maybe a day if not. from tkinter import * from random import randint root = Tk() def display(label): label.destroy() label = Label(text=randint(0, 9)) label.pack() root.after(100, display, label) display(Label()) mainloop() You could try with .grid instead. This opens a tiny window that displays a random digit on a new label every .1 second. (Obviously I could do this by updating the text rather than recreating the label, but my real application has to destroy widgets and create new ones). Plus you seem to have a memory leak and may need to replicate that. This works for 3-4 hours, but eventually the window freezes. The process uses about 26 Mb of memory at first, and this gradually increases to around 30 or so by the time it freezes. -- Terry Jan Reedy -- https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter long-running window freezes
On 2021-02-24 11:35, John O'Hagan wrote: Hi list I have a 3.9 tkinter interface that displays data from an arbitrary number of threads, each of which runs for an arbitrary period of time. A frame opens in the root window when each thread starts and closes when it stops. Widgets in the frame and the root window control the thread and how the data is displayed. This works well for several hours, but over time the root window becomes unresponsive and eventually freezes and goes grey. No error messages are produced in the terminal. Here is some minimal, non-threaded code that reproduces the problem on my system (Xfce4 on Debian testing): from tkinter import * from random import randint root = Tk() def display(label): label.destroy() label = Label(text=randint(0, 9)) label.pack() root.after(100, display, label) display(Label()) mainloop() This opens a tiny window that displays a random digit on a new label every .1 second. (Obviously I could do this by updating the text rather than recreating the label, but my real application has to destroy widgets and create new ones). This works for 3-4 hours, but eventually the window freezes. The process uses about 26 Mb of memory at first, and this gradually increases to around 30 or so by the time it freezes. Any ideas what could be causing this, or even how to approach debugging or workarounds? The problem might be that you're adding the label using .pack but not removing it with .pack_forget when you destroy it. Try doing both: from tkinter import * from random import randint root = Tk() def display(label): if label is not None: label.pack_forget() label.destroy() label = Label(text=randint(0, 9)) label.pack() root.after(100, display, label) display(None) mainloop() -- https://mail.python.org/mailman/listinfo/python-list