Как отследить нажатие крестика PyQt5?

У меня проблема. Я создал поток с помощью библиотеки threading.
Но когда я выхожу из приложения он не останавливается.

Вот код:

from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow
import time
from threading import Thread
import sys

i = 0
stop = 0

def timer():
    global stop
    global i
    while stop == 0:
        time.sleep(0.01)
        i += 1

    if stop == 1:
        print(f"Прошло {i / 100} секунд")
        while stop == 1:
            pass

    if (stop == 0):
        timer()

th1 = Thread(target=timer)

class Window(QMainWindow):
    def __init__(self):
        super(Window,self).__init__()

        self.setWindowTitle("Pyqt5")
        self.setGeometry(100,200,300,500)

        self.main_text = QtWidgets.QLabel(self)
        self.main_text.setText("""Нажимте на кнопку timer через 10 секунд. 
        Нажмите на кнопку start, чтобы начать.""")
        self.main_text.move(50,100)
        self.main_text.adjustSize()

        self.start_button = QtWidgets.QPushButton(self)
        self.start_button.setText("START")
        self.start_button.move(50,150)
        self.start_button.adjustSize()
        self.start_button.clicked.connect(self.start)

        self.timer_button = QtWidgets.QPushButton(self)
        self.timer_button.setText("TIMER")
        self.timer_button.move(150,150)
        self.timer_button.adjustSize()
        self.timer_button.clicked.connect(self.timer_clk)

    def start(self):
        global i
        global stop
        if (i == 0):    
            th1.start()
        i = 0
        stop = 0

    def timer_clk(self):
        global stop
        stop = 1

def application():
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    application()

Ответы (1 шт):

Автор решения: S. Nick

Класс QThread предоставляет независимый от платформы способ управления потоками.

Класс QLayout является базовым классом менеджеров геометрии.

void QWidget::closeEvent(QCloseEvent *event)
Этот обработчик событий вызывается с данным событием, когда Qt получает запрос на закрытие окна для виджета верхнего уровня из оконной системы.

import sys
#import time
#from threading import Thread
from PyQt5.QtWidgets import QApplication, QMainWindow, \
    QLabel, QPushButton, QWidget, QGridLayout, QProgressBar
from PyQt5.QtCore import Qt, QThread, pyqtSignal


class Thread(QThread):
    valueChanged = pyqtSignal(int)  # Сигнал изменения значения
    
    def __init__(self, *args, **kwargs):
        super(Thread, self).__init__(*args, **kwargs)
        self.percent = 0
        self.running = True        
    
    def run(self):
        while self.running:
            self.percent += 1
            self.valueChanged.emit(self.percent)
            self.msleep(1000)


class Window(QMainWindow):
    def __init__(self):
        super(Window, self).__init__()
        
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)

        self.main_text = QLabel(self)
        self.main_text.setText("""Нажимте на кнопку "TIMER" через 10 секунд. 
Нажмите на кнопку "START", чтобы начать.""")

        self.start_button = QPushButton(self)
        self.start_button.setText("START")
        self.start_button.clicked.connect(self.start)

        self.timer_button = QPushButton(self)
        self.timer_button.setText("TIMER")
        self.timer_button.clicked.connect(self.timer_clk)
        self.timer_button.setEnabled(False)
        
        self.progressBar = QProgressBar(self)        
        self.progressBar.setRange(0, 100)
        
        layout = QGridLayout(self.central_widget)
        layout.addWidget(self.main_text, 1, 1, 1, 2, alignment = Qt.AlignCenter)
        layout.addWidget(self.start_button, 2, 1)
        layout.addWidget(self.timer_button, 2, 2)
        layout.addWidget(self.progressBar, 4, 1, 1, 2)
        
        layout.setRowStretch(0, 1)
        layout.setRowStretch(3, 1)
        layout.setColumnStretch(0, 1)
        layout.setColumnStretch(3, 1)
        
        self.thread = Thread(self)
        self.thread.valueChanged.connect(self.on_update)

    def on_update(self, data):
        self.progressBar.setValue(data + 1)
        self.progressBar.setFormat(f'Прошло {data} секунд.')  #
        if data == 100:
            self.thread.running = False
            self.toggle_buttons()
            self.thread.percent = 0

    def start(self):
        self.thread.running = True
        self.toggle_buttons()
        self.thread.start()

    def timer_clk(self):
        self.thread.running = False
        self.toggle_buttons()
        
    def toggle_buttons(self):
        self.start_button.setEnabled(not self.start_button.isEnabled())
        self.timer_button.setEnabled(not self.timer_button.isEnabled())
        
    def closeEvent(self, event):
        self.thread.running = False
        if self.thread.isRunning():
            self.thread.quit()
            self.thread.wait()  
        super(Window, self).closeEvent(event)
            

def application():
    app = QApplication(sys.argv)
    app.setStyle("Fusion")
    window = Window()
    window.setWindowTitle("PyQt5")
    window.resize(300, 500)
    window.show()
    sys.exit(app.exec())

if __name__ == "__main__":
    application()

введите сюда описание изображения

введите сюда описание изображения

→ Ссылка