alisianoi
alisianoi

Reputation: 2353

PyQt5: how to make QThread return data to main thread

I am a PyQt 5.4.1-1 beginner, my python is 3.4.3. Here is my attempt to follow the many blogposts and SO questions on the proper ™ way to make a thread (i.e. no QThread subclassing):

#!/usr/bin/env python3

from PyQt5.QtCore import QObject, QThread
from PyQt5.QtCore import pyqtSlot, pyqtSignal
from PyQt5.QtWidgets import QMainWindow

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        print("Base init")

        self.thread = QThread()
        w = Worker()
        w.finished[int].connect(self.onFinished)
        w.moveToThread(self.thread)
        self.thread.started.connect(w.work)
        self.thread.start()

    @pyqtSlot(int)
    def onFinished(self, i):
        print("Base caught finished, {}".format(i))

class Worker(QObject):
    finished = pyqtSignal(int)

    def __init__(self):
        print("Worker init")
        super().__init__()

    def work(self):
        print("Worker work")
        Worker.finished.emit(42)

if __name__ == "__main__":
    import sys
    from PyQt5.QtWidgets import QApplication

    app = QApplication(sys.argv)
    mw = MainWindow()
    mw.show()

    sys.exit(app.exec_())

While writing this question, I realised that if the following changes are made, then it all seems to work:

...
self.w = Worker()
self.w.finished[int].connect(self.onFinished)
self.w.moveToThread(self.thread)
self.thread.started.connect(self.w.work)
...

...
self.finished.emit(42)
...

But I do not understand why this helps. Why do I have to make a non-Gui related Worker instance a member of the Gui class? Feels wrong, frankly. And now about the signal: it's a class attribute so why is there a difference between calling Worker.finished.emit(42) which fails at runtime and a self.finisehd.emit(42) which succeeds when I would expect no difference (it's a class attribute!)

Upvotes: 3

Views: 7952

Answers (1)

nullstellensatz
nullstellensatz

Reputation: 774

First, Python is an automatically garbage collected language. Your variable w goes out of scope in the __init__ method, and is promptly garbage collected after the method returns. This is why things did not work out the way you expected the first time. Making the variable a member of the class makes sure that it does not go out of scope while the MainWindow instance exists.

Your second question is a bit harder to answer, but I bet you will gain valuable insight into how signals work in PySide/PyQt by looking at the QMetaObject documentation.

--EDIT--

Found a better answer for your second question here.

Upvotes: 1

Related Questions