Блокировка QMessageBox при выполнении цикла while в дополнительном потоке

Есть код, который изначально был сделан в одном потоке, но блокировался основной потоке при работе бесконечного цикла (while True:).

После вроде разобрался, сделал выполнение функции в другом потоке. Но при вызове перестают работать QMessageBox (программа сообщения передает, но вылетает), которые мне необходимы об уведомлении, что будильник свою работу выполнил, либо же время было некорректно установлено.
Думаю и переделываю уже 3 день все, проблема наверное в бесконечном цикле.
Собственно прошу помощи в данной проблеме и пути ее решения. Опыта у меня немного, заранее спасибо!

class WorkerThread(QThread):
    message = pyqtSignal(str)

    def __init__(self, callback, *args, **kwargs):
        super().__init__()
        self.callback = callback
        self.args = args
        self.kwargs = kwargs

    def run(self):
        self.callback(*self.args, **self.kwargs)


class Ui_MainWindow(object):

    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(331, 345)
        MainWindow.setToolButtonStyle(QtCore.Qt.ToolButtonStyle.ToolButtonTextOnly)
        self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.groupBox = QtWidgets.QGroupBox(parent=self.centralwidget)
        self.groupBox.setGeometry(QtCore.QRect(30, 10, 271, 71))
        self.groupBox.setObjectName("groupBox")
        self.groupBox.setStyleSheet("color: black; "
                                    "font-weight: bold;"
                                    "")
        self.label = QtWidgets.QLabel(parent=self.groupBox)
        self.label.setGeometry(QtCore.QRect(20, 20, 231, 41))
        self.label.setObjectName("label")
        self.label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
        self.label.setFont(QtGui.QFont("Times", 20)) #ШРИФТ И РАЗМЕР
        self.label.setStyleSheet("font-weight: bold;" 
                                 "border: 1px solid;")
        self.groupBox_2 = QtWidgets.QGroupBox(parent=self.centralwidget)
        self.groupBox_2.setGeometry(QtCore.QRect(30, 110, 271, 61))
        self.groupBox_2.setObjectName("groupBox_2")
        self.groupBox_2.setStyleSheet("color: black; "
                                    "font-weight: bold;"
                                    "")
        self.timeEdit = QtWidgets.QTimeEdit(parent=self.groupBox_2)
        self.timeEdit.setEnabled(True)
        self.timeEdit.setGeometry(QtCore.QRect(10, 20, 251, 31))
        self.timeEdit.setWrapping(False)
        self.timeEdit.setFrame(False)
        self.timeEdit.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
        self.timeEdit.setButtonSymbols(QtWidgets.QAbstractSpinBox.ButtonSymbols.UpDownArrows)
        self.timeEdit.setAccelerated(False)
        self.timeEdit.setCorrectionMode(QtWidgets.QAbstractSpinBox.CorrectionMode.CorrectToPreviousValue)
        self.timeEdit.setProperty("showGroupSeparator", False)
        self.timeEdit.setTime(QtCore.QTime(0, 0, 0))
        self.timeEdit.setObjectName("timeEdit")
        self.timeEdit.setFont(QtGui.QFont("Times", 20))
        self.timeEdit.setStyleSheet("font-weight: bold;"
                                 "border: 1px solid;")
        self.groupBox_3 = QtWidgets.QGroupBox(parent=self.centralwidget)
        self.groupBox_3.setGeometry(QtCore.QRect(29, 180, 271, 61))
        self.groupBox_3.setObjectName("groupBox_3")
        self.lineEdit = QtWidgets.QLineEdit(parent=self.groupBox_3)
        self.lineEdit.setGeometry(QtCore.QRect(10, 20, 251, 31))
        self.lineEdit.setFrame(False)
        self.lineEdit.setObjectName("lineEdit")
        self.pushButton = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(30, 260, 271, 41))
        self.pushButton.setObjectName("pushButton")
        self.line = QtWidgets.QFrame(parent=self.centralwidget)
        self.line.setGeometry(QtCore.QRect(10, 10, 20, 311))
        self.line.setFrameShape(QtWidgets.QFrame.Shape.VLine)
        self.line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
        self.line.setObjectName("line")
        self.line_2 = QtWidgets.QFrame(parent=self.centralwidget)
        self.line_2.setGeometry(QtCore.QRect(300, 10, 21, 311))
        self.line_2.setFrameShape(QtWidgets.QFrame.Shape.VLine)
        self.line_2.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
        self.line_2.setObjectName("line_2")
        MainWindow.setCentralWidget(self.centralwidget)
        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def Svernut(self):
        self.showMinimized()

    def update_time(self):
        self.timer = QTimer()
        self.timer.timeout.connect(self.update_time)
        self.timer.start(1000)
        current_time = QDateTime.currentDateTime().toString('hh:mm:ss')
        self.label.setText(current_time)

    def run_alarm(self):
        self.worker = WorkerThread(self.alarm)
        self.worker.message.connect(self.show_message)
        self.worker.start()

    def alarm(self):
        now = datetime.now()
        t_mod = self.timeEdit.time()
        while True:
            now = datetime.now()
            if now.hour <= t_mod.hour():
                if now.minute == t_mod.minute():
                    self.worker.message.emit('Будильник')
                    print("YES")

                    break

                elif t_mod.minute() <= now.minute:
                    print("NO")
                    break

                else:
                    pass
            else:
                pass

    def show_message(self, message):
        print(message)
        QtWidgets.QMessageBox.information(self, 'Bудильник', message)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Будильник"))
        self.groupBox.setTitle(_translate("MainWindow", "Текущее время"))
        self.groupBox_2.setTitle(_translate("MainWindow", "Будильник"))
        self.groupBox_3.setTitle(_translate("MainWindow", "Сообщение"))
        self.pushButton.setText(_translate("MainWindow", "Поставить будильник"))
        self.update_time()
        self.pushButton.clicked.connect(self.run_alarm)

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec())

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

Автор решения: S. Nick
  1. НИКОГДА НЕ ИЗМЕНЯЙТЕ код, сгенерированный Qt Designer, НИКОГДА.
    Создайте другой класс, который наследуется от соответствующего виджета, и используйте созданный класс для его заполнения.

  2. Бесконечный цикл нельзя использовать в основном потоке, это блокирует интерфейс.

  3. Я оставил возможный вариант с дополнительным потоком и бесконечным циклом в нем.

  4. Хотя для вашей задачи не нужен ни бесконечный цикл, ни дополнительный поток. Все что вам надо это QTimer. Подумайте как это можно реализовать только используя QTimer, если у вас что-то не получится, а вам это будет надо - зададите новый вопрос.


from datetime import datetime
from PyQt5.Qt import *
from PyQt5 import QtWidgets, QtCore, QtGui


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(331, 345)
        MainWindow.setToolButtonStyle(QtCore.Qt.ToolButtonStyle.ToolButtonTextOnly)
        self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.groupBox = QtWidgets.QGroupBox(parent=self.centralwidget)
        self.groupBox.setGeometry(QtCore.QRect(30, 10, 271, 71))
        self.groupBox.setObjectName("groupBox")
        self.groupBox.setStyleSheet("color: black; "
                                    "font-weight: bold;"
                                    "")
        self.label = QtWidgets.QLabel(parent=self.groupBox)
        self.label.setGeometry(QtCore.QRect(20, 20, 231, 41))
        self.label.setObjectName("label")
        self.label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
        self.label.setFont(QtGui.QFont("Times", 20))                 # ШРИФТ И РАЗМЕР
        self.label.setStyleSheet("font-weight: bold;" 
                                 "border: 1px solid;")
        self.groupBox_2 = QtWidgets.QGroupBox(parent=self.centralwidget)
        self.groupBox_2.setGeometry(QtCore.QRect(30, 110, 271, 61))
        self.groupBox_2.setObjectName("groupBox_2")
        self.groupBox_2.setStyleSheet("color: black; "
                                    "font-weight: bold;"
                                    "")
        self.timeEdit = QtWidgets.QTimeEdit(parent=self.groupBox_2)
        self.timeEdit.setEnabled(True)
        self.timeEdit.setGeometry(QtCore.QRect(10, 20, 251, 31))
        self.timeEdit.setWrapping(False)
        self.timeEdit.setFrame(False)
        self.timeEdit.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
        self.timeEdit.setButtonSymbols(QtWidgets.QAbstractSpinBox.ButtonSymbols.UpDownArrows)
        self.timeEdit.setAccelerated(False)
        self.timeEdit.setCorrectionMode(QtWidgets.QAbstractSpinBox.CorrectionMode.CorrectToPreviousValue)
        self.timeEdit.setProperty("showGroupSeparator", False)
        self.timeEdit.setTime(QtCore.QTime(0, 0, 0))
        self.timeEdit.setObjectName("timeEdit")
        self.timeEdit.setFont(QtGui.QFont("Times", 20))
        self.timeEdit.setStyleSheet("font-weight: bold;"
                                 "border: 1px solid;")
        self.groupBox_3 = QtWidgets.QGroupBox(parent=self.centralwidget)
        self.groupBox_3.setGeometry(QtCore.QRect(29, 180, 271, 61))
        self.groupBox_3.setObjectName("groupBox_3")
        self.lineEdit = QtWidgets.QLineEdit(parent=self.groupBox_3)
        self.lineEdit.setGeometry(QtCore.QRect(10, 20, 251, 31))
        self.lineEdit.setFrame(False)
        self.lineEdit.setObjectName("lineEdit")
        self.pushButton = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(30, 260, 271, 41))
        self.pushButton.setObjectName("pushButton")
        
        self.line = QtWidgets.QFrame(parent=self.centralwidget)
        self.line.setGeometry(QtCore.QRect(10, 10, 20, 311))
        self.line.setFrameShape(QtWidgets.QFrame.Shape.VLine)
        self.line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
        self.line.setObjectName("line")
        self.line_2 = QtWidgets.QFrame(parent=self.centralwidget)
        self.line_2.setGeometry(QtCore.QRect(300, 10, 21, 311))
        self.line_2.setFrameShape(QtWidgets.QFrame.Shape.VLine)
        self.line_2.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
        self.line_2.setObjectName("line_2")
        
        MainWindow.setCentralWidget(self.centralwidget)
        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Будильник"))
        self.groupBox.setTitle(_translate("MainWindow", "Текущее время"))
        self.groupBox_2.setTitle(_translate("MainWindow", "Будильник"))
        self.groupBox_3.setTitle(_translate("MainWindow", "Сообщение"))
        self.pushButton.setText(_translate("MainWindow", "Поставить будильник"))
#        self.update_time()
#        self.pushButton.clicked.connect(self.run_alarm)


class WorkerThread(QThread):
    message = pyqtSignal(str)

    def __init__(self):                  #(self, callback, *args, **kwargs):
        super().__init__()
#        self.callback = callback
#        self.args = args
#        self.kwargs = kwargs
        
        self.t_mod_hour = 0                                               # +++
        self.t_mod_minute = 0                                             # +++
        self.flag = False                                                 # +++

    def run(self):
        while self.flag:
            now = datetime.now()

#            if now.hour <= self.t_mod_hour:
            if now.hour == self.t_mod_hour:
                if now.minute == self.t_mod_minute:
                    self.message.emit('Будильник')
                    #print("YES")
                    self.flag = False
                elif self.t_mod_minute < now.minute:
                    self.message.emit('NO. Установите новое время для будильника.')
                    #print("NO")
                    self.flag = False
            self.msleep(1000)                                                # +++
        

class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        self.setupUi(self)
        
        self.pushButton.clicked.connect(self.run_alarm)

        self.timer = QTimer()                                          # +++
        self.timer.timeout.connect(self.update_time)                   # +++
        self.timer.start(1000)                                         # +++
#        self.update_time()


        self.worker = WorkerThread()                                   # +++
        self.worker.message.connect(self.show_message)                 # +++

# ?? def Svernut(self):
# ??     self.showMinimized()

    def update_time(self):
        current_time = QDateTime.currentDateTime().toString('hh:mm:ss')
        self.label.setText(current_time)

    def run_alarm(self):
#        self.worker = WorkerThread(self.alarm)
#        self.worker.message.connect(self.show_message)

        self.worker.t_mod_hour = self.timeEdit.time().hour()             # +++
        self.worker.t_mod_minute = self.timeEdit.time().minute()         # +++
        self.worker.flag = True                                          # +++
        self.worker.start()
      
    def show_message(self, message):
        self.lineEdit.setText(message)                                   # +++
        QtWidgets.QMessageBox.information(self, 
            'Bудильник', 
            f'{message}: {self.label.text()}')
            
    def closeEvent(self, event):
        self.worker.flag = False
        if self.worker.isRunning(): 
            self.worker.terminate()
        super().closeEvent(event)        


if __name__ == "__main__":
    import sys
    
    app = QtWidgets.QApplication(sys.argv)
#    MainWindow = QtWidgets.QMainWindow()
#    ui = Ui_MainWindow()
#    ui.setupUi(MainWindow)
#    MainWindow.show()

    w = MainWindow()
    w.show()
    sys.exit(app.exec())

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

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

→ Ссылка