Tommaso
Tommaso

Reputation: 89

Python - PyQt : Continue after a QThread is finished

I have a for loop in a QThread, which is started from the main GUI via a push button. When the for loop is over I would like to come back to the main thread (which is inside the Gui class) and do something else. As far as I understood, one should use the join method to wait for the thread to be finished. In my case it seems that MyThread is never finished.

import sys
from PyQt5 import QtCore
import PyQt5.QtWidgets as QtW
from PyQt5.QtCore import QThread

class MyWindow(QtW.QMainWindow):

  def __init__(self):
    super().__init__()
    self.setWindowTitle('MyWindow')
    self._main = QtW.QWidget()
    self.setCentralWidget(self._main) 
    self.button = QtW.QPushButton('Do it', self)
    self.button.clicked.connect(self.MyMethod)
    self.layout = QtW.QGridLayout(self)
    self.layout.addWidget(self.button)
    self.setLayout(self.layout)


  def MyMethod(self):
    self.n = 5
    self.loadthread = MyThread(self.n)
    self.loadthread.start()
    self.loadthread.join() # Will wait for the thread until it finishes its task
    print('thread finished')


class MyThread(QThread):

    def __init__(self, n):
        QThread.__init__(self)
        self.n = n

    def run(self):
        for i in range(self.n):
            print(i)
        print('cycle finished')


if __name__ == '__main__':
    app = QtCore.QCoreApplication.instance() # checks if QApplication already exists 
    if app is None: # create QApplication if it doesnt exist 
        app = QtW.QApplication(sys.argv)
    mainGui = MyWindow()
    mainGui.show()
    app.aboutToQuit.connect(app.deleteLater)
    app.exec_()

The output of the cose is

0
1
2
3
4
cycle finished

and print('thread finished') is never reached.

Upvotes: 1

Views: 11370

Answers (1)

eyllanesc
eyllanesc

Reputation: 243897

QThread does not have the join() method, so your application should quit unexpectedly and point to the following error message.

QLayout: Attempting to add QLayout "" to MyWindow "", which already has a layout
QWidget::setLayout: Attempting to set QLayout "" on MyWindow "", which already has a layout
0
1
2
3
Traceback (most recent call last):
  File "main.py", line 24, in MyMethod
    self.loadthread.join() # Will wait for the thread until it finishes its task
AttributeError: 'MyThread' object has no attribute 'join'
4
cycle finished
Aborted (core dumped)

If you want to execute some task after it is finished executing the thread you must use the finished signal of QThread:

import sys
from PyQt5 import QtCore, QtWidgets


class MyWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('MyWindow')
        self._main = QtWidgets.QWidget()
        self.setCentralWidget(self._main) 
        self.button = QtWidgets.QPushButton('Do it')
        self.button.clicked.connect(self.my_method)
        layout = QtWidgets.QGridLayout(self._main)
        layout.addWidget(self.button)
        layout.addWidget(self.button)

    @QtCore.pyqtSlot()
    def my_method(self):
        self.n = 5
        self.loadthread = MyThread(self.n, self)
        self.loadthread.finished.connect(self.on_finished)
        self.loadthread.start()

    @QtCore.pyqtSlot()
    def on_finished(self):
        print('thread finished')


class MyThread(QtCore.QThread):
    def __init__(self, n, parent=None):
        QtCore.QThread.__init__(self, parent)
        self.n = n

    def run(self):
        for i in range(self.n):
            print(i)
        print('cycle finished')


if __name__ == '__main__':
    app = QtWidgets.QApplication.instance()
    if app is None: 
        app = QtWidgets.QApplication(sys.argv)
    mainGui = MyWindow()
    mainGui.show()
    app.aboutToQuit.connect(app.deleteLater)
    sys.exit(app.exec_())

Upvotes: 4

Related Questions