Multithreading tkinter question

Discussion in 'Python' started by Mark English, Dec 15, 2004.

  1. Mark English

    Mark English Guest

    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.
    -----------------------------------------------------------------------
    Mark English, Dec 15, 2004
    #1
    1. Advertising

  2. Mark English

    John Pote Guest

    "Mark English" <> wrote in message
    news:...
    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" <> wrote in message
    news:...
    > On Tue, 09 Nov 2004 17:41:56 GMT, John Pote <>
    > 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
    John Pote, Dec 16, 2004
    #2
    1. Advertising

  3. 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
    Oleg Paraschenko, Dec 17, 2004
    #3
  4. Mark English

    Eric Brunel Guest

    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
    Eric Brunel, Dec 17, 2004
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Rob

    multithreading question

    Rob, Sep 8, 2004, in forum: ASP .Net
    Replies:
    4
    Views:
    356
  2. Jean-Yves Nief

    question on multithreading, pipes

    Jean-Yves Nief, Dec 15, 2003, in forum: Python
    Replies:
    1
    Views:
    328
  3. corrado
    Replies:
    1
    Views:
    471
    Eric Brunel
    May 24, 2004
  4. facugaich

    Multithreading question

    facugaich, Oct 10, 2006, in forum: C Programming
    Replies:
    9
    Views:
    294
    facugaich
    Oct 10, 2006
  5. Alexander Dong Back Kim

    A simple question on multithreading

    Alexander Dong Back Kim, Mar 19, 2008, in forum: C++
    Replies:
    2
    Views:
    244
    Chris Thomasson
    Mar 19, 2008
Loading...

Share This Page