Close modal dialog by code: How to wait for the cleanup?
-
I have an application that may show message boxes. If a user remains inactive for too long, I need to cancel all message boxes, and change into a kind of "screensaver" mode.
The problem is that in order to enter this "screensaver mode", I have to clean up all the code that led to the message box being displayed. So the message box has to be completely gone, and the call stack that waited for the exec() to return has to be unwound before I may continue, otherwise my application crashes.
I have tried the following to close message boxes:
for(int i=0; i < 10000; ++i) // loop catcher { auto* const pModalWidget = QApplication::activeModalWidget(); if ( ! pModalWidget ) { return; } pModalWidget->close(); QApplication::processEvents(); }
The close does the right thing in principle, it rejects (cancels). However the processEvents after it is not sufficient to allow the QMessageBox::exec() to return, and the code that created the message box to unwind it's stack and clean up.
I have read about using QDialog::open() instead of exec(), but this would be a massive redesign possibly affecting the behavior of lots of message boxes, and I would prefer not to touch the part, and instead figure out how to wait for exec() to return.
Any ideas?
EDIT: I have since found out that processEvents() never allows exec() to return, no matter how often I call it. So I need something else..?
-
I have an application that may show message boxes. If a user remains inactive for too long, I need to cancel all message boxes, and change into a kind of "screensaver" mode.
The problem is that in order to enter this "screensaver mode", I have to clean up all the code that led to the message box being displayed. So the message box has to be completely gone, and the call stack that waited for the exec() to return has to be unwound before I may continue, otherwise my application crashes.
I have tried the following to close message boxes:
for(int i=0; i < 10000; ++i) // loop catcher { auto* const pModalWidget = QApplication::activeModalWidget(); if ( ! pModalWidget ) { return; } pModalWidget->close(); QApplication::processEvents(); }
The close does the right thing in principle, it rejects (cancels). However the processEvents after it is not sufficient to allow the QMessageBox::exec() to return, and the code that created the message box to unwind it's stack and clean up.
I have read about using QDialog::open() instead of exec(), but this would be a massive redesign possibly affecting the behavior of lots of message boxes, and I would prefer not to touch the part, and instead figure out how to wait for exec() to return.
Any ideas?
EDIT: I have since found out that processEvents() never allows exec() to return, no matter how often I call it. So I need something else..?
What if you make your
QMessageBoxes
clean up themselves?
Keep the first part of your code to get all active windows and then call their reject / end function to clean them up.You could also try
pModalDialog->done()
instead ofclose()
,
as- If this dialog is shown with exec(), done() also causes the local event loop to finish, and exec() to return r.
(https://doc.qt.io/qt-5/qdialog.html#done)
With
done(int r)
you can also pass an return code, that you can use later. So you know, that the message box was terminated because of timeout / user inactivity.(Edit: Ok, this isn't as simple as I thought, but you still can use
0
or1
forQDialog::Rejected
andQDialog::Accepted
.
Custom return codes may work, but you need to override / re-implement some stuff) -
I have an application that may show message boxes. If a user remains inactive for too long, I need to cancel all message boxes, and change into a kind of "screensaver" mode.
The problem is that in order to enter this "screensaver mode", I have to clean up all the code that led to the message box being displayed. So the message box has to be completely gone, and the call stack that waited for the exec() to return has to be unwound before I may continue, otherwise my application crashes.
I have tried the following to close message boxes:
for(int i=0; i < 10000; ++i) // loop catcher { auto* const pModalWidget = QApplication::activeModalWidget(); if ( ! pModalWidget ) { return; } pModalWidget->close(); QApplication::processEvents(); }
The close does the right thing in principle, it rejects (cancels). However the processEvents after it is not sufficient to allow the QMessageBox::exec() to return, and the code that created the message box to unwind it's stack and clean up.
I have read about using QDialog::open() instead of exec(), but this would be a massive redesign possibly affecting the behavior of lots of message boxes, and I would prefer not to touch the part, and instead figure out how to wait for exec() to return.
Any ideas?
EDIT: I have since found out that processEvents() never allows exec() to return, no matter how often I call it. So I need something else..?
@Asperamanca said in Close modal dialog by code: How to wait for the cleanup?:
I have read about using QDialog::open() instead of exec(), but this would be a massive redesign possibly affecting the behavior of lots of message boxes, and I would prefer not to touch the part, and instead figure out how to wait for exec() to return.
Unfortunately for you, this (
open()
) is indeed the right way to do it, so that you will not have the problems you are seeing. It may be very difficult to getexec()
to terminate nicely.The "proper" way to permit Qt stuff to clean up cleanly is to allow code to return to the top-level, main event loop.
processEvents()
does not do the same as that, and e.g. does not do any pending deletions.As @Pl45m4 says, if you do have to do this you would better having your own (sub-classed) message boxes be "timeout-aware" and handle situation themselves, than forcibly closing them from outside world, and having to make some return code that was never anticipated.
-
@Asperamanca said in Close modal dialog by code: How to wait for the cleanup?:
I have read about using QDialog::open() instead of exec(), but this would be a massive redesign possibly affecting the behavior of lots of message boxes, and I would prefer not to touch the part, and instead figure out how to wait for exec() to return.
Unfortunately for you, this (
open()
) is indeed the right way to do it, so that you will not have the problems you are seeing. It may be very difficult to getexec()
to terminate nicely.The "proper" way to permit Qt stuff to clean up cleanly is to allow code to return to the top-level, main event loop.
processEvents()
does not do the same as that, and e.g. does not do any pending deletions.As @Pl45m4 says, if you do have to do this you would better having your own (sub-classed) message boxes be "timeout-aware" and handle situation themselves, than forcibly closing them from outside world, and having to make some return code that was never anticipated.
@JonB said in Close modal dialog by code: How to wait for the cleanup?:
Unfortunately for you, this (open()) is indeed the right way to do it, so that you will not have the problems you are seeing. It may be very difficult to get exec() to terminate nicely.
IDK... maybe just
done()
instead ofclose()
will do it. Docs say, that it terminates / finishes the event loop, started withexec
. -
@JonB said in Close modal dialog by code: How to wait for the cleanup?:
Unfortunately for you, this (open()) is indeed the right way to do it, so that you will not have the problems you are seeing. It may be very difficult to get exec() to terminate nicely.
IDK... maybe just
done()
instead ofclose()
will do it. Docs say, that it terminates / finishes the event loop, started withexec
.The root problem seems to be that the call closing the messagebox by code is now running inside the event loop of the message box. It's clear it cannot properly unwind the stack in this scenario.
I have come to the conclusion that the open() strategy is the only proper way to go...