43

電話すると

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
4

6 に答える 6

52

メインスレッド以外のスレッドでメイン GUI ループを実行しています。これはできません。

ドキュメントでは、Tkinter が完全にスレッド セーフではないことがいくつかの場所で率直に述べられていますが、私の知る限り、メイン スレッドからのみ Tk と通信できるとは決して言いません。その理由は、真実はやや複雑だからです。Tkinter 自体スレッドセーフですが、マルチスレッドでの使用は困難です。これに関する公式ドキュメントに最も近いのは、このページのようです:

Q. スレッドセーフな Tkinter の代替手段はありますか?

トキンター?

すべての UI コードをメイン スレッドで実行し、ライターに Queue オブジェクトへの書き込みを許可するだけです…</p>

(与えられたサンプル コードは素晴らしいものではありませんが、彼らが何を提案しているかを理解し、適切に実行するには十分です。)

実際に、Tkinter のスレッドセーフな代替手段であるmtTkinterがあります。そして、そのドキュメントは実際に状況をかなりよく説明しています:

Tkinter は技術的にはスレッドセーフですが (Tk が --enable-threads でビルドされていると仮定します)、実際には、マルチスレッドの Python アプリケーションで使用するとまだ問題があります。この問題は、_tkinter モジュールが、他のスレッドからの呼び出しを処理するときに、ポーリング手法を介してメイン スレッドの制御を取得しようとするという事実に起因しています。

これはまさにあなたが見ているものだと思います.Thread-1のTkinterコードは、メインスレッドを覗いてメインループを見つけようとしていますが、そこにはありません.

したがって、ここにいくつかのオプションがあります:

  • Tkinter ドキュメントの推奨事項を実行し、メイン スレッドから TkInter を使用します。おそらく、現在のメイン スレッド コードをワーカー スレッドに移動します。
  • メイン スレッドを引き継ぐ他のライブラリを使用している場合 (例: twisted)、Tkinter と統合する方法がある可能性があります。その場合は、それを使用する必要があります。
  • mkTkinter問題を解決するために使用します。

また、この質問の正確な重複は見つかりませんでしたが、SO には関連する質問がいくつかあります。詳細については、この質問この回答などを参照してください。

于 2013-02-04T20:26:46.403 に答える
2
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()

たぶん、この例は誰かを助けるでしょう。

于 2020-04-28T20:40:10.587 に答える