Unsolved 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/5These topics suggest:
- setting the application as a dialog's parent (but I need it to be the parent dialog, which is also modal)
- moving the dialog off-screen (kinda hackish - could cause some problems in multi-screen setup)
- 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); } } }
-
Simply don't call exec() but show().
-
Hi
Besides using show() as Mr Ehrlicher suggests you can also use open
https://doc.qt.io/qt-5/qdialog.html#open
for dialog2That seems to work fine, keeping both modal. (at least on win 10)
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()?
-
@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.