I have a rather complex code that utilizes QMessageBox through several states, showing results of processing of a separate thread, mostly showing different results/statuses of the process, but every once in a while (and also at the end of the process) offering the user some choices via the buttons – which actually works good enough for me.
The whole thing relies on the QMessageBox.exec_ blocking while this processing thread runs, and only exiting on when the user (is allowed to) clicks a button (and a couple of times, forcibly from the other thread using the .done(0)
method).
I now have the problem of wanting to run the same process at application startup, but not show the message box for the most part, – and only show it for a couple of specific (but not all) results of the process.
Since it would be quite complex to refactor this into a “no-gui” and “gui” part, I though I’d try what I thought was an easy solution: simply run the whole process including the QMessageBox instantiation, but hide the QMessageBox right before its blocking .exec_()
is called. This would have been a great idea, unfortunately it turns out that calling .hide()
causes .exec_()
to return and stop its blocking, which is demonstrated by the following example:
import sys
from PyQt5.QtWidgets import QApplication, QDialog, QMainWindow, QMessageBox, QPushButton
from PyQt5.QtCore import (QTimer)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("My App")
button = QPushButton("Press me for a dialog!")
button.clicked.connect(self.button_clicked)
self.setCentralWidget(button)
# one second after startup, run raise dialog quietly
QTimer.singleShot(1000, lambda : self.raise_dialog(quiet=True))
def button_clicked(self, s):
self.raise_dialog()
def raise_dialog(self, quiet=False):
print("some process runs here ...")
dlg = QMessageBox(self)
dlg.setWindowTitle("I have a question!")
dlg.setText("This is a simple dialog")
if quiet:
QTimer.singleShot(1000, dlg.hide) # if I just call dlg.hide() here, ,_exec shows it again; in actual code, using this with 1 ms delay, but here 1000 for test
button = dlg.exec()
print("exec() returned - proceeding with code after exec() ...")
if button == QMessageBox.Ok:
print("OK!")
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
I’ve deliberately set the hiding to occur 1 second after the QMessageBox is shown, so it is easily visible that as soon as the QMessageBox is hidden/disappears, the text “exec() returned - proceeding with code after exec() ...
” is printed on the terminal.
So, I wanted to ask – is there a way to hide a QMessageBox (which would hopefully also hide its modality, so the main window can be interacted with in the meantime), without causing it to exit/return from its .exec_
blocking call (like QMessageBox.hide() does)?