3

I am new to PySide2. I am just trying to launch a sample application and start a thread as the application starts and want to stop the thread as the application closes. When I am closing the application I am getting the following error: QThread: Destroyed while the thread is still running. The sample_ui.py is a python file that I converted from sample_ui.ui

Code:

import time
import sys
import sample_ui
from PySide2 import QtWidgets
from PySide2 import QtCore


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        main_window_ui = sample_ui.Ui_MainWindow()
        main_window_ui.setupUi(self)
        self.custom_thread = CustomThread()
        self.custom_thread.start()

    def closeEvent(self, event):
        self.custom_thread.stop()
        QtWidgets.QMainWindow.closeEvent(self, event)

class CustomThread(QtCore.QThread):
    def __init__(self):
        super(CustomThread, self).__init__()
    def run(self):
        while self.isRunning():
           print("Thread is Running")
           time.sleep(1)
    def stop(self):
        print("Thread Stopped")
        self.quit()




if __name__ == '__main__':
    app = QtWidgets.QApplication()
    main_window = MainWindow()
    main_window.show()
    sys.exit(app.exec_())

Output:

Thread is Running
Thread is Running
Thread is Running
Thread Stopped
QThread: Destroyed while thread is still running

2 Answers2

6

Explanation:

By default the QThread run() method has the following implementation:

// https://github.com/qt/qtbase/blob/5.14.1/src/corelib/thread/qthread.cpp#L601
void QThread::run()
{
    (void) exec();
}

In other words, the run method executes an event loop but when override that method you are removing the event loop by the while loop.

On the other hand if the Qt docs is reviewed:

void QThread::quit()

Tells the thread's event loop to exit with return code 0 (success). Equivalent to calling QThread::exit(0).

This function does nothing if the thread does not have an event loop.

(emphasis mine)

So if there is no event loop then the quit method will do nothing.

Solution:

A possible solution is to use isInterruptionRequested() and requestInterruption() since the first one indicates the state of the flag and the second one changes the state of that flag. On the other hand you have to wait for the thread to finish executing using the wait() method:

class CustomThread(QtCore.QThread):
    def run(self):
        while not self.isInterruptionRequested():
            print("Thread is Running")
            time.sleep(1)

    def stop(self):
        print("Thread Stopped")
        self.requestInterruption()
        self.wait()
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Thanks a lot, @eyllanesc. I tried your method and it worked. I really appreciate that you took the time to explain about the QThread run() method. It would be a great help if you could let me know about some resources or tutorials about PySide2 and using QtDesigner. Thank you. – Pratik Tayshete Feb 08 '20 at 07:42
  • @PratikTayshete 1) If you want more explanation about QThread then you should read the docs, if you do not point me to a precise question then I will not be able to answer you. 2) Use the Qt docs, do not know about tutorials or similar, I think that Google can be a good search engine for tutorials of that type. – eyllanesc Feb 08 '20 at 07:45
  • does deleteLater() method have anything to do with this – greendino Oct 31 '20 at 19:22
1

QThread::quit() does nothing if your thread doesn't have an event loop. You should use a flag that "turns off" the while inside your run() instead of checking what QThread::isRunning() returns. In addition it is advisable to always add a QThread::wait() with a specific time limit and then add (as a backup plan) a QThread::terminate().

Just as an experiment add a check for QThread::isRunning() after you call your stop() function and see what it returns.

rbaleksandar
  • 8,713
  • 7
  • 76
  • 161