2016-10-21 1 views
0

J'ai rencontré ce problème QCoreApplication où l'appel de input() après l'exécution d'un QObject à l'intérieur d'un QThread provoque une impression en boucle infinie sur la console "QCoreApplication :: exec: La boucle d'événements est déjà en cours".Pourquoi est-ce que input() cause "QCoreApplication :: exec: La boucle d'événement est déjà en cours"?

Dans le code, je crée un worker générique comme QObject, déplacez-le dans QThread (la manière approuvée d'utiliser QThread, au lieu de le sous-classer) puis exécutez une autre fonction QObject dans le worker générique. Tout fonctionne bien tant que je n'appelle pas input() après que le Master a été exécuté. Notez que le problème se produit également si j'exécute une fonction directement dans le worker (pas la fonction d'une instance maître).

Voici l'exemple de code pour reproduire le problème:

import sys 
from PyQt4.QtCore import QCoreApplication, QObject, QThread, pyqtSignal, pyqtSlot 


class Worker(QObject): 
    """ 
    Generic worker. 
    """ 
    start = pyqtSignal(str) 
    finished = pyqtSignal() 

    def __init__(self, function): 
     QObject.__init__(self) 
     self._function = function 
     self.start.connect(self.run) 

    def run(self): 
     self._function() 
     self.finished.emit() 


class Master(QObject): 
    """ 
    An object that will use the worker class. 
    """ 
    finished = pyqtSignal() 

    def __init__(self): 
     QObject.__init__(self) 

    @pyqtSlot() 
    def do(self): 
     print("Do what?") 
     self.finished.emit() 


def done(): 
    # FIXME This will cause an infinite loop printing to the console: 
    # "QCoreApplication::exec: The event loop is already running" 
    input("Enter your answer: ") 


def main(): 
    app = QCoreApplication(sys.argv) 

    master = Master() 
    worker = Worker(master.do) 
    master.finished.connect(done) 

    thread = QThread() 
    thread.started.connect(worker.run) 
    worker.moveToThread(thread) 

    # Terminating thread gracefully, or so. 
    worker.finished.connect(thread.quit) 
    worker.finished.connect(worker.deleteLater) 
    thread.finished.connect(thread.deleteLater) 

    thread.start() 

    sys.exit(app.exec_()) 


if __name__ == "__main__": 
    main() 

Répondre

2

Il n'y a pas de réel problème avec input dans votre exemple. Après avoir appuyé sur entrée dans done(), le contrôle retournera à la boucle d'événements et attendra ensuite une interaction supplémentaire de l'utilisateur - ce qui est le comportement normal et attendu.

Vous n'indiquez pas clairement ce que vous attendez après cela. Mais si vous voulez que le programme de cesser de fumer, faire ceci:

def done(): 
    input("Enter your answer: ") 
    QCoreApplication.quit() 

Le message d'avertissement Qt est inoffensif, mais il peut être enlevé comme ceci:

def main(): 
    from PyQt4.QtCore import pyqtRemoveInputHook 
    pyqtRemoveInputHook() 

    app = QCoreApplication(sys.argv) 
    ... 

Le seul problème réel dans votre exemple est l'implémentation du threading. Si vous ajoutez la ligne print(QThread.currentThread()) à Worker.run(), Master.do() et main(), vous verrez que tous les trois sont exécutés dans le thread principal . En effet, vous avez connecté le thread.start signal avant en déplaçant le worker vers l'autre thread. La meilleure solution (c'est-à-dire la plus facile à maintenir) pour résoudre ce problème en utilisant toujours le décorateur @pyqtSlot sur tous les connecteurs connectés aux threads, car cela n'a aucune importance lorsque les connexions de signaux sont établies. (Voir this answer pour une explication plus complète de ce problème).

+0

Excellent! J'ai résolu 2 problèmes avec votre réponse. Merci beaucoup. – R01k