Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Multithreading tkinter question

2,416 views
Skip to first unread message

Mark English

unread,
Dec 15, 2004, 1:21:48 PM12/15/04
to pytho...@python.org
Is there a safe way to run tkinter in a multithreaded app where the
mainloop runs in a background thread ?
Here's some test code demonstrating the problem. I'm running Python2.4
under Windows 2000.


----------------Code snip starts-------------
from Tkinter import *

def GetTkinterThread():
import threading
def TestTkinter():
def greeting():
print "Hello stdout world !"

win = Frame()
win.pack()
Label(win, text="Hello container world").pack(side=TOP)
Button(win, text="Hello", command=greeting).pack(side=TOP)
Button(win, text="Quit", command=win.quit).pack(side=RIGHT)

win.mainloop()
#Run the mainloop in another thread
t = threading.Thread(None, TestTkinter, 'Test Tkinter Thread')
t.setDaemon(1) #Keep going
t.start()
print 'Hi'
return t

t = GetTkinterThread() #This works fine

#Now press the "Hello" button on the Tkinter window
#Now press the return key at the python prompt

----------------Code snip ends-------------
With a debug build the call stack looks like this:
python24_d.dll!Py_FatalError(const char * msg=0x1e23ca14) Line
1513 C
python24_d.dll!PyThreadState_Swap(_ts * new=0x0098d0b8) Line
293 + 0xa C
python24_d.dll!PyEval_RestoreThread(_ts * tstate=0x0098d0b8)
Line 309 + 0x9 C
_tkinter_d.pyd!EventHook() Line 2969 + 0xc C
python24_d.dll!my_fgets(char * buf=0x009ef3e8, int len=100,
_iobuf * fp=0x1027c838) Line 46 C
python24_d.dll!PyOS_StdioReadline(_iobuf * sys_stdin=0x1027c838,
_iobuf * sys_stdout=0x1027c858, char * prompt=0x0087f974) Line 125 +
0x11 C
python24_d.dll!PyOS_Readline(_iobuf * sys_stdin=0x1027c838,
_iobuf * sys_stdout=0x1027c858, char * prompt=0x0087f974) Line 205 +
0x12 C

Is this because of access to sys.stdout ? Or some deeper darker problem
? Is there a right way to achieve this ? Basically I want to be able to
call a function from the Python prompt which creates a Tkinter window
(not necessarily interactive apart from a close button) to display some
data, but leaves the Python prompt active so I can carry on from there.

Haven't found anything about this yet. All sample multithreaded Tkinter
code I've seen uses the main thread as the GUI thread. Also, should I be
posting this to another newsgroup ?

Thanks for any help,
Mark


-----------------------------------------------------------------------
The information contained in this e-mail is confidential and solely
for the intended addressee(s). Unauthorised reproduction, disclosure,
modification, and/or distribution of this email may be unlawful. If you
have received this email in error, please notify the sender immediately
and delete it from your system. The views expressed in this message
do not necessarily reflect those of LIFFE Holdings Plc or any of its subsidiary companies.
-----------------------------------------------------------------------

John Pote

unread,
Dec 16, 2004, 6:59:53 AM12/16/04
to

"Mark English" <Mark.E...@liffe.com> wrote in message
news:mailman.7804.1103134...@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" <mfran...@gatwick.westerngeco.slb.com> wrote in message
news:mailman.6201.1100071...@python.org...
> On Tue, 09 Nov 2004 17:41:56 GMT, John Pote <john...@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

Mark English

unread,
Dec 16, 2004, 7:31:38 AM12/16/04
to pytho...@python.org, john...@blueyonder.co.uk
> Date: Thu, 16 Dec 2004 11:59:53 GMT
> From: "John Pote" <john...@blueyonder.co.uk>

>
> "Mark English" <Mark.E...@liffe.com> wrote in message
> news:mailman.7804.1103134...@python.org...
> Is there a safe way to run tkinter in a multithreaded app
> where the mainloop runs in a background thread ?
>
>
> 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.
John,
Thanks for trying that out. I hadn't thought to try another version of
python. I should have said that the example code snippet was for entry
at the command prompt rather than putting in a module or script.

I've tried with activestate Python 2.3.2 and it worked fine.

When I submitted the code I was using my own build of Python 2.4.
On another machine I installed Python2.4 and pywin32-203.win32-py2.4 to
ensure I hadn't messed my build up.
Running a normal Tkinter test works fine. Running the test code I
submitted at the python prompt (or rather putting the function in a
module and calling it from the python prompt as indicated in the test
code) caused the Tkinter window to come up on the taskbar, but it was
invisible. I can only minimize and attempt to close it - close reports
that I will have to kill the program. In other words, it appears to be
broken.

Has anyone else seen any similar problems with Python 2.4 or,
conversely, does it all work ok ? Having now seen the code work with
activestate Python 2.3.2 there's this tantalising prospect that it all
might "just work" after all.

Cheers,
mE

Oleg Paraschenko

unread,
Dec 17, 2004, 2:21:21 AM12/17/04
to
Hello John,

> 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()

And I added

print "\n\nPress key to end"

l = sys.stdin.readline()

> Both your Hello and Quit buttons worked.

In my case "Hello" works and "Quit" doesn't (GUI stays frozen).
Linux, Python 2.3.3, pygtk-0.6.9.

> ...


>
> 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?
>

Yes, it should help. A time ago I tried to write a tkinter
application,
and my test code is available:

A complete Python Tkinter sample application for a long operation
http://uucode.com/texts/pylongopgui/pyguiapp.html
Maybe you find it interesting.

--
Oleg

Eric Brunel

unread,
Dec 17, 2004, 3:48:49 AM12/17/04
to
Oleg Paraschenko wrote:
[snip]

> In my case "Hello" works and "Quit" doesn't (GUI stays frozen).
> Linux, Python 2.3.3, pygtk-0.6.9.

That's not a multithreading issue, but just the way the quit method works. Try:

-------------------------------------------------
import time
from Tkinter import *

root = Tk()
b = Button(root, text='Quit')
b.configure(command=root.quit)
b.pack()

root.mainloop()

time.sleep(2)
-------------------------------------------------

When you click the "Quit" button, the GUI stays there until the time.sleep ends.
root.quit just goes out of the mainloop; it doesn't destroy the widgets. To do
that, you have to add an explicit root.destroy() after root.mainloop()
--
- Eric Brunel <eric (underscore) brunel (at) despammed (dot) com> -
PragmaDev : Real Time Software Development Tools - http://www.pragmadev.com

0 new messages