Reputation: 170
I've been messing around with PyQt and signals/slots across threads. Here a situation where I can't find my mistake:
I have a class (MultipleProcessLauncher) that is able to launch multiple processes in separate threads. I catch the stdout of each processes and send those messages to a single queue that is read by another thread (OutputWorker), this last thread should send a signal onNewMessage (I think it doesn't) catch on the main class but the callback function is never called.
print(item)
in the while loop)But: - The signal of the reading thread doesn't seems to emit anything, so the callback function of the main thread is never called...
Your help would be much appreciated, I think I'm missing something with cross threads signals...
class OutputWorker(QObject):
onNewMessage = pyqtSignal(['QString'])
def __init__(self, queue, parent=None):
super(OutputWorker, self).__init__(parent)
self.queue = queue
def work(self):
while True:
item = self.queue.get()
self.onNewMessage.emit(item)
self.queue.task_done()
class MultipleProcessLauncher(QObject):
commandEvent = pyqtSignal(['QString'])
def __init__(self, parent=None):
super(MultipleProcessLauncher, self).__init__(parent)
self.messaging_queue = Queue()
# Start reading message
self.reading_thread = QThread()
self.worker = OutputWorker(self.messaging_queue)
self.worker.moveToThread(self.reading_thread)
self.worker.onNewMessage.connect(self.command_event)
self.reading_thread.started.connect(self.worker.work)
self.reading_thread.start()
def execute(self, command):
p = subprocess.Popen(command, stdout=subprocess.PIPE)
t = Thread(target=self.enqueue, args=(p.stdout, self.messaging_queue))
t.daemon = True
t.start()
def enqueue(self, stdout, queue):
for line in iter(stdout.readline, b''):
queue.put(line.decode())
stdout.close()
def command_event(self, event):
# This point is never reached
print('message received')
if __name__ == '__main__':
manager = MultipleProcessLauncher()
manager.execute('ipconfig')
time.sleep(100)
Upvotes: 4
Views: 1431
Reputation: 29591
Qt's cross-thread signaling is based on event loop, so you need to exec a QApplication so that there is a main event loop to process signals from other threads. For example:
if __name__ == '__main__':
app = QApplication([])
manager = MultipleProcessLauncher()
manager.execute('ipconfig')
MAX_WAIT_MSEC = 100 * 1000 # 100 seconds
QTimer.singleShot(MAX_WAIT_MSEC, app.quit)
app.exec()
In your real application you will probably execute the manager based on user input so the execute would be in a slot, and there wouldn't be a need to quit, etc, but you get the idea.
Upvotes: 3