Search code examples
pythonpyqtpopuppyqt4pyqt5

How to create simple PyQt popup in Python function and close at the end of it?


I'm running a function in which I would like to open a PyQt popup that says something like "Waiting..." which lasts as long as it takes for the function to finish. Afterwards I would like to change the text to "Complete", sleep for a second or two, and close the popup. I've tried several many ways to do this but have been so far unsuccessful. It looks like any time I call app.exec_() the function halts the function until I close the popup. I want to do this outside of the context of the main event loop of PyQt (I tried running this event loop asynchronously to no avail). If I don't call exec_() I never see the popup at all. Basically I want something like this:

# this is the function that is being run
def run():
    create_popup() # show popup
    wait_for_some_messages() # do some stuff
    close_popup() # close popup

def create_popup():
    app = QApplication([])
    popup = Popup()
    popup.show()

class Popup(QDialog):
    super().__init__()
    self.label = QLabel(self) 

Any help is much appreciated. Thanks!


Solution

  • I think the task you want to execute is heavy so you can block the GUI loop so it is recommended to implement a QRunnable and launch it with QThreadPool, in the run method the heavy function will be called, in the end this task will update the data through QMetaObject::invokeMethod, then we will use a QEventLoop to wait 2 seconds and call the method close.

    class Runnable(QRunnable):
        def __init__(self, popup, func):
            QRunnable.__init__(self)
            self.popUp = popup
            self.func = func
    
        def run(self):
            # execute hard function
            self.func()
            # Update the text shown in the popup
            QMetaObject.invokeMethod(self.popUp, "setText",
                                     Qt.QueuedConnection,
                                     Q_ARG(str, "Complete"))
            # wait 2 seconds
            loop = QEventLoop()
            QTimer.singleShot(2000, loop.quit)
            loop.exec_()
            # call the close method of the popup
            QMetaObject.invokeMethod(self.popUp, "close",
                                     Qt.QueuedConnection)
    
    
    class PopUp(QDialog):
        def __init__(self, *args, **kwargs):
            QDialog.__init__(self, *args, **kwargs)
            self.setLayout(QVBoxLayout())
            self.label = QLabel(self)
            self.layout().addWidget(self.label)
    
        @pyqtSlot(str)
        def setText(self, text):
            self.label.setText(text)
    
    # emulate the heavy task
    def func():
        for i in range(10000):
            print(i)
            QThread.msleep(1)
    
    if __name__ == '__main__':
        import sys
    
        app = QApplication(sys.argv)
        w = PopUp()
        w.show()
        w.setText("Waiting...")
        runnable = Runnable(w, func)
        QThreadPool.globalInstance().start(runnable)
        w.exec_()
    

    plus:

    If you want to call another function just change:

    runnable = Runnable(w, func)
    

    to:

    runnable = Runnable(w, name_of_your_function)