为什么input()导致“QCoreApplication :: exec:事件循环已经在运行”?

时间:2016-10-21 13:18:50

标签: python multithreading input pyqt4 qcoreapplication

我遇到了这个QCoreApplication问题,在QObject完成QThread内部执行后调用input()会导致无限循环打印到控制台“QCoreApplication :: exec:事件循环已经在运行”。

在代码中我创建一个泛型工作器作为QObject,将其移动到QThread(使用QThread的制裁方式,而不是子类化它),然后在泛型工作者中执行另一个QObject(主类)函数。只要在执行完Master后我没有调用input(),一切正常。请注意,如果我直接在worker中执行函数(而不是Master实例的函数),也会出现问题。

以下是重现问题的示例代码:

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

1 个答案:

答案 0 :(得分:3)

您的示例中input没有真正的问题。按下done()后输入,控件将返回事件循环,然后等待进一步的用户交互 - 这是正常和预期的行为。

你没有说清楚你希望在那之后发生什么。但是如果你想让程序退出,那就这样做:

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

Qt警告消息无害,但可以像这样删除:

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

    app = QCoreApplication(sys.argv)
    ...

您的示例中唯一真正的问题是线程实现。如果您将行print(QThread.currentThread())添加到Worker.run()Master.do()main(),您会看到所有三个都在主线程中执行。这是因为您在将工作人员移动到另一个线程之前连接了thread.start信号。修复此问题的最佳方法(即最容易维护的方法)始终在跨线程连接的任何插槽上使用@pyqtSlot装饰器 - 因为这样在进行信号连接时无关紧要。 (有关此问题的更完整说明,请参阅this answer。)