Re: Tkinter long-running window freezes

2021-03-02 Thread John O'Hagan
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

2021-02-26 Thread MRAB

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

2021-02-26 Thread John O'Hagan
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

2021-02-26 Thread John O'Hagan
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

2021-02-26 Thread MRAB

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

2021-02-26 Thread John O'Hagan
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

2021-02-25 Thread Christian Gollwitzer

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

2021-02-25 Thread John O'Hagan
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

2021-02-25 Thread MRAB

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

2021-02-25 Thread John O'Hagan
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

2021-02-25 Thread John O'Hagan
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

2021-02-25 Thread Christian Gollwitzer

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

2021-02-25 Thread Richard Damon
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

2021-02-25 Thread Terry Reedy

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

2021-02-24 Thread John O'Hagan
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

2021-02-24 Thread MRAB

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

2021-02-24 Thread John O'Hagan
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

2021-02-24 Thread John O'Hagan
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

2021-02-24 Thread Terry Reedy

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

2021-02-24 Thread MRAB

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


Tkinter long-running window freezes

2021-02-24 Thread 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.

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