I tried this example from StackOverflow:
How to get a child thread to close when main GUI window is closed in pyqt5 / python 3?
I would like this child thread to terminate when I close the main application. In this example, the child thread is a simple counter. When I close the main GUI, the counter still keeps going. How can I get the thread to end when the GUI window is closed?
My code is here:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Apr 11 12:41:26 2020
@author: Pietro
"""
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import (QWidget, QApplication,QPushButton,
QVBoxLayout)
from PyQt5.QtCore import QThread
import time, threading, sys
class testScriptApp(QtWidgets.QWidget):
def __init__(self, parent=None):
# initialize the widget
QtWidgets.QWidget.__init__(self, parent)
# set the window title
self.setWindowTitle("Scripting")
# manage the layout
self.center()
self.resize(400,400)
self.mainGrid = QVBoxLayout()
self.button = QPushButton('Start')
self.button.clicked.connect(self.on_click)
self.mainGrid.addWidget(self.button)
self.setLayout(self.mainGrid)
def center(self):
qr = self.frameGeometry()
cp = QtWidgets.QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
def on_click(self):
global running
print('global running : ' , running)
self.worker = Worker()
self.worker.run()
def closeEvent(self, event):
global running
running = False
print('Closing')
# self.worker.terminate()
event.accept()
class Worker(QThread):
def __init__(self):
QThread.__init__(self)
def run(self):
count=1
global running
while count>0 and not running:
# while count>0:
print('counter on: ',count, running)
time.sleep(2)
count+=1
else:
print('out of loop')
return
if __name__ == "__main__":
running = False
app = QtWidgets.QApplication(sys.argv)
myapp = testScriptApp()
myapp.show()
app.exec_()
I started trying to learn Python3 weeks ago so please be gentle. What did I do wrong here?
From : QThread : run() Vs start()
From the docs
QThread begins executing in run(). To create your own threads, subclass QThread and reimplement run() Use the start() method to begin execution. Execution ends when you return from run(), just as an application does when it leaves main() To create your own thread you'll always need to subclass QThread and reimplement run(). To start the thread you'll need to call start().
I hope it's clear now.
So like alec said
You should call
.worker.start()
instead of run()
.
If you set self.worker.setTerminationEnabled(True)
then in the closeEvent
self.worker.terminate()
will terminate the thread. –
Changed code:
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import (QWidget, QApplication,QPushButton,
QVBoxLayout)
from PyQt5.QtCore import QThread
import time, threading, sys
class testScriptApp(QtWidgets.QWidget):
def __init__(self, parent=None):
# initialize the widget
QtWidgets.QWidget.__init__(self, parent)
# set the window title
self.setWindowTitle("Scripting")
# manage the layout
self.center()
self.resize(400,400)
self.mainGrid = QVBoxLayout()
self.button = QPushButton('Start')
self.button.clicked.connect(self.on_click)
self.mainGrid.addWidget(self.button)
self.setLayout(self.mainGrid)
def center(self):
qr = self.frameGeometry()
cp = QtWidgets.QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
def on_click(self):
global running
print('global running : ' , running)
self.worker = Worker()
self.worker.setTerminationEnabled(True)
self.worker.start()
def closeEvent(self, event):
global running
# running = False ## doesnt print 'out of loop' line
running = True ## does print 'out of loop' line
# self.worker.terminate() # if not commented out doesnt print the 'out of loop' line
### self.worker Execution ends when you return from run()
time.sleep(5)
print('Closing')
event.accept()
class Worker(QThread):
def __init__(self):
QThread.__init__(self)
def run(self):
count=1
global running
while count>0 and not running:
print('counter on: ',count, running)
time.sleep(2)
count+=1
else:
print('out of loop')
return
if __name__ == "__main__":
running = False
app = QtWidgets.QApplication(sys.argv)
myapp = testScriptApp()
myapp.show()
app.exec_()
output:
global running : False
counter on: 1 False
counter on: 2 False
out of loop
Closing
uncommenting self.worker.terminate()
, output:
global running : False
counter on: 1 False
counter on: 2 False
counter on: 3 False
counter on: 4 False
counter on: 5 False
Closing
Note that I added time.sleep(5)
in closeEvent to allow the Qthread to print the out of loop
line.
Remember as in the link above self.worker Execution ends when you return from run() allowing terminate with self.worker.setTerminationEnabled(True)
will allow to call self.worker.terminate()
at any point.