[Tkinter-discuss] Tkinter, threads? memory?

Michael Lange klappnase at web.de
Thu Sep 21 22:46:53 CEST 2006


On Wed, 20 Sep 2006 12:27:47 -0400
Michael M Haimes <mhaimes at MIT.EDU> wrote:


> ------------1----------
> Linux implementations of Python/Tkinter (the most current) seem to exhibit the
> problem, but not Sun/Solaris or Windows.
> 
> Essentially, I can't animate objects on a canvas without leaking memory at about
> 1Mb/s . I've tried 4 different methods, either deleting and redrawing objects,
> or just moving objects, and either using Tk tags to label and operate the
> objects, or storing their IDs in my program and operating with them that way. I
> can't think of any other way to do this, and I basically need to. I would try
> the WCK to make my own, but the idea is to have the software run on a base
> install of Python.
> 
> The link to the four example test scripts I wrote up is here:
> http://web.mit.edu/mhaimes/Public/tktest.tar
> 
> ------------2----------
> The Mac OS X implementation of Python seems to exhibit this problem, and no
> other systems.
> 
> This problem is related to the above, namely, on the Mac animation seems to be
> totally broken. Whenever Tk is running its event loop, it stops all other
> python threads from processing and sits and waits for input. So my example code
> below runs a counter in a background thread which increments the number Tk has
> displayed. On Windows/Linux, it just counts up until you press exit. On the
> Mac, it will sit there and freeze if you aren't inputting anything, but as soon
> as you wave the mouse around or start typing it'll continue to increment;
> whereas on Windows and Linux, the counter just counts up no matter what you are
> inputting. I know this could be done without two separate threads, but the real
> program that this is modelling an issue I'm having needs Tk to be in its event
> loop, so that is not an option.
> 
> Test code as follows:
> #################
> import Tkinter
> import thread
> import sys
> 
> app = Tkinter.Tk()
> text = Tkinter.StringVar()
> Tkinter.Label(app, textvariable = text).pack()
> def looper():
>         count = 0
>         while 1:
>                 text.set(`count`)
>                 count+=1
> thread.start_new_thread(looper, ())
> Tkinter.Button(app, text="exit", command = sys.exit).pack()
> app.mainloop()
> ##############
> 
> -----------------------
> 
> If anyone has any hints or suggestions for workarounds for either of the above
> problems, it would be much appreciated. Or if you just run any of the test
> programs on your systems and get different results than I predicted for you,
> tell me some things about your system and what happened.
> 

Hi Michael,

for me (on linux) #2 works, on all examples of #1 i get this error:

Unhandled exception in thread started by <function mover at 0xb7dbf684>
Traceback (most recent call last):
  File "projekte/test2.py", line 18, in mover
    canvas.move(dot, -100*sin(t)*dt, 100*cos(t)*dt)
  File "/usr/lib/python2.4/lib-tk/Tkinter.py", line 2189, in move
    self.tk.call((self._w, 'move') + args)
_tkinter.TclError: too many nested evaluations (infinite loop?)

Generally I don't think it is a good idea to update Tk widgets directly from
Python threads. I think you better create some variable to manage the communication
between Tk and your child thread, as in this example which does practcally the same
as #2:

import Tkinter
import thread
import sys

app = Tkinter.Tk()
text = Tkinter.StringVar()
Tkinter.Label(app, textvariable = text).pack()
newtext = '0'
def looper():
        global newtext
        count = 0
        while 1:
                #text.set(`count`)
                newtext = str(count)
                count+=1
thread.start_new_thread(looper, ())
Tkinter.Button(app, text="exit", command = sys.exit).pack()

def update_label():
    text.set(newtext)
    app.after(10, update_label)
update_label()

app.mainloop()

And a working animation as in #1 with the same technique:

import Tkinter
from time import time, sleep
from math import sin, cos
from thread import start_new_thread
import sys

app = Tkinter.Tk()
canvas = Tkinter.Canvas(app, width=300, height=300)
canvas.pack()
t = time()
dot = canvas.create_oval(100*cos(t)+150, 100*sin(t)+150, 100*cos(t)+150, 100*sin(t)+150)

x, y = 0, 0
def mover():
    global x, y
    t = time()
    try:
        while 1:
            dt = time()-t;t=time()
            #canvas.move(dot, -100*sin(t)*dt, 100*cos(t)*dt)
            x, y = -100*sin(t)*dt, 100*cos(t)*dt
            sleep(0.1)
    except KeyboardInterrupt:
        sys.exit(-1)
start_new_thread(mover, ())

def update_oval():
    canvas.move(dot, x, y)
    canvas.after(100, update_oval)
update_oval()

app.mainloop()

I hope this helps

Michael


More information about the Tkinter-discuss mailing list