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!
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)