当我打电话时
self.client = ThreadedClient()
在我的 Python 程序中,我收到错误
“运行时错误:主线程不在主循环中”
我已经做了一些谷歌搜索,但不知何故我犯了一个错误......有人可以帮助我吗?
完整错误:
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
课程:
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
您正在主线程之外的线程中运行主 GUI 循环。你不能这样做。
文档在一些地方随口提到 Tkinter 不太线程安全,但据我所知,从来没有完全站出来说你只能从主线程与 Tk 对话。原因是事实有些复杂。 Tkinter 本身是线程安全的,但很难以多线程方式使用。最接近官方文档的似乎是此页面:
问。有没有线程安全的 Tkinter 替代方案?Tkinter?
只需在主线程中运行所有 UI 代码,然后让编写者写入 Queue 对象......
(给出的示例代码不是很好,但足以弄清楚他们的建议并正确执行操作。)
实际上
有Tkinter 的线程安全替代品,mtTkinter。它的文档实际上很好地解释了这种情况:
虽然 Tkinter 在技术上是线程安全的(假设 Tk 是使用 --enable-threads 构建的),但实际上在多线程 Python 应用程序中使用时仍然存在问题。这些问题源于以下事实:_tkinter 模块在处理来自其他线程的调用时尝试通过轮询技术获得主线程的控制权。我相信这正是您所看到的:Thread-1 中的 Tkinter 代码正在尝试查看主线程以查找主循环,但它不在那里。
所以,这里有一些选择:
按照 Tkinter 文档的建议进行操作并从主线程使用 TkInter。可能通过将当前的主线程代码移至工作线程中。
twisted
mkTkinter
、这个答案以及更多更多信息。
plt.switch_backend('agg')
t = threading.Thread(target=your_func)
t.setDaemon(True)
t.start()
就我而言,我开始在多个线程中导入 pyplot 库并在那里使用它。将所有库调用移至我的主线程后,我仍然遇到该错误。
我确实通过删除其他线程中使用的其他文件中该库的所有导入语句来摆脱它。即使他们没有使用该库,也会导致相同的错误。
root.mainloop()
当然,如果不是
root
,则应使用
Tk
对象的名称来代替 root
。也许这个例子会对某人有所帮助。
然后在您的代码中添加以下内容: 从 mttkinter 导入 mtTkinter 作为 tk
如果我这样做:
while (True):
#more code here
window.update_idletasks()
window.update()
当线程尝试访问 tkinter 函数时,代码崩溃。 但是,如果我这样做:
window.mainloop()
所有线程都正常执行。 希望这对某人有帮助。
我还使用了一个可由任一线程操作的关闭标志。它确保退出/销毁调用是在主线程中进行的。之后,我加入该线程,这很重要!像这样的东西(虽然我在类中使用它,并且标志,窗口和线程是属性):
window = None
thread = threading.Thread(target=run_window, daemon=True)
thread.start()
def run_window():
global window
window = tk.Tk()
window.mainloop()
def destroy_window():
try:
window.quit()
window.destroy()
except RuntimeError:
pass
thread.join()