运行时错误:主线程不在主循环中

问题描述 投票:0回答:10

当我打电话时

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
python multithreading tkinter
10个回答
60
投票

您正在主线程之外的线程中运行主 GUI 循环。你不能这样做。

文档在一些地方随口提到 Tkinter 不太线程安全,但据我所知,从来没有完全站出来说你只能从主线程与 Tk 对话。原因是事实有些复杂。 Tkinter 本身是线程安全的,但很难以多线程方式使用。最接近官方文档的似乎是此页面

问。有没有线程安全的 Tkinter 替代方案?

Tkinter?

只需在主线程中运行所有 UI 代码,然后让编写者写入 Queue 对象......

(给出的示例代码不是很好,但足以弄清楚他们的建议并正确执行操作。)

实际上

Tkinter 的线程安全替代品,mtTkinter。它的文档实际上很好地解释了这种情况:

虽然 Tkinter 在技术上是线程安全的(假设 Tk 是使用 --enable-threads 构建的),但实际上在多线程 Python 应用程序中使用时仍然存在问题。这些问题源于以下事实:_tkinter 模块在处理来自其他线程的调用时尝试通过轮询技术获得主线程的控制权。

我相信这正是您所看到的:Thread-1 中的 Tkinter 代码正在尝试查看主线程以查找主循环,但它不在那里。

所以,这里有一些选择:

按照 Tkinter 文档的建议进行操作并从主线程使用 TkInter。可能通过将当前的主线程代码移至工作线程中。
  • 如果您正在使用其他想要接管主线程的库(例如,
  • twisted
  • ),它可能有一种与 Tkinter 集成的方法,在这种情况下您应该使用它。
    使用
  • mkTkinter
  • 来解决问题。
    
    
  • 此外,虽然我没有找到这个问题的任何精确重复项,但有很多相关的问题。请参阅
这个问题

这个答案以及更多更多信息。


28
投票

plt.switch_backend('agg')



24
投票

t = threading.Thread(target=your_func) t.setDaemon(True) t.start()



4
投票

就我而言,我开始在多个线程中导入 pyplot 库并在那里使用它。将所有库调用移至我的主线程后,我仍然遇到该错误。

我确实通过删除其他线程中使用的其他文件中该库的所有导入语句来摆脱它。即使他们没有使用该库,也会导致相同的错误。


4
投票

root.mainloop()

当然,如果不是 
root

,则应使用

Tk
对象的名称来代替
root
    


3
投票
也许这个例子会对某人有所帮助。


2
投票

然后在您的代码中添加以下内容: 从 mttkinter 导入 mtTkinter 作为 tk


1
投票


0
投票

如果我这样做:

while (True): #more code here window.update_idletasks() window.update()

当线程尝试访问 tkinter 函数时,代码崩溃。
但是,如果我这样做:

window.mainloop()

所有线程都正常执行。
希望这对某人有帮助。


0
投票

我还使用了一个可由任一线程操作的关闭标志。它确保退出/销毁调用是在主线程中进行的。之后,我加入该线程,这很重要!像这样的东西(虽然我在类中使用它,并且标志,窗口和线程是属性):

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

© www.soinside.com 2019 - 2024. All rights reserved.