Reputation: 609
When I call
self.client = ThreadedClient()
in my Python program, I get the error
"RuntimeError: main thread is not in main loop"
I have already done some googling, but I am making an error somehow ... Can someone please help me out?
Full error:
Exception in thread Thread-1:
Traceback (most recent call last):
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.py", line 530, in __bootstrap_inner
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.py", line 483, in run
File "/Users/Wim/Bird Swarm/bird_swarm.py", line 156, in workerGuiThread
self.root.after(200, self.workerGuiThread)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py", line 501, in after
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py", line 1098, in _register
RuntimeError: main thread is not in main loop
Classes:
class ThreadedClient(object):
def __init__(self):
self.queue = Queue.Queue( )
self.gui = GuiPart(self.queue, self.endApplication)
self.root = self.gui.getRoot()
self.running = True
self.GuiThread = threading.Thread(target=self.workerGuiThread)
self.GuiThread.start()
def workerGuiThread(self):
while self.running:
self.root.after(200, self.workerGuiThread)
self.gui.processIncoming( )
def endApplication(self):
self.running = False
def tc_TekenVogel(self,vogel):
self.queue.put(vogel)
class GuiPart(object):
def __init__(self, queue, endCommand):
self.queue = queue
self.root = Tkinter.Tk()
Tkinter.Canvas(self.root,width=g_groottescherm,height=g_groottescherm).pack()
Tkinter.Button(self.root, text="Move 1 tick", command=self.doSomething).pack()
self.vogelcords = {} #register of bird and their corresponding coordinates
def getRoot(self):
return self.root
def doSomething():
pass #button action
def processIncoming(self):
while self.queue.qsize( ):
try:
msg = self.queue.get(0)
try:
vogel = msg
l = vogel.geeflocatie()
if self.vogelcords.has_key(vogel):
cirkel = self.vogelcords[vogel]
self.gcanvas.coords(cirkel,l.geefx()-g_groottevogel,l.geefy()-g_groottevogel,l.geefx()+g_groottevogel,l.geefy()+g_groottevogel)
else:
cirkel = self.gcanvas.create_oval(l.geefx()-g_groottevogel,l.geefy()-g_groottevogel,l.geefx()+g_groottevogel,l.geefy()+g_groottevogel,fill='red',outline='black',width=1)
self.vogelcords[vogel] = cirkel
self.gcanvas.update()
except:
print('Failed, was van het type %' % type(msg))
except Queue.Empty:
pass
Upvotes: 60
Views: 190371
Reputation: 1
Put a try block around any root.quit() or root.destroy() calls and catch the RuntimeException. This works like a charm for me. Also, join the thread after quit/destroy.
I also use a closing flag that can be manipulated by either thread. It makes sure that the quit/destroy calls are made in the main thread. Basically the thread can set the flag to True and the main thread needs to check the flag. Something like this (although I use this in a class and there the flag, window and thread are attributes):
window = None
thread = threading.Thread(target=run_window)
thread.start()
def run_window():
global window
window = tk.Tk()
window.mainloop()
def destroy_window():
try:
window.quit()
except RuntimeError:
pass
try:
window.destroy()
except RuntimeError:
pass
thread.join()
Upvotes: 0
Reputation: 21
Installing the improved version of tkinter can solve this problem. No need to modify your code. You need only do following: pip3 install mtTkinter
then add following in your code: from mttkinter import mtTkinter as tk
Upvotes: 2
Reputation: 1
I know this question was asked a long time ago, but I wanted to tell you how I solved it. In my case, I have a program that sends and receives messages through the serial port and uses the TKinter library.
If I do:
while (True):
#more code here
window.update_idletasks()
window.update()
The code crashes when a thread tries to access a tkinter function. But, if I do this:
window.mainloop()
All the threads execute normaly. Hope this helps someone.
Upvotes: 0
Reputation: 11
You cannot modify your main GIU from another thread you need to send event to the main GUI in order to avoid exceptions Use window.write_event_value instead, this method allows you to send events from your threads you can take a look a this too: window.perform_long_operation
Upvotes: 1
Reputation: 637
I found a way to solve it. it might look like a joke but you just should add
plt.switch_backend('agg')
Upvotes: 33
Reputation: 49
Write it at the end:
root.mainloop()
Of course, in place of root
should be the name of your Tk
object if it is not root
.
Upvotes: 4
Reputation: 103
from tkinter import *
from threading import Thread
from time import sleep
from random import randint
class GUI():
def __init__(self):
self.root = Tk()
self.root.geometry("200x200")
self.btn = Button(self.root,text="lauch")
self.btn.pack(expand=True)
self.btn.config(command=self.action)
def run(self):
self.root.mainloop()
def add(self,string,buffer):
while self.txt:
msg = str(randint(1,100))+string+"\n"
self.txt.insert(END,msg)
sleep(0.5)
def reset_lbl(self):
self.txt = None
self.second.destroy()
def action(self):
self.second = Toplevel()
self.second.geometry("100x100")
self.txt = Text(self.second)
self.txt.pack(expand=True,fill="both")
self.t = Thread(target=self.add,args=("new",None))
self.t.setDaemon(True)
self.t.start()
self.second.protocol("WM_DELETE_WINDOW",self.reset_lbl)
a = GUI()
a.run()
maybe this example would help someone.
Upvotes: 3
Reputation: 653
I know this is late, but I set my thread to a Daemon, and no exception was raised:
t = threading.Thread(target=your_func)
t.setDaemon(True)
t.start()
Upvotes: 24
Reputation: 543
Since all this did help my problem but did not solve it completely here is an additional thing to keep in mind:
In my case I started off importing the pyplot library in many threads and using it there. After moving all the library calls to my main thread I still got that error.
I did get rid of it by removing all import statements of that library in other files used in other threads. Even if they did not use the library the same error was caused by it.
Upvotes: 4
Reputation: 366123
You're running your main GUI loop in a thread besides the main thread. You cannot do this.
The docs mention offhandedly in a few places that Tkinter is not quite thread safe, but as far as I know, never quite come out and say that you can only talk to Tk from the main thread. The reason is that the truth is somewhat complicated. Tkinter itself is thread-safe, but it's hard to use in a multithreaded way. The closest to official documentation on this seems to be this page:
Q. Is there an alternative to Tkinter that is thread safe?
Tkinter?
Just run all UI code in the main thread, and let the writers write to a Queue object…
(The sample code given isn't great, but it's enough to figure out what they're suggesting and do things properly.)
There actually is a thread-safe alternative to Tkinter, mtTkinter. And its docs actually explain the situation pretty well:
Although Tkinter is technically thread-safe (assuming Tk is built with --enable-threads), practically speaking there are still problems when used in multithreaded Python applications. The problems stem from the fact that the _tkinter module attempts to gain control of the main thread via a polling technique when processing calls from other threads.
I believe this is exactly what you're seeing: your Tkinter code in Thread-1 is trying to peek into the main thread to find the main loop, and it's not there.
So, here are some options:
twisted
), it may have a way to integrate with Tkinter, in which case you should use that.mkTkinter
to solve the problem.Also, while I didn't find any exact duplicates of this question, there are a number of related questions on SO. See this question, this answer, and many more for more information.
Upvotes: 64