Multithreading tkinter question

John Pote johnpote at blueyonder.co.uk
Thu Dec 16 12:59:53 CET 2004


"Mark English" <Mark.English at liffe.com> wrote in message 
news:mailman.7804.1103134911.5135.python-list at python.org...
Is there a safe way to run tkinter in a multithreaded app where the
mainloop runs in a background thread ?


Mark,

I tried your code snippet with Python 2.3.4. Worked fine. Only problem was 
that the program fell off the end and terminated before the second thread 
could open the Tkinter window. So I added these lines at the end to make the 
main thread wait:-

from msvcrt import kbhit, getch
print "\n\nPress key to end"
while not kbhit(): pass
getch()

Both your Hello and Quit buttons worked.

However, I have found that tkinter crashes if any components, labels text 
box etc, are accessed directly from another thread. Below is a posting I did 
some time ago. My solution to the problem. I'm still interested to know if 
this is a good/best way to solve this problem.

It is not optimal in that 'otherThread' runs continuously even when the 
label is not being updated. What a waste of cpu cycles! This shows up in 
that other windows apps slow right down. What is needed is a comms method 
between threads that causes a thread to block while it's waiting for data 
rather than my continuous polling approach. Would a Queue help here?

John Pote

"Martin Franklin" <mfranklin1 at gatwick.westerngeco.slb.com> wrote in message
news:mailman.6201.1100071949.5135.python-list at python.org...
> On Tue, 09 Nov 2004 17:41:56 GMT, John Pote <johnpote at blueyonder.co.uk>
> wrote:
>
>> Running my programme in Python 2.3.4 I received the following msg in the
>> consol :-
>> (Pent III running W2K prof)
>>
>> """
>> Exception in Tkinter callback
>> Traceback (most recent call last):
>>   File "c:\apps\python\234\lib\lib-tk\Tkinter.py", line 1345, in __call__
>>     return self.func(*args)
>>   File "c:\apps\python\234\lib\lib-tk\Tkinter.py", line 459, in callit
>>     self.deletecommand(tmp[0])
>> AttributeError: 'str' object has no attribute 'deletecommand'
>> UpdateStringProc should not be invoked for type option
>>
>> abnormal program termination
>> """
>> There was no other traceback information.
>>
>> Could this be related to lines of the ilk:-
>>   self.infoSpd.config(text="%d.%01d"%spd)
>> where infoSpd is a Tkinter Label object placed using the grid manager.
>>
>> Thousands of these updates were performed so the labels displayed
>> progress
>> through a memory dump of a system accessed through a serial port.
>>
>> I had trouble before with Python versions 2.2.1 and 2.2.3 where
>> commenting
>> out these Label updates stopped the system crashing and it was happy to
>> run
>> for hours performing tests on the external hardware. (an embedded data
>> logger I'm developing)
>>
>> Anyone any thoughts?
>>
>> John
>
>
> Only one (thought that is)  Are you updating thses Label widgets from
> other
> threads? and could you possibly post an example?
>
> Martin


Ahhhh  --  Experience had already taught me that lesson about tkinter. On
checking my code guess what I found I'd done - called the widget.config
method from the other thread. So I put in a list to queue the label updates
from the other thread to the tkinter thread and it's now been running for
several hours without problem.

Thanks for the reminder.

BTW the program structure I've been using is:-

def otherThread():
    while TRUE:
        if updatelabel:
            labelQ = "new label text"

def guiLoop():
    if labelQ:
        myLabel.config(text=labelQ)
        labelQ = None
     #re-register this fn to run again
     rootWin.after(10, guiLoop) #strangely .after_idle(guiLoop) is slower!
.
.
rootWin = Tk(className=" tester")

#rest of GUI set up. then:-

 thread.start_new( otherThread, () )
 rootWin.after(50, guiLoop)
 rootWin.mainloop()

It works but is it the best way to do this sort of thing? The point is that
I need moderately fast serial comms, which I do in 'otherThread' and found
the 'after' and 'after_idle' call backs were too slow. The timing I did on
py2.2.1 indicated that 'after_idle' could not do better than ~70ms and
'after(10, ....)' was faster, 30-40 ms, but still too slow for my app.

Any more thoughts appreciated.

John






More information about the Python-list mailing list