This is quite beginner post but I believe it’s good to share what we learn through experience with the people that are starting. In this entry I’ll show you how to have a UI non closable QDialog but that it can be close from the code.
In GitQlient I’m using this dialog to inform the user that the repository is being loaded. There is some data I’m loading in the cache/background (branches, tags, etc) that can take a bit if the repository is massive. In my case, I’m working with some repos that are quite big (~10 Gb only text files) and old. People usually forget to remove their branches and right know I can see around 2000 branches and close to 500 tags. In that case an async load is needed so the user has to wait.
How to do a non closable QDialog?
We want to prevent the user to close the dialog in all it’s forms:
- Disabling the window close icon.
- Disabling the Esc key that in QDialog closes/cancels the dialog.
Acting on the windows title
The way to do that is quite simple. In the first place we hide the close button:
setWindowFlags(windowFlags() & ~Qt::WindowCloseButtonHint);
This will make the close button gone and keep the resize behaviour and dialog title. If you want a dialog only for the text you can do:
setWindowFlags(Qt::FramelessWindowHint);
That will remove the title bar and all it’s components.
Method 1: Acting on the Esc key event
QDialog process the Esc key as close/cancel. Since we don’t want that the user closes our dialog, we need to take care of that behaviour. To do that we need to capture the key press event and avoid processing it:
void WaitingDlg::keyPressEvent(QKeyEvent *e)
{
if (e->key() == Qt::Key_Escape)
return;
QDialog::keyPressEvent(e);
}
The code is quite simple, we check the pressed key and if it’s the one we’re expecting, we don’t process the event.
Additionally, it’s also useful to have full control of another event: the closeEvent. That method plus a custom close method will give us the full behaviour:
void WaitingDlg::closeEvent(QCloseEvent *e)
{
if (!mPrepareToClose)
e->ignore();
else
QDialog::closeEvent(e);
}
void WaitingDlg::close()
{
mPrepareToClose = true;
QDialog::close();
}
In this code I’m using a class member to store when I do want to close the dialog, preventing the dialog to close if it wasn’t me how said it. The result is that this dialog can only be close by calling the close() method.
Method 2: Override reject method
Suggested in the comments by NickD2039 another possibility is to override the reject method, that would prevent the end user from closing the dialog. The code posted has a closeNow method that acts in a similar way to the method 1, it activates a control variable that finall calls accept.
I kind of like a bit more this code, since it’s a bit more elegant and requires you to write less code:
void closeNow()
{
mPrepareToClose = true;
accept();
mPrepareToClose = false;
}
void accept()
{
if (mPrepareToClose)
QDialog::accept();
}
void reject()
{
if (mPrepareToClose)
QDialog::reject();
}
Where to find the source coude?
You can find the source code for the non closable dialog in my GitHub repository for Qt tips. Please noticed that I name it WaitingDlg since I did the code for GitQlient and that was its purpose.
Comments
Or you could just override the accept and reject methods, like this:
class NonClosableDialog: public QDialog
{
bool _canClose{false};
public:
void closeNow()
{
_canClose = true;
accept();
_canClose = false; // for next time
}
void accept() override
{
if (_canClose)
QDialog::accept();
}
void reject() override
{
if (_canClose)
QDialog::reject();
}
};
Hi NickD2039,
Thanks for the comment!
That’s an option, but you would still have the change of pressing the “Esc” key and close the dialog. So you still need to override the closeEvent and the keyPressEvent.
Cheers,
Francesc.
Actually, no – pressing the escape key reaches reject() which has been overridden. (I did check this and there is no way to close the dialog with escape or window close button etc.). Give it a try…
True to that! Let’s update the entry! 🙂
Edit: One idea about the code in general: I think that you can override and keep the reject() empty, I don’t think there is any need to have code in there since you will never close it. Although you want to “reject” it by code.