0

With the below code, if you click start, a loop in qthread starts and when you click stop, it will terminate qthread by using ctypes. I know there are safer ways to terminiate qthread but since my actual code is way much longer than this, I think this method is better than other methods out there. The stop button works well for my intention but if I close gui, it will give me QThread: Destroyed while thread is still running. (even after the thread is terminated by using ctypes.pythonapi.PyThreadState_SetAsyncExc) I dont understand why I'm getting the error but I'm thinking ctypes.pythonapi.PyThreadState_SetAsyncExc doesnt actually terminate the thread but just pause the thread? I dont know.. It'd be very much appreciated if someone could tell me what I'm missing

import ctypes
import sys
import threading
import time

from PyQt5 import QtWidgets, QtCore
from PyQt5.QtCore import QThread
from PyQt5.QtWidgets import *

class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)

        self.Button_state = True
        self.pb_start.clicked.connect(self.click1_function)

        # thread setup
        self.test_thread_id = 0


    def click1_function(self):
        if self.Button_state:
            self.Button_state = False
            self.pb_start.setText('Stop')

            self.test = Thread_Test1(self)
            self.test.daemon = True
            self.test.start()

        elif self.Button_state == False:
            self.Button_state = True
            self.pb_start.setText('start')
            self.test.stop_thread()

        print('check')

    def setupUi(self, QMainWindow):
        self.resize(500, 500)
        self.centralwidget = QtWidgets.QWidget(QMainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.pb_start = QtWidgets.QPushButton(self.centralwidget)
        self.pb_start.setGeometry(QtCore.QRect(100, 100, 100, 100))
        self.pb_start.setObjectName("pb_start")
        self.pb_start.setText("Start")
        QMainWindow.setCentralWidget(self.centralwidget)

class Thread_Test1(QThread):

    def __init__(self,parent):
        super().__init__(parent)
        self.parent = parent

    def run(self):
        i = 0
        
        self.test_thread_id = int(self.currentThreadId())
        while True:
            print(i)
            i += 1
            time.sleep(1)
            print("END")
        print("End")


    def stop_thread(self):
        print (self.test_thread_id)
        res = ctypes.pythonapi.PyThreadState_SetAsyncExc(self.test_thread_id,
                                                         ctypes.py_object(SystemExit))


if __name__ == "__main__":
    app = QApplication(sys.argv)
    myApp = MyWindow()
    myApp.show()
    app.exec_()
wookidookik123
  • 79
  • 2
  • 10
  • I tried to create the same script, but without using the ctypes to stop the thread. I got the *same error*. Here's how I fixed it: on `click1_function(self)` scope, right after `self.test.stop_thread()`, try calling `self.test.wait()`. What might be happening here is that python is deleting `self.test` before the thread finishes executing properly on qt5's side (as the daemon property is set to true). – Carl HR Nov 29 '22 at 18:08
  • @CarlHR well i tried it and it didnt work. btw what method did you use to stop the thread? – wookidookik123 Nov 29 '22 at 18:21
  • 1
    Attempting to use ctypes to kill a thread is a very bad idea, and totally unnecessary. You need to stop the actual code executing inside the thread, which can be done very simply with a flag on the while-loop. – ekhumoro Nov 29 '22 at 18:39
  • @wookidookik123 simple boolean flags. Instead of using a `while (True)` loop, I made something like `while (self.getRunning())`, and on the stop function, I wrote `self.setRunning(False)`. It's simple and it works. I also used a `QMutex` as I call the stop function from the *main thread*. – Carl HR Nov 29 '22 at 18:48
  • @ekhumoro well but that method wouldn't stop thread immediately. and i need to put so many flags in order to do so – wookidookik123 Dec 01 '22 at 19:09
  • @wookidookik123 Yes, it will, if the long-running task can be broken up into into a series of steps - which is exactly what your code example does. If your *real code* doesn't work like that, threading will probably be no use at all, and you may have to use multiprocessing instead. However, the most appropriate solution really depends on the exact nature of the long-running task. Without knowing that, it's impossible to give better advice. I suggest you post a proper [mre], instead of a toy example like the one in your question (which often prove nothing). – ekhumoro Dec 01 '22 at 19:25

0 Answers0