簡體   English   中英

RuntimeError:主線程不在主循環中

[英]RuntimeError: main thread is not in main loop

當我打電話

self.client = ThreadedClient() 

在我的 Python 程序中,出現錯誤

“RuntimeError:主線程不在主循環中”

我已經做了一些谷歌搜索,但不知何故我犯了一個錯誤......有人可以幫我嗎?

完整錯誤:

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 替代方案?

特金特?

只需在主線程中運行所有 UI 代碼,然后讓編寫者寫入 Queue 對象……

(給出的示例代碼不是很好,但足以弄清楚他們的建議並正確地做事。)

實際上一個線程安全的替代 Tkinter, mtTkinter 它的文檔實際上很好地解釋了這種情況:

盡管 Tkinter 在技術上是線程安全的(假設 Tk 是使用 --enable-threads 構建的),但實際上在多線程 Python 應用程序中使用時仍然存在問題。 問題源於這樣一個事實,即 _tkinter 模塊在處理來自其他線程的調用時試圖通過輪詢技術獲得對主線程的控制。

我相信這正是您所看到的:您在 Thread-1 中的 Tkinter 代碼試圖窺視主線程以找到主循環,但它並不存在。

所以,這里有一些選擇:

  • 執行 Tkinter 文檔的建議並從主線程使用 TkInter。 可能通過將您當前的主線程代碼移動到工作線程中。
  • 如果您正在使用其他一些想要接管主線程的庫(例如, twisted ),它可能有一種與 Tkinter 集成的方法,在這種情況下,您應該使用它。
  • 使用mkTkinter解決問題。

另外,雖然我沒有發現這個問題的任何完全重復,但有一些關於 SO 的相關問題。 請參閱此問題此答案以及更多信息以獲取更多信息。

我知道這已經晚了,但是我將線程設置為守護進程,並且沒有引發異常:

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

我找到了解決它的方法。 它可能看起來像一個笑話,但你應該添加

plt.switch_backend('agg')

由於所有這些確實幫助了我的問題,但並沒有完全解決它,因此需要記住的另一件事是:

就我而言,我開始在許多線程中導入 pyplot 庫並在那里使用它。 將所有庫調用移動到我的主線程后,我仍然遇到該錯誤。

我確實通過刪除其他線程中使用的其他文件中該庫的所有導入語句來擺脫它。 即使他們沒有使用該庫,也會導致同樣的錯誤。

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

也許這個例子會對某人有所幫助。

您不能從另一個線程修改您的主 GIU,您需要將事件發送到主 GUI 以避免異常 請改用 window.write_event_value,此方法允許您從您的線程發送事件,您也可以看看這個:window。執行長操作

寫在最后:

root.mainloop()

當然,如果不是root ,則代替root應該是您的Tk對象的名稱。

我知道很久以前就有人問過這個問題,但我想告訴你我是如何解決它的。 在我的例子中,我有一個通過串行端口發送和接收消息並使用 TKinter 庫的程序。

如果我做:

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

當線程嘗試訪問 tkinter function 時代碼崩潰。但是,如果我這樣做:

window.mainloop()

所有線程都正常執行。 希望這對某人有幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM