Mailing List Archive

Tkinter long-running window freezes
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
--
https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter long-running window freezes [ In reply to ]
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
Re: Tkinter long-running window freezes [ In reply to ]
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 [ In reply to ]
On Wed, 24 Feb 2021 13:07:24 +0000
MRAB <python@mrabarnett.plus.com> 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 [ In reply to ]
On Wed, 24 Feb 2021 11:03:30 -0500
Terry Reedy <tjreedy@udel.edu> 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 [ In reply to ]
On 2021-02-24 23:23, John O'Hagan wrote:
> On Wed, 24 Feb 2021 13:07:24 +0000
> MRAB <python@mrabarnett.plus.com> 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 [ In reply to ]
On Thu, 25 Feb 2021 00:27:33 +0000
MRAB <python@mrabarnett.plus.com> 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 [ In reply to ]
On 2/24/2021 6:53 PM, John O'Hagan wrote:
> On Wed, 24 Feb 2021 11:03:30 -0500
> Terry Reedy <tjreedy@udel.edu> 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 [ In reply to ]
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 [ In reply to ]
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()*10000000)}
}

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 [ In reply to ]
On Thu, 25 Feb 2021 09:54:15 -0500
Terry Reedy <tjreedy@udel.edu> wrote:

> On 2/24/2021 6:53 PM, John O'Hagan wrote:
> > On Wed, 24 Feb 2021 11:03:30 -0500
> > Terry Reedy <tjreedy@udel.edu> 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 [ In reply to ]
On Thu, 25 Feb 2021 11:06:05 -0500
Richard Damon <Richard@Damon-Family.org> 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 [ In reply to ]
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()*10000000)}
> }
>
> 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 [ In reply to ]
On Thu, 25 Feb 2021 21:57:19 +0100
Christian Gollwitzer <auriocus@gmx.de> 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()*10000000)}
> }
>
> 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 [ In reply to ]
Am 26.02.21 um 06:15 schrieb John O'Hagan:
> On Thu, 25 Feb 2021 21:57:19 +0100
> Christian Gollwitzer <auriocus@gmx.de> 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()*10000000)}
>> }
>>
>> 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()*10000000)}
}

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 [ In reply to ]
On Fri, 26 Feb 2021 08:19:14 +0100
Christian Gollwitzer <auriocus@gmx.de> 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/1173484fffffffffffff

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()*10000000)}
> }
>
> 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 [ In reply to ]
On 2021-02-26 23:59, John O'Hagan wrote:
> On Fri, 26 Feb 2021 08:19:14 +0100
> Christian Gollwitzer <auriocus@gmx.de> 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/1173484fffffffffffff
>
> 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()*10000000)}
>> }
>>
>> 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 [ In reply to ]
On Sat, 27 Feb 2021 01:06:06 +0000
MRAB <python@mrabarnett.plus.com> wrote:

> On 2021-02-26 23:59, John O'Hagan wrote:
> > On Fri, 26 Feb 2021 08:19:14 +0100
> > Christian Gollwitzer <auriocus@gmx.de> 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/1173484fffffffffffff
> >
> > 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()*10000000)}
> >> }
> >>
> >> 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 [ In reply to ]
On Sat, 27 Feb 2021 10:59:24 +1100
John O'Hagan <research@johnohagan.com> wrote:

> On Fri, 26 Feb 2021 08:19:14 +0100
> Christian Gollwitzer <auriocus@gmx.de> 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()*10000000)}
> > }
> >
> > 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 [ In reply to ]
On 2021-02-27 02:38, John O'Hagan wrote:
> On Sat, 27 Feb 2021 01:06:06 +0000
> MRAB <python@mrabarnett.plus.com> wrote:
>
>> On 2021-02-26 23:59, John O'Hagan wrote:
>> > On Fri, 26 Feb 2021 08:19:14 +0100
>> > Christian Gollwitzer <auriocus@gmx.de> 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/1173484fffffffffffff
>> >
>> > 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()*10000000)}
>> >> }
>> >>
>> >> 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 [ In reply to ]
On Wed, 24 Feb 2021 22:35:32 +1100
John O'Hagan <research@johnohagan.com> 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