Uploaded image for project: 'Qt for Python'
  1. Qt for Python
  2. PYSIDE-2832

QThread.startup signal connection has unexpected behaviour

    XMLWordPrintable

Details

    • Bug
    • Resolution: Invalid
    • Not Evaluated
    • None
    • 6.7.2
    • PySide
    • None
    • Brand new virtual environment, with python 3.12.2 and Pyside6 6.7.2.
    • Windows

    Description

      I seem to be unable to get QThread's `started` signal to behave properly.

      When simply running the following:

      import logging
      import time
      
      from PySide6 import QtCore
      
      logging.basicConfig(level=logging.INFO)
      
      
      class Controller(QtCore.QObject):
          def manage(self):
              worker = Worker()
      
              thread = QtCore.QThread()
      
              thread.started.connect(worker.do_work)
              worker.finished.connect(thread.quit)
      
              worker.moveToThread(thread)
      
              thread.start()
      
              self.worker_thread = thread
      
      
      class Worker(QtCore.QObject):
          finished = QtCore.Signal()
      
          @QtCore.Slot()
          def do_work(self):
              logging.info("Starting doing work")
      
              time.sleep(2)
      
              logging.info("Finished doing work")
      
              self.finished.emit()
      
      
      if __name__ == "__main__":
          controller = Controller()
          controller.manage()
      
          logging.info("Returned from `controller.manage()`")
      
          controller.worker_thread.wait(2000)
      

      the `do_work` method is never run. This is the output:

      INFO:root:Returned from `controller.manage()`
      
      Process finished with exit code 0
      

      Also note that not inheriting QObject for Controller leads to this exit code:

      Process finished with exit code -1073740791 (0xC0000409) 
      

      which I believe is linked to killing a QThread while it is running.

       

      The only workaround that I have found to run the method is to connect the `worker.do_work` slot to a dummy signal. By connecting `controller.dummy_signal` to `worker.do_work`, the slot is called correctly in the worker thread, even though the dummy signal is never emitted. I've tested it with a barebones `QtWidgets.QApplication` and the GUI is responsive while the worker does work which indicates it is indeed running in another thread.

      Another requirement is that the `thread.started.connect(worker.do_work)` connection does not have a specified connection type. Trying any value of the enum leads to the worker method not running (even using `AutoConnection` which is the default).
      However, providing a connection type for the dummy signal connection is also required and using any value from the enum leads to the application working (if previous conditions are fullfilled).

      Here is the working example:

      import logging
      import time
      
      from PySide6 import QtCore
      
      logging.basicConfig(level=logging.INFO)
      
      
      class Controller(QtCore.QObject):
          dummy_signal = QtCore.Signal()
      
          def manage(self):
              worker = Worker()
      
              thread = QtCore.QThread()
      
              thread.started.connect(worker.do_work)
              worker.finished.connect(thread.quit)
      
              worker.moveToThread(thread)
      
              thread.start()
      
              self.dummy_signal.connect(worker.do_work, QtCore.Qt.DirectConnection)
      
              self.worker_thread = thread
      
      
      class Worker(QtCore.QObject):
          finished = QtCore.Signal()
      
          @QtCore.Slot()
          def do_work(self):
              logging.info("Starting doing work")
      
              time.sleep(2)
      
              logging.info("Finished doing work")
      
              self.finished.emit()
      
      
      if __name__ == "__main__":
          controller = Controller()
          controller.manage()
      
          logging.info("Returned from `controller.manage()`")
      
          controller.worker_thread.wait(2000)
      

      Which outputs:

      INFO:root:Starting doing work
      INFO:root:Returned from `controller.manage()`
      INFO:root:Finished doing work
      
      Process finished with exit code 0
      

      I'm still relatively knew to Pyside so I'm more than happy to be corrected if there is something wrong with my approach but this does appear to be unexpected behaviour.

       

      In summary:
      Connecting QThread's `started` signal has no effect unless the target of the signal is also connected to another dummy signal.

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            crmaurei Cristian Maureira-Fredes
            johnreports Olivier Delree
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes