How to get properly manage QMessageBox’s close signal
-
Hi everyone,
As I wrote in the title, I cannot manage the call of the close button ("X") of a QMessageBox.
To give you a better understanding of what I mean, here is a picture of the window and the code I have written:void MainWindow::closeEvent (QCloseEvent *event) { QMessageBox* messaggio; if(controller->getModificato()){ messaggio = new QMessageBox(QMessageBox::Question, tr("QtDrive"), tr("Vuoi salvare le modifiche prima di uscire dall'applicazione"), QMessageBox::Yes | QMessageBox::No, this); messaggio->setButtonText(QMessageBox::Yes, tr("Sì")); messaggio->setButtonText(QMessageBox::No, tr("No")); messaggio->setWindowFlags(messaggio->windowFlags() & ~Qt::WindowCloseButtonHint); int ret = messaggio->exec(); if(ret == QMessageBox::Yes){ salvaIlFile(); //Se ho salvato effettivamente il file -> modificato sarà false. //Altrimenti modificato continua ad avere valore true //Se chiudessi perderei tutta l'informazione if(controller->getModificato()) event->ignore(); else event->accept(); }else if(ret == QMessageBox::No){ event->accept(); }else event->ignore(); }else{ ... } }
As the name of the function suggests, what is written in this code should only be executed when the programme is closed.
Only if there has been a change in the data (during the life of the program) the QMessageBox is presented to the user to ask if he wants to save these changes, otherwise the program is closed.
In practice, what I want to understand is how to manage the button "X" (at the top right of the photo), because when I click it, the check stops at the second one (
else if(ret == QMessageBox::No)
), whereas it should simply make theevent->ignore()
call inelse
.
I would also like to point out that I tried to remove the close button from the QMessageBox with the following codes, but the results were more than in vain:messaggio->setWindowFlags((messaggio->windowFlags() | Qt::CustomizeWindowHint) & ~Qt::WindowCloseButtonHint);
messaggio->setWindowFlags(messaggio->windowFlags() & ~Qt::WindowCloseButtonHint);
messaggio->setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint);
as suggested here
But nothing has changed
(Maybe) useful information:
Qt version: 5.15.2
Qt Creator version: 4.15.2
Architecture: x86_64 -
Hello!
First of all, you can remove the
QMessageBox
close button by using this code:setWindowFlags(Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint);
Screenshot 1:
Secondly, you can handle the
QMessageBox
close event withoutQMessageBox::Cancel
button by adding some bool class member variable to keep tracking the data change.
In close event, you can check if your data has been changed or not. For example: I have createdbool isDataChanged
in dialog.h file. In constructor (dialog.cpp) file I initialized the boolean variable toisDataChanged = false;
. When pressing onUpdate data
buttonisDataChanged
sets to true. Finally, you can check ifisDataChanged
to display theQMessageBox
in thecloseEvent
method.void Dialog::closeEvent(QCloseEvent *event) { qDebug() << "closeEvent"; if (isDataChanged) { QMessageBox *messaggio = new QMessageBox( QMessageBox::Question, tr("QtDrive"), tr("Vuoi salvare le modifiche prima di uscire dall'applicazione"), QMessageBox::Yes | QMessageBox::No, this); messaggio->setButtonText(QMessageBox::Yes, tr("Sì")); messaggio->setButtonText(QMessageBox::No, tr("No")); messaggio->setWindowFlags(Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint); int ret = messaggio->exec(); if (ret == QMessageBox::Yes) { event->accept(); } else { event->ignore(); } messaggio->deleteLater(); } }
Screenshot 2:
bool isDataChanged
is just a variable for a test case, in your case you can use thecontroller->getModificato()
method instead. Happy coding! -
@LucaPolese Here's what I've tested and it works (Qt 5.14.2):
void Widget::closeEvent(QCloseEvent *event) { if(! controller->getModificato()) return; QMessageBox messaggio( QMessageBox::Question, tr("QtDrive"), tr("Vuoi salvare le modifiche prima di uscire dall'applicazione"), QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel, this); messaggio.setButtonText(QMessageBox::Yes, tr("Sì")); messaggio.setButtonText(QMessageBox::No, tr("No")); messaggio.setWindowFlags(messaggio.windowFlags() & ~Qt::WindowCloseButtonHint); int ret = messaggio.exec(); if(ret == QMessageBox::Yes) { // Save settings event->accept(); } else if(ret == QMessageBox::No) { // Close app event->accept(); } else { // Do nothing (Cancel or Close) event->ignore(); } }
-
@SamurayH It works fine for me too!
I believe that at this point I will adopt this version too.The only thing that bothers me is that I can't handle the closing event of the QMessageBox ("X"), without adding the
QMessageBox::Cancel
button.
Would you happen to know if there is an additional solution for this? -
Hello!
First of all, you can remove the
QMessageBox
close button by using this code:setWindowFlags(Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint);
Screenshot 1:
Secondly, you can handle the
QMessageBox
close event withoutQMessageBox::Cancel
button by adding some bool class member variable to keep tracking the data change.
In close event, you can check if your data has been changed or not. For example: I have createdbool isDataChanged
in dialog.h file. In constructor (dialog.cpp) file I initialized the boolean variable toisDataChanged = false;
. When pressing onUpdate data
buttonisDataChanged
sets to true. Finally, you can check ifisDataChanged
to display theQMessageBox
in thecloseEvent
method.void Dialog::closeEvent(QCloseEvent *event) { qDebug() << "closeEvent"; if (isDataChanged) { QMessageBox *messaggio = new QMessageBox( QMessageBox::Question, tr("QtDrive"), tr("Vuoi salvare le modifiche prima di uscire dall'applicazione"), QMessageBox::Yes | QMessageBox::No, this); messaggio->setButtonText(QMessageBox::Yes, tr("Sì")); messaggio->setButtonText(QMessageBox::No, tr("No")); messaggio->setWindowFlags(Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint); int ret = messaggio->exec(); if (ret == QMessageBox::Yes) { event->accept(); } else { event->ignore(); } messaggio->deleteLater(); } }
Screenshot 2:
bool isDataChanged
is just a variable for a test case, in your case you can use thecontroller->getModificato()
method instead. Happy coding! -
@Cobra91151 one small thing to your suggestion: there's no need to allocated your message box on the heap. Just make it a local instance and it will be automatically destroyed at the end of the method.
-
@Cobra91151 That's exactly what I was looking for
Thanks a lot! -
Hello!
I know that. I just copied @LucaPolese code and modify it a bit. Besides, it calls
messaggio->deleteLater();
at the end of the method, so no memory leak occurs. I forgot to mention it in my post above. For a personal use, I have createdMsgDialog
class which inherits fromQMessageBox
class and call it as a local instance. UsingMsgDialog
class I can make it stay always on top, add custom icon etc. The example below displays a simple usage of my class.Code:
MsgDialog testDlg(this, "Title", "This is a test?", QMessageBox::Question, QMessageBox::Yes | QMessageBox::No); int userRes = testDlg.exec(); if (userRes == QMessageBox::Yes) { //confirm } else { //decline }
-
@Cobra91151 No worries, I saw that you deleted the dialog at the end of the method. Just pointing out that it's an easy to forget step and that in this case it was not necessary to use the heap for that. It's also one small thing that allows for reduced heap fragmentation.