Search code examples
pythonpyqtpyqt5qmessagebox

QMessageBox to block parent while calculation


I want to use QMessageBox to block its parent QDialog, while doing calculation. I came upon something like that, but this does not work.

msgBox = QtWidgets.QMessageBox()
msgBox.setWindowTitle('Working ....')
msgBox.setText("Working, please wait ...")
msgBox.setStandardButtons(QtWidgets.QMessageBox.NoButton)
msgBox.exec_()

(doing some time consuming work)

msgBox.close()

What is wrong, how can I do it properly?


Solution

  • You have to execute the time-consuming task in another thread and signal through a signal that the task is finished executing. To close the QMessageBox you must use the accept() method:

    import threading
    
    from PyQt5 import QtCore, QtWidgets
    
    
    class Worker(QtCore.QObject):
        started = QtCore.pyqtSignal()
        finished = QtCore.pyqtSignal()
    
        def execute(self, func, args):
            threading.Thread(target=self._execute, args=(func, args,), daemon=True).start()
    
        def _execute(self, func, args):
            self.started.emit()
            func(*args)
            self.finished.emit()
    
    
    def consuming_work(arg1, arg2):
        import time
    
        print(arg1, arg2)
        time.sleep(5)
        print("finish")
    
    
    if __name__ == "__main__":
        import sys
    
        app = QtWidgets.QApplication(sys.argv)
    
        msgBox = QtWidgets.QMessageBox()
        msgBox.setWindowTitle("Working ....")
        msgBox.setText("Working, please wait ...")
        msgBox.setStandardButtons(QtWidgets.QMessageBox.NoButton)
        worker = Worker(msgBox)
        worker.finished.connect(msgBox.accept)
        worker.execute(consuming_work, ["Stack", "Overflow"])
        msgBox.exec_()
    

    Update:

    import threading
    
    from PyQt5 import QtCore, QtWidgets
    
    
    class WorkerMessageBox(QtWidgets.QMessageBox):
        started = QtCore.pyqtSignal()
        finished = QtCore.pyqtSignal()
    
        def __init__(self, parent=None):
            super().__init__(parent)
            self.finished.connect(self.accept)
    
        def execute(self, func, args):
            threading.Thread(target=self._execute, args=(func, args,), daemon=True).start()
            return self.exec_()
    
        def _execute(self, func, args):
            self.started.emit()
            func(*args)
            self.finished.emit()
    
    
    def consuming_work(arg1, arg2):
        import time
    
        print(arg1, arg2)
        time.sleep(5)
        print("finish")
    
    
    if __name__ == "__main__":
        import sys
    
        app = QtWidgets.QApplication(sys.argv)
    
        msgBox = WorkerMessageBox()
        msgBox.setWindowTitle("Working ....")
        msgBox.setText("Working, please wait ...")
        msgBox.setStandardButtons(QtWidgets.QMessageBox.NoButton)
    
        msgBox.execute(consuming_work, ["Stack", "Overflow"])