Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Hide/Show modal QDialog



  • Hi, I need to be able to show/hide modal QDialog when a specific event occurs. I know, that calling QDialog::setVisible(false) on the modal dialog (after calling QDialog::exec()) calls the destructor.

    https://forum.qt.io/topic/104160/temporarily-hide-a-dialog-without-causing-it-s-dismiss-action-to-fire
    https://forum.qt.io/topic/58205/solved-modal-qdialog-setvisibible-false-calls-the-destructor/5

    These topics suggest:

    1. setting the application as a dialog's parent (but I need it to be the parent dialog, which is also modal)
    2. moving the dialog off-screen (kinda hackish - could cause some problems in multi-screen setup)
    3. setting its size to (0,0) (this seems like most friendly, but when I do this, after setting its size back to the original size, the modal dialog behaves like modeless)

    Here's my minimal reproducible sample - just a simple Qt desktop application created from a template in Qt Creator (using Qt 5.12.9 on macOS 10.15.5):

    Dialog::Dialog(QWidget *parent)
        : QDialog(parent)
        , ui(new Ui::Dialog)
    {
        ui->setupUi(this);
    
        QPushButton* bn_create = findChild<QPushButton*>("pushButton");
        connect(bn_create, &QPushButton::clicked, this, &Dialog::CreateNew);
    
        QPushButton* bn_showhide = findChild<QPushButton*>("pushButton_2");
        connect(bn_showhide, &QPushButton::clicked, this, &Dialog::ShowHideParent);
    }
    
    Dialog::~Dialog()
    {
        delete ui;
    }
    
    // creates a new modal dialog with the current dialog as its parent
    void Dialog::CreateNew()
    {
        Dialog new_dialog(this);
        new_dialog.exec();
    }
    
    // hides its parent if it's shown, shows its parent if its hidden
    void Dialog::ShowHideParent()
    {
        if (parentWidget())
        {
            static QSize original_size = parentWidget()->size();
            if (parentWidget()->size().isNull())
            {
                // after this, it behaves like modeless ???
                // allows me to click its buttons even thought its modal child is still active
                parentWidget()->resize(original_size);
            }
            else
            {
                parentWidget()->resize(0, 0);
            }
        }
    }
    

  • Qt Champions 2019

    Simply don't call exec() but show().


  • Lifetime Qt Champion

    Hi
    Besides using show() as Mr Ehrlicher suggests you can also use open
    https://doc.qt.io/qt-5/qdialog.html#open
    for dialog2

    That seems to work fine, keeping both modal. (at least on win 10)
    alt text

    test project
    https://www.dropbox.com/s/hm5cptaqu305s7w/ModalModalDialog.zip?dl=0



  • Thank you both for your answers! The only problem I have with this is that both solutions return immediately - I need the call to be blocking. I know, that the dialog's return value can be obtained via accept() or reject() signals, but in the app that I'm building I would prefer the blocking call. Can I achieve something like this with the show()/open()?


  • Lifetime Qt Champion

    @brkonator
    Hi
    Im afraid not. But you can move the code you currently have after exec()
    to its own slot and hook up the finished() signal to this slot to have mostly same
    feature as blocking.
    It will not fire when hide/showing the dialog so should work as you want and
    first run the code on ok/cancel/x.

    You could use a lambda to keep code more local. like

    void DialogOne::on_pushButton_clicked()
    {
        if (!d2) {
            d2 = new DialogTwo(this);
            connect(d2, &QDialog::finished, this, [](int result) {
                qDebug() << result;
            });
        }
    
        d2->open();
    }
    


  • @brkonator
    I don't get it --- if you want a blocking call to a modal dialog, why would you then want to hide it? You haven't explained what behaviour you want when you do decide to hide it?



  • @JonB I'm working on the library that "wraps" multiple GUI frameworks - one of them is Qt. The idea behind this is that the user writes one code base using the library and it's up to the user, which of the underlying frameworks is used.

    Multiple working dialogs are already implemented using this library, but the current requirement is to display the modal dialog (D1) and if the user clicks a certain button, another modal dialog (D2) with the progress bar (updated by the background process) is displayed while the D1 gets hidden (and is shown only when the background process finishes and closes the D2). I know this may be a strange behavior but it's not up to me and I was able to solve this using other frameworks.

    So the sample code would look like this:

    // user's code using the library
    int main() 
    {
        WrapperLib::Dialog d;
        int return_value = d.ShowModal();
        // do something based on the return_value
    }
    
    // this is the code inside the library
    namespace WrapperLib
    {
        class Dialog
        {
        private:
    #ifdef _QT_FRAMEWORK
            QDialog* dialog_impl_;
    #elif _OTHER_FRAMEWORK
            OtherFrameworkDialogClass* dialog_impl_;
    #endif
    
        public:
            int ShowModal()
            {
    #ifdef _QT_FRAMEWORK
                return dialog_impl_->exec();
    #elif _OTHER_FRAMEWORK
                return dialog_impl_->OtherFrameworkModalDialogBlockingMethod();
    #endif
            }
        };
    }
    


  • Maybe you can try to fake window modality? I am not entirely sure if this works. But, could one install an event filter on the QApplication object? This event filter could then ignore all events except for the currently active dialog (and most likely you still need to let paint events go through to other widgets).

    BTW, I have read that you should be careful when nesting event loops. exec() will start its own event loop. Showing two modal dialogs then also nests two event loops. This might get you into trouble with Qt.


Log in to reply