How to close all QMessageBoxes



  • Is there a way to close all QMessageBoxes which might be open at the time, without having to keep track of them yourself?



  • @
    QWidgetList allToplevelWidgets = QApplication::topLevelWidgets();
    foreach (QWidget *w, allToplevelWidgets) {
    if (w->inherits("QMessageBox") {
    QMessageBox *mb = qobject_cast<QMessageBox *>(w);
    mb->close();
    }
    }
    @

    (Brain to terminal, not tested)



  • Even if it doesn't, it's the pointer I needed. Thanks!



  • You can use the QObjectCleanupHandler, adding each messagebox at creation.

    If you call clear it will destruct every QObject added by the add function.

    It also take car of watch if the object already have been delete someone else.

    @
    void Dialog::on_pushButton_clicked()
    {
    QMessageBox *msgBox_A = new QMessageBox(this);
    msgBox_A->setModal( false );
    msgBox_A->setText("Message A.");
    msgBox_A->show();

    MyObjectHandler->add( msgBox_A );
    
    QMessageBox *msgBox_B = new QMessageBox(this);
    msgBox_B->setModal( false );
    msgBox_B->setText("Message B.");
    msgBox_B->show();
    
    MyObjectHandler->add( msgBox_B );
    

    }

    void Dialog::on_pushButton_2_clicked()
    {
    MyObjectHandler->clear();
    }
    @

    Enjoy !!!



  • After some more thinking, I realized that I want to solve a slightly different problem than the one I described above:

    I want to close all modal dialogs.

    The ObjectHandler approach does not appeal to me, because whoever adds a modal dialog later on has to remember to add the handler. If I make specific sub-classes that wrap the object handler stuff, they still have to remember to use them, not the normal Qt classes.

    I like the QApplication approach better.

    The simplest way to do so seems to be a loop like this:

    @
    // for loop instead of while prevents an endless loop
    for (int i = 0; i < 100; ++i)
    {
    QWidget* pWidget = QApplication::activeModalWidget();
    if ( ! pWidget)
    {
    break;
    }

        pWidget->hide();
    }@
    

    The interesting thing is...I have no idea why it works

    A look into the Qt sources reveals that this might cause trouble: The widget is removed from the stack of modal widgets only when it is destroyed. However, the close() call of a widget might or might not delete it (depending on the Qt::WA_DeleteOnClose flag), and even if it does close it, it only does so using deleteLater().

    My conclusion is that the loop would never exit, as the event loop must run before the widget is actually closed, and therefore I would get the same widget multiple times when calling activeModalWidget().

    The fact is however: It does work. At least on Windows.



  • Hiding a modal window removes it from the modal stack.

    I would still go with the (slightly modified) version of Volker, because it is cleaner and less error-prone.
    @
    foreach (QWidget *widget, QApplication::topLevelWidgets())
    {
    QDialog dialog = qobject_cast<QDialog>(widget);
    if ((dialog != 0) && (dialog->isModal()))
    {
    dialog->close(); // or delete dialog;
    // depending on your definition of close
    }
    }
    @



  • The docs do not guarantee the order in which the topLevelWidgets arrive. Therefore, using activeModalWidget() felt safer to me.

    And why the cast to QDialog? QWidget has the isModal() method.



  • Mostly because you've said you want to close all modal dialogs, not widgets ;-)

    Why do you care about the order of the modal dialogs as all have to be closed. You do not need to close them in the order they appear on the modal stack.

    Your code isn't safe at all. If a single dialog reimplements the closeEvent and refuses to close you are trapped in the loop as the active modal window won't change. And adding some artifical upper boundary like 100 is code that you don't want to write. It is nigh impossible to comprehend, has an expiry date when there are more then n modal dialogs and is wasting resources when there are less.



  • Modal windows must be closed in the correct order. Doing it any other way would be like leaving a method somewhere in the middle of the call stack. Which is what you would do, because the modal windows make up the call stack for the GUI thread, from the first on that had been opened, to the current top modal window.

    Commented properly, the for loop is a better safety net than nothing. In a proper program, I would add a logging entry in case I ever reach the end of the loop, identify the widget that doesn't behave, and fix it.

    But I agree it's a bit of a hack. The proper way would be to obtain a list of windows in their correct stacking order. And even then, there might be a message box generated by e.g. a library call, which might or might not show up in the list of modal widgets, because it might not be a QWidget at all.

    Some years ago, I spent weeks navigating all the possible pitfalls of modal window management. And that was for Windows only. I hoped to find an easier solution with Qt.



  • [quote author="Asperamanca" date="1331715180"]Modal windows must be closed in the correct order. Doing it any other way would be like leaving a method somewhere in the middle of the call stack. Which is what you would do, because the modal windows make up the call stack for the GUI thread, from the first on that had been opened, to the current top modal window.[/quote]

    What makes you think so? The documentation might be a bit misleading here, but Qt's "modal stack" isn't a stack in terms of QStack or push / pop / top, it is a simple QWidgetList.

    It just behaves like a stack in terms of that there can be only one active modal window, and as soon as it loses its modal status due to hiding or closing the next modal window (in the order of becoming modal) is activated. If the list is empty because it was the only modal window or every other modal window has been hidden or closed meanwhile the application leaves modal mode and the widget which had the focus before entering modal mode becomes active.

    You can be quite sure if hiding a non-top modal window would mess up call stacks, the rendering pipeline, event propagation or cause any other lethal thing Qt wouldn't let you do that.

    [quote author="Asperamanca" date="1331715180"]Commented properly, the for loop is a better safety net than nothing. In a proper program, I would add a logging entry in case I ever reach the end of the loop, identify the widget that doesn't behave, and fix it.[/quote]
    As mentioned above Qt allows for modifying the "modal stack" freely, there is nothing that would require a safety net.

    I would not recommend adding a piece of code that will break sooner or later at least once and requires additional work to be fixed just to break again a bit later - especially if there is a hassle-free solution.

    [quote author="Asperamanca" date="1331715180"]I hoped to find an easier solution with Qt.[/quote]
    It actually is. ;-)



  • I have a single GUI thread. In a method of mine, I open a modal dialog, say

    @
    void openMessageBox()
    {
    qDebug() << "Enter openMessageBox()";
    QMessageBox::information(this, "Text", "More text");
    qDebug() << "Exit openMessageBox()";
    }@

    The instruction pointer does not leave the method until the message box is closed. Nevertheless, a slot in another class is called by e.g. a timer. How can this work in a single thread? Because the modal window is running the event loop. But that means that the instruction pointer never leaves openMessageBox().
    It is still part of my call stack.

    Assume, I open a modal dialog first. And open a message box from within that modal dialog. Then my call stack goes from the method that called the myDialog.exec(), through the event loop, to the method in the dialog that opens the message box, through the event loop again, and maybe into some slot somewhere that handles a timer event.

    I tried to check with a simple code sample, but Creator/GDB foiled my attempts (would not show the whole call stack, and display tons of internal errors).



  • It's irrelevant if you call slot close()

    • via a connection from a button to the slot
    • call that slot from "outside" directly.
    • clicking on the (x) button on of the window bar an triggering a closeEvent


  • Yes, but the user cannot close any but the topmost modal window, because the UI is disabled for windows below. The code can bypass this constraint.



  • [quote author="Asperamanca" date="1331723526"]Yes, but the user cannot close any but the topmost modal window, because the UI is disabled for windows below. The code can bypass this constraint.[/quote]

    It was already worked out that this doesn't matter.



  • I stand corrected. It does work. Thanks for your patience.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.