I have run into this QCoreApplication problem where invoking input() after a QObject finishes executing inside a QThread causes an infinite loop printing to the console "QCoreApplication::exec: The event loop is already running".
In the code I create a generic worker as a QObject, move it into a QThread (the sanctioned way to use QThread, instead of subclassing it) and then execute another QObject's (Master class) function inside the generic worker. Everything works fine as long as I don't call input() after the Master has been executed. Note that the problem occurs also if I execute a function directly in the worker (not a Master instance's function).
Here is the sample code to reproduce the problem:
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()
There is no real problem with input
in your example. After pressing enter in done()
, control will return to the event-loop and then wait for further user interaction - which is the normal and expected behaviour.
You don't make it clear what you expect to happen after that. But if you want the program to quit, just do this:
def done():
input("Enter your answer: ")
QCoreApplication.quit()
The Qt warning message is harmless, but it can be removed like this:
def main():
from PyQt4.QtCore import pyqtRemoveInputHook
pyqtRemoveInputHook()
app = QCoreApplication(sys.argv)
...
The only real problem in your example is the threading implementation. If you add the line print(QThread.currentThread())
to Worker.run()
, Master.do()
and main()
, you will see that all three are executed in the main thread. This is because you connected the thread.start
signal before moving the worker to the other thread. The best (i.e. most easily maintainable) way to fix this issue to always use the @pyqtSlot
decorator on any slots that are connected across threads - because then it won't matter when the signal connections are made. (See this answer for a more complete explanation of this issue).