5

I have written a python tkinter code using threads so as the tkinter wizard updates automatically by tkinter mainloop running in the main thread and background process running in separate thread. But I noticed, that python crashes after some time when running the code. Moreover its random in nature but python crashes most of the time. I have written a small test code which can show this problem (My original code is similar to this but having some real processes and many other features, so I am sharing the test code).

######################################################################
 # Test Code for Tkinter with threads
import Tkinter
import threading
import Queue
import time

# Data Generator which will generate Data
def GenerateData(q):
    for i in range(1000000):
        #print "Generating Some Data, Iteration %s" %(i)
        time.sleep(0.01)
        q.put("Some Data from iteration %s. Putting this data in the queue for testing" %(i))

# Queue which will be used for storing Data
q = Queue.Queue()

def QueueHandler(widinst, q):
    linecount = 0
    while True:
        print "Running"
        if not q.empty():
            str = q.get()
            linecount = linecount + 1
            widinst.configure(state="normal")
            str = str + "\n"
            widinst.insert("end", str)
            if linecount > 100:
                widinst.delete('1.0', '2.0')
                linecount = linecount - 1
            widinst.see('end')
            widinst.configure(state="disabled")

# Create a thread and run GUI & QueueHadnler in it
tk = Tkinter.Tk()
scrollbar = Tkinter.Scrollbar(tk)
scrollbar.pack(side='right', fill='y' )
text_wid = Tkinter.Text(tk,yscrollcommand=scrollbar.set)
text_wid.pack()
t1 = threading.Thread(target=GenerateData, args=(q,))
t2 = threading.Thread(target=QueueHandler, args=(text_wid,q))
t2.start()
t1.start()

tk.mainloop()
######################################################################

TO REPRODUCE:

If you open this code in IDLE and run it, it will sometimes appeared to be in hang state. So to reproduce, modify the sleep time to 0.1 from 0.01 and run it. After this stop the application, and modify it back to 0.01, do save and run it. This time it will run and after some time, python will stop working. I am using windows 7 (64 bit).

QUESTION

I have submitted it to python bugs and it got rejected. But I got this idea from one of the stackoverflow question to use queue for writing in tkinter. Can some one please suggest what should be done to handle it.

EDITED CODE:

# Test Code for Tkinter with threads
import Tkinter
import threading
import Queue
import time

# Data Generator which will generate Data
def GenerateData(q):
    for i in range(1000000):
        #print "Generating Some Data, Iteration %s" %(i)
        time.sleep(0)
        q.put("Some Data from iteration %s. Putting this data in the queue for testing" %(i))

# Queue which will be used for storing Data
q = Queue.Queue()

def QueueHandler():
    global widinst, q
    linecount = 0
    if not q.empty():
        str = q.get()
        linecount = linecount + 1
        widinst.configure(state="normal")
        str = str + "\n"
        widinst.insert("end", str)
        if linecount > 100:
            widinst.delete('1.0', '2.0')
            linecount = linecount - 1
        widinst.see('end')
        widinst.configure(state="disabled")
        tk.after(1,QueueHandler)

# Create a thread and run GUI & QueueHadnler in it
tk = Tkinter.Tk()
scrollbar = Tkinter.Scrollbar(tk)
scrollbar.pack(side='right', fill='y' )
text_wid = Tkinter.Text(tk,yscrollcommand=scrollbar.set)
text_wid.pack()
t1 = threading.Thread(target=GenerateData, args=(q,))
#t2 = threading.Thread(target=QueueHandler, args=(text_wid,q))
#t2.start()
widinst = text_wid
t1.start()
tk.after(1,QueueHandler)
tk.mainloop()
sarbjit
  • 3,786
  • 9
  • 38
  • 60
  • The idea was either different or it is simply wrong. The problem regarding threads and GUI is not restricted to Tk, most GUI toolkits I'm aware impose the same restriction: update the GUI only in the thread that the GUI is running. To correct your example: move the queue reading, and consequently the GUI updates, to the main thread. `GenerateDate` continues running on his own thread. – mmgp Jan 05 '13 at 04:48
  • The updated code is problematic because now your main thread leaves no opportunity for the worker thread to run. I see you put a `time.sleep(0)` in the worker thread, that makes it yield its control to another thread (the main one in this case). Now, in your main thread you are calling `tk.after(1, ...)` which is not yielding the control back to the other thread at any time. So, all you have to do is also add a `time.sleep(0)` there too. Now both threads acts nicely, allowing each other to run. – mmgp Jan 06 '13 at 17:36

3 Answers3

8

Tkinter is not thread safe; you cannot access Tkinter widgets from anywhere but the main thread. You will need to refactor your code so that QueueHandler runs in the main thread.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • There is one more problem I am seeing now:- In my code I used `after` to call `QueueHandler` to update the GUI. But if I uncomment out the print statement from the `GenerateData` thread, then I see only the printed statements on the console and GUI is never updated. As soon as I comment that print statement back, then GUI starts populating. There was one instance where I could see the printed statements and GUI update as well. It seems to be random ??. Can you please comment on this behavior – sarbjit Jan 06 '13 at 05:30
  • See the comments to your question, this is barely related to Tk and Tkinter (and applies to most GUI toolkits). – mmgp Jan 06 '13 at 17:37
3

As Bryan says Tkinter is not thread safe. Here is a modification that attempts to make it so: http://tkinter.unpythonic.net/wiki/mtTkinter

Alex Peters
  • 2,601
  • 1
  • 22
  • 29
Gonzo
  • 2,023
  • 3
  • 21
  • 30
1

Tkinter is designed to be thread-safe but isn't due to bugs, in both 2.x and 3.x.

Until fixes are released (and in older versions), you should use mtTkinter (which is designed to be a drop-in replacement) as a workaround.

ivan_pozdeev
  • 33,874
  • 19
  • 107
  • 152
  • Thank you. Do you know whether I can use `mtTkinter` with `ttk` in Python 2.7? E.g. `import mttkinter` and then `import ttk`? – actual_panda Oct 24 '19 at 18:02