web-dev-qa-db-fra.com

RuntimeError: le thread principal n'est pas dans la boucle principale

Quand j'appelle 

self.client = ThreadedClient() 

dans mon programme Python, j'obtiens l'erreur 

"RuntimeError: le thread principal n'est pas dans la boucle principale"

J'ai déjà fait des recherches sur Google, mais je me suis trompé ... Quelqu'un peut-il m'aider, s'il vous plaît? 

Erreur complète:

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

Des 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
18
user2040823

Vous exécutez votre boucle d'interface graphique principale dans un fil à part le fil principal. Tu ne peux pas faire ça.

Les docs mentionnent de manière désobligeante dans quelques endroits que Tkinter n'est pas tout à fait sûr, mais pour autant que je sache, ne dites jamais que vous ne pouvez parler à Tk que du fil principal. La raison en est que la vérité est un peu compliquée. Tkinter lui-même est thread-safe, mais il est difficile à utiliser de manière multithread. La documentation officielle la plus proche semble être cette page :

Q. Y at-il une alternative à Tkinter qui est thread-safe?

Tkinter?

Il suffit d'exécuter tout le code de l'interface utilisateur dans le fil principal et de laisser les rédacteurs écrire dans un objet de la file d'attente…

(L'exemple de code donné n'est pas génial, mais il suffit de comprendre ce qu'ils suggèrent et de faire les choses correctement.)

En fait, est une alternative thread-safe à Tkinter, mtTkinter . Et sa documentation explique assez bien la situation:

Bien que Tkinter soit techniquement sûr pour les threads (en supposant que Tk soit construit avec --enable-threads), il existe toujours des problèmes lorsqu'il est utilisé dans des applications Python multithreads. Les problèmes proviennent du fait que le module _tkinter tente de prendre le contrôle du thread principal via une technique d’interrogation lors du traitement des appels provenant d’autres threads. 

Je pense que c'est exactement ce que vous voyez: votre code Tkinter dans Thread-1 tente de jeter un coup d'œil dans le thread principal pour trouver la boucle principale, et ce n'est pas là.

Alors, voici quelques options:

  • Faites ce que la documentation Tkinter recommande et utilisez TkInter à partir du thread principal. Peut-être en déplaçant votre code de thread principal actuel dans un thread de travail.
  • Si vous utilisez une autre bibliothèque qui souhaite prendre en charge le thread principal (par exemple, twisted), elle peut avoir un moyen de s’intégrer à Tkinter, auquel cas vous devriez l’utiliser.
  • Utilisez mkTkinter pour résoudre le problème.

En outre, bien que je n’aie trouvé aucun doublon exact de cette question, il existe un certain nombre de questions connexes sur les SO. Voir cette question , cette réponse , et beaucoup d’autres pour plus d’informations.

25
abarnert

Puisque tout ceci a aidé mon problème mais ne l'a pas résolu complètement, voici une chose supplémentaire à garder à l'esprit:

Dans mon cas, j'ai commencé à importer la bibliothèque pyplot dans de nombreux threads et à l'utiliser. Après avoir déplacé tous les appels de la bibliothèque vers mon thread principal, j'ai toujours cette erreur.

Je m'en suis débarrassé en supprimant toutes les instructions d'importation de cette bibliothèque dans d'autres fichiers utilisés dans d'autres threads. Même s'ils n'utilisaient pas la bibliothèque, la même erreur était causée par celle-ci.

1
Christian Mosz