0

I created a widget, which contains a method will start an another thread.

When I tried to connect the thread's started signal to a lambda function, it failed.

Actually, the thread has a ThreadId as same as Application instance. But when I tried to connect it to a non-lambda function, it works normally.

So, what should I do to connect to a function which requires parameters?

Here is my code:

from PyQt6.QtWidgets import QLabel, QApplication, QHBoxLayout, QPushButton, QWidget
from PyQt6.QtCore import pyqtSignal, QObject, QThread
import time
import sys

class Worker(QObject):
    send = pyqtSignal(str)
    finished = pyqtSignal()
    def __init__(self) -> None:
        super().__init__()

    def run(self, stop = 5):
        print(f"Worker thread: {int(self.thread().currentThreadId())}")
        for i in range(stop):
            time.sleep(1)
            self.send.emit(str(i))
        self.finished.emit()
    
    def runNoParam(self):
        print(f"Worker thread: {int(self.thread().currentThreadId())}")
        for i in range(5):
            time.sleep(1)
            self.send.emit(str(i))
        self.finished.emit()

class Window(QWidget):
    def __init__(self) -> None:
        super().__init__()
        layout = QHBoxLayout()
        self.label = QLabel("number")
        self.btn = QPushButton("click me")
        layout.addWidget(self.label)
        layout.addWidget(self.btn)
        self.setLayout(layout)
        self.btn.clicked.connect(self.test)


    def test(self):
        self.threader = QThread()
        self.worker = Worker()
        self.worker.moveToThread(self.threader)

        # connect to a lambda funcion, work failed!
        self.threader.started.connect(lambda: self.worker.run())

        # connect to a non-lambda function, work normally!
        # self.threader.started.connect(self.worker.runNoParam)
        
        self.worker.send.connect(lambda s: self.label.setText(s))
        
        self.worker.finished.connect(self.threader.quit)
        self.worker.finished.connect(self.threader.deleteLater)

        self.threader.start()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = Window()
    win.show()
    print(f"Application thread: {int(QApplication.instance().thread().currentThreadId())}")
    sys.exit(app.exec())
Poison
  • 75
  • 1
  • 6
  • Thanks @ekhumoro for pointing out that I should use `QThread.currentThreadId()` to identify QThread. But Now a new question occurs, when I try to print `self.test_threader.currentThreadId()`, the result is same as the result returned by `self.thread().currentThreadId()` – Poison Mar 15 '23 at 14:34
  • 1
    That's because you're executing all the code in the main thread. The QThread objects you create never do anything. – ekhumoro Mar 15 '23 at 20:48
  • Thanks, @ekhumoro again, I have updated my question, can you help me solve my confusion? Thanks very much! – Poison Mar 16 '23 at 07:58
  • @Poison If you check the documentation you'll see that `currentThreadId` is a static member function of `QThread`. As such. it doesn't matter which `QThread` instance it's called against the call is always effectively `QThread.currentThreadId()`. – G.M. Mar 16 '23 at 13:15
  • Thanks @G.M., now I have a confusion. Why, when I use `self.threader.started.connect (lambda: self.worker.run())`, the child thread has the same ID as the main thread so that the workerthreader cannot work properly. But when I use `self.threader.started.connect(self.worker.run)`, the child works normally. – Poison Mar 16 '23 at 14:06
  • @Poison That's a different issue and should be posted as a new question. – G.M. Mar 16 '23 at 14:08
  • 1
    @Poison The `lambda` is in the main thread, so it will be executed from there. The whole point of moving an object to another thread is to call its slots in a different thread context. Parameters can be passed in via `__init__` and stored as attributes. – ekhumoro Mar 16 '23 at 20:56

0 Answers0