I need to safely interrupt/stop a thread, and I am attempting to use requestInterrupt() to get a safe stop. I have been searching around google and stackoverflow, but it seems I cannot get it to stop the thread at all. It is not producing any errors, just moving on like I never made the request.
isInterruptRequested() does not change at all. It should be True when an interrupt is requested, but it is always false.
MRE of my code:
import sys
from time import sleep
from pathlib import Path
#PyQt5 Imports
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(343, 362)
self.centralwidget = QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayoutWidget = QWidget(self.centralwidget)
self.gridLayoutWidget.setGeometry(QRect(20, 20, 301, 281))
self.gridLayoutWidget.setObjectName("gridLayoutWidget")
self.gridLayout = QGridLayout(self.gridLayoutWidget)
self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.gridLayout.setObjectName("gridLayout")
self.stopSearch = QPushButton(self.gridLayoutWidget)
self.stopSearch.setEnabled(False)
self.stopSearch.setObjectName("stopSearch")
self.gridLayout.addWidget(self.stopSearch, 1, 1, 1, 1)
self.startSearch = QPushButton(self.gridLayoutWidget)
self.startSearch.setObjectName("startSearch")
self.gridLayout.addWidget(self.startSearch, 1, 0, 1, 1)
self.textEdit = QTextEdit(self.gridLayoutWidget)
self.textEdit.viewport().setProperty("cursor", QCursor(Qt.ForbiddenCursor))
self.textEdit.setAutoFormatting(QTextEdit.AutoBulletList)
self.textEdit.setUndoRedoEnabled(False)
self.textEdit.setReadOnly(True)
self.textEdit.setAcceptRichText(False)
self.textEdit.setObjectName("textEdit")
self.gridLayout.addWidget(self.textEdit, 0, 0, 1, 2)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "Test Window"))
self.stopSearch.setText(_translate("MainWindow", "Stop Task"))
self.startSearch.setText(_translate("MainWindow", "Start Task"))
class contactPuller(QMainWindow, Ui_MainWindow):
def __init__(self):
super(contactPuller, self).__init__()
self.setupUi(self)
self.thread = QThread()
self.worker = searchWorker()
self.worker.moveToThread(self.thread)
self._connectActions()
def _connectActions(self):
self.startSearch.clicked.connect(self.searchStart)
self.stopSearch.clicked.connect(self.searchStop)
self.thread.started.connect(self.worker.run)
self.worker.finished.connect(self.thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.thread.finished.connect(self.thread.deleteLater)
self.worker.progress.connect(self.reportProgress)
self.thread.finished.connect(lambda: self.startSearch.setEnabled(True))
def searchStart(self):
self.textEdit.clear()
self.startSearch.setEnabled(False)
self.stopSearch.setEnabled(True)
self.thread.start()
def searchStop(self):
self.textEdit.append('searchStop called')
self.startSearch.setEnabled(True)
self.stopSearch.setEnabled(False)
self.worker.requestInterruption()
self.thread.requestInterruption()
def reportProgress(self, text):
self.textEdit.append(text)
class searchWorker(QThread):
finished = pyqtSignal()
progress = pyqtSignal(str)
def __init__(self):
super(QThread, self).__init__()
def save(self):
self.progress.emit("Save Call")
def _terminate(self):
self.progress.emit("_terminate call")
def run(self):
while True:
sleep(2)
self.progress.emit(f'Interrupt reqested: {self.isInterruptionRequested()}')
print(f'Interrupt reqested: {self.isInterruptionRequested()}')
if self.isInterruptionRequested():
self.browser.quit()
self.save()
self.thread.stop()
self.thread.wait()
self.progress.emit('Stop Call')
if __name__ == "__main__":
app = QApplication([])
app.setStyle('Fusion')
cntctChkr = contactPuller()
cntctChkr.show()
sys.exit(app.exec())
EDIT: Changed truncated snippet into "working" example
CodePudding user response:
You are subclassing QThread, but you're not using it as such, only as a simple QObject.
Any call to requestInterruption()
is ignored if the thread for which it is called is not running (or is finished).
While you are running the run
function from another thread, you're not running the worker
thread.
The solution is simple: don't use another QThread, use the one you already have.
class ContactPuller(QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.worker = SearchWorker()
self._connectActions()
def _connectActions(self):
self.startSearch.clicked.connect(self.searchStart)
self.stopSearch.clicked.connect(self.searchStop)
self.worker.progress.connect(self.reportProgress)
self.worker.finished.connect(self.workerFinished)
def searchStart(self):
self.textEdit.clear()
self.startSearch.setEnabled(False)
self.stopSearch.setEnabled(True)
self.worker.start()
def searchStop(self):
self.textEdit.append('searchStop called')
self.stopSearch.setEnabled(False)
self.worker.requestInterruption()
def reportProgress(self, text):
self.textEdit.append(text)
def workerFinished(self):
self.startSearch.setEnabled(True)
class SearchWorker(QThread):
progress = pyqtSignal(str)
def __init__(self):
super().__init__()
def save(self):
self.progress.emit("Save Call")
def _terminate(self):
self.progress.emit("_terminate call")
def run(self):
while True:
sleep(2)
interrupt = self.isInterruptionRequested()
self.progress.emit(f'Interrupt requested: {interrupt}')
print(f'Interrupt requested: {interrupt}')
if interrupt:
self.save()
self.progress.emit('Stop Call')
break