I'm making a PyQt app that allows users to select a script file from their machine, the app then executes it using exec()
on a separate QThread
then shows them the results. I've already implemented all that, and now I'm trying to add a "Stop Executing" button.
I'm not able to interrupt the script execution, which should happen whenever the user presses the "Stop Executing" button. I can't stop the QObject
's task that's executing the script or terminate the QThread
that's hosting the object.
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtCore import QObject, QThread
class Execute(QObject):
def __init__(self, script):
super().__init__()
self.script = script
def run(self):
exec(open(self.script).read())
class GUI(QMainWindow):
# Lots of irrelevant code here ...
# Called when "Start Executing" button is pressed
def startExecuting(self, user_script):
self.thread = QThread()
self.test = Execute(user_script)
self.test.moveToThread(self.thread)
self.thread.started.connect(self.test.run)
self.thread.start()
# Called when "Stop Executing" button is pressed
def stopExecuting(self):
# Somehow stop script execution
There's a ton of question related to stopping an exec()
or a QThread
, but none of them work in my case. Here's what I've tried:
thread.quit()
from GUI (kills thread after script execution ends - same with wait()
)SystemExit
from object (exits the whole app after script execution ends)thread.terminate()
from GUI (app crashes when "Stop Executing" button is pressed)run()
isn't loop based)So, is there any other solution to stop the exec()
or kill the thread right when the button is pressed?
Thank's to @ekhumoro's hint about using multiprocessing instead of multithreading, I was able to find a solution.
I used a QProcess
to execute the script, and then called process.kill()
when the "Stop Executing" button is clicked. Like so:
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtCore import QProcess
class GUI(QMainWindow):
# Lots of irrelevant code here ...
# Called when "Start Executing" button is pressed
def startExecuting(self, user_script):
self.process = QProcess()
self.process.setProcessChannelMode(QProcess.MergedChannels)
self.process.start("python", ["-u", user_script])
# Called when "Stop Executing" button is pressed
def stopExecuting(self):
self.process.kill()
This stops the script execution right away without interrupting the GUI process, which's exactly what I was looking for.