Issue
I have a PyQt6 application which aims to detect objects. I inherited QThread to a user-defined class named "StartProcessWorkerThread" to create a worker instance once a start button (a QPushButton) gets clicked by the user.
This is the worker class that inherited from the QThread class
class StartProcessWorkerThread(QThread):
processing_finished = pyqtSignal(dict) # Signal for determining if the thread/worker has finished
progress_updated = pyqtSignal(int) # Signal for determining if the progress bar value was updated
# Initialization
def __init__(self, input_video_filepaths, weapons_to_detect, clothings_to_detect_and_colors, username):
super().__init__()
self.input_video_filepaths = input_video_filepaths
self.weapons_to_detect = weapons_to_detect
self.clothings_to_detect_and_colors = clothings_to_detect_and_colors
self.username = username
# Run method that emits the resulting data after running the method run_main_driver_code()
def run(self):
table_data = self.run_main_driver_code()
self.processing_finished.emit(table_data)
# This method is ran by run() method which runs the imported main_driver_code() from another .py file
def run_main_driver_code(self):
user_specific_table_data = cwd.main_driver_code(
input_video_filepaths = self.input_video_filepaths,
weapons_to_detect = self.weapons_to_detect,
clothings_to_detect_and_colors = self.clothings_to_detect_and_colors,
username=self.username,
progress_callback=self.progress_updated
)
return user_specific_table_data # returns the data returned by the imported main_driver_code() function or method from another .py file
From a QMainWindow() instance (i.e., the main window of the application), after clicking the start button, a method gets called which creates the worker or thread instance and uses its methods to start or run the cwd.main_driver_code() from earlier. This is how the instance is created and how it gets started and get it running:
self.worker = StartProcessWorkerThread(filtered_video_filepaths, weapons_to_detect, clothings_to_detect_and_colors, self.userloginname.text()) # Create thread worker instance and pass necessary parameters
# Start running the thread worker instance to detectm generate table data, and update QTableWidget in main section with it
self.worker.start()
# When the PyQt signal (progress_updated) gets updated from detect_weapon.py or detect_clothings.py, update the value of QProgressBar with the emitted value (detect_weapon.py - Line 255; detect_clothings.py - Line 290)
self.worker.progress_updated.connect(self.updateProgressBar)
# When the PyQt signal (processing_finished) is finished, update the table data by executing self.updateTableData
self.worker.processing_finished.connect(self.updateTableData)
# Enable input widgets after the thread worker is finished detecting and providing the table data
self.worker.finished.connect(self.disableEnableDetectionButtons)
# Show Start Button again and hide the Progress Bar
self.worker.finished.connect(self.hideUnhideStartButtonAndProgressBar)
I tried the methods quit() and exit() but these did not stop or the StartProcessWorkerThread(QThread) instance. I also tried the terminate() method which stops the process but the app hangs and crashes after pressing or clicking the start button again.
Does anyone have an idea on how to stop this StartProcessWorkerThread(QThread) instance that is currently running without any problems after doing so? Any answers or suggestions will be highly appreciated. If more information is needed, ask away and I'll do what I can to provide them. Kindly refer to the comments for the details or explanations for each line. Thank you in advance.
Solution
Here's a minimal example of how you can stop a running thread. You don't actually stop 'the thread' but the process that's running in the thread.
Since a callback function was already used, it makes sense to use a callback to stop the process. The callback function serves two purposes: It emits the progress_update
signal, and returns the value of isInterruptionRequested()
. That way long_process
knows whether it should continue by checking the return value of the callback function.
import time
from typing import Callable
from PyQt6.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QVBoxLayout
from PyQt6.QtCore import QThread, pyqtSignal
class Worker(QThread):
progress_update = pyqtSignal(int)
def run(self):
long_process(self.callback)
def callback(self, update: int):
self.progress_update.emit(update)
return self.isInterruptionRequested()
def long_process(callback: Callable):
for i in range(1000):
time.sleep(0.5)
if callback(i):
break
class Window(QWidget):
def __init__(self):
super().__init__()
self.worker = Worker()
label = QLabel()
self.worker.progress_update.connect(lambda i: label.setText(str(i)))
self.worker.finished.connect(lambda: label.setText('finished'))
p1 = QPushButton('start')
p2 = QPushButton('stop')
p1.clicked.connect(self.worker.start)
p2.clicked.connect(self.worker.requestInterruption)
layout = QVBoxLayout(self)
layout.addWidget(label)
layout.addWidget(p1)
layout.addWidget(p2)
app = QApplication([])
win = Window()
win.show()
app.exec()
Answered By - mahkitah
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.