Dear all,

I would like to use QThread for my app for computational demanding methods. Up to now, I was sublassing QThread and reimplemented the run() method. And it worked actually. Last week-end, I read several blogs, including the one of one Qt developer, that say that this is not the way to do things. Instead, it is better to create a worker that does the task and to move that worker to a thread using the moveToThread method. I managed to create a minimal example that worked with that method. I then tried to do the same using a QMainWindow, and it stopped working. I spent some time trying to understand what was wrong with my code, and was not able to find it. May be one of you can help me?

Here is a minimal python example. Both ways to use threads are implemented. If you want to test it with a QThead, please comment line 41 and uncomment line 42. If you want to use the worker method, just do the opposite. The code displays the id of the thread in the terminal and is connected to a label in the statusbar (if added using the 'add widget' entry in the file menu). Using the QThread method, everything works perfectly. The gui remains responsive and the label is updated correctly. Using the worker method, the gui is frozen and the label is updated once the thread has finished its job.

Any help would be highly appreciated and thank you in advance for your time.

If that help, I work on Archlinux using python 3.6 and PyQt 5.7.

Best regards,

Vincent

Here is the code :
Qt Code:
  1. from PyQt5 import QtWidgets, QtCore, QtGui
  2. import sys, time
  3.  
  4. class Worker(QtCore.QObject):
  5. sigStatusChanged = QtCore.pyqtSignal(int)
  6. sigTaskFinished = QtCore.pyqtSignal()
  7. def __init__(self):
  8. QtCore.QObject.__init__(self)
  9.  
  10. def task(self):
  11. for n in range(10):
  12. time.sleep(0.1)
  13. self.sigStatusChanged.emit(n)
  14. print('thread : ', int(self.thread().currentThreadId()))
  15. self.sigTaskFinished.emit()
  16.  
  17. class Thread(QtCore.QThread):
  18. sigStatusChanged = QtCore.pyqtSignal(int)
  19. sigTaskFinished = QtCore.pyqtSignal()
  20. def __init__(self, parent):
  21. QtCore.QThread.__init__(self, parent)
  22.  
  23. def run(self):
  24. for n in range(10):
  25. time.sleep(0.1)
  26. self.sigStatusChanged.emit(n)
  27. print('thread : ', int(self.thread().currentThreadId()))
  28. self.sigTaskFinished.emit()
  29.  
  30. class MainWindow(QtWidgets.QMainWindow):
  31. def __init__(self):
  32. QtWidgets.QMainWindow.__init__(self)
  33. self.setWindowTitle("Test de QThread")
  34. self.statusBar().showMessage('Welcome', 1000)
  35. self.mdiArea = QtWidgets.QMdiArea()
  36. self.setCentralWidget(self.mdiArea)
  37. self.lbl = None
  38.  
  39. fileMenu = self.menuBar().addMenu('File')
  40.  
  41. threadAction = QtWidgets.QAction('Thread', self)
  42. #threadAction.triggered.connect(self.threadWithWorker)
  43. threadAction.triggered.connect(self.threadWithThread)
  44.  
  45. addWidgetAction = QtWidgets.QAction('Add Widget', self)
  46. addWidgetAction.triggered.connect(self.addWidget)
  47. removeWidgetAction = QtWidgets.QAction('Remove Widget', self)
  48. removeWidgetAction.triggered.connect(self.removeWidget)
  49.  
  50. fileMenu.addAction(threadAction)
  51. fileMenu.addAction(addWidgetAction)
  52. fileMenu.addAction(removeWidgetAction)
  53.  
  54. print('app : ', int(self.thread().currentThreadId()))
  55.  
  56. def threadWithWorker(self):
  57. def stopThread():
  58. thread.quit()
  59. thread.wait()
  60. thread = QtCore.QThread()
  61. thread.start()
  62. worker = Worker()
  63. worker.moveToThread(thread)
  64. worker.sigTaskFinished.connect(stopThread)
  65. worker.sigStatusChanged.connect(self.updateStatus)
  66. worker.task()
  67.  
  68. def threadWithThread(self):
  69. thread = Thread(self)
  70. QtCore.QCoreApplication.processEvents()
  71. thread.sigStatusChanged.connect(self.updateStatus)
  72. thread.start()
  73.  
  74. def updateStatus(self, val):
  75. if self.lbl is not None:
  76. self.lbl.setText(str(val))
  77.  
  78. def addWidget(self):
  79. self.lbl = QtWidgets.QLabel('Test')
  80. self.statusBar().addWidget(self.lbl)
  81.  
  82. def removeWidget(self):
  83. if self.lbl is not None:
  84. self.statusBar().removeWidget(self.lbl)
  85.  
  86. if __name__ == '__main__':
  87. app = QtWidgets.QApplication(sys.argv)
  88. view = MainWindow()
  89. view.show()
  90. app.exec_()
To copy to clipboard, switch view to plain text mode