QApplication::quit() does not trigger closeEvent() of the main window: by design or bug?
-
Is it by design that exiting the application with
QApplication::quit()
does not triggercloseEvent()
of the main window? I was under the impression thatqApp->quit()
is the right way to exit the app, and the right slot to connect the "File -> Exit" menu action to. But it bypassesQMainWindows::closeEvent()
, which often triggers, directly or indirectly, some cleanup and finalization. Bypassing it causes bugs in applications.I think this is a Qt bug, but I want to make sure before I post a bug report.
-
@Violet-Giraffe
quit basically calls exit(0).therefore every destructor should be called before the final exit.
2 Questions
- did you connect to quit with a Qt::QueuedConnection ? This one is the prefered to auto/direkt
- is your QMainWindow on the heap? int hat case yes, you'll have to manualy call delete/delete later for it to be properly deleted.
-
@J-Hilk :
- I did not use
Qt::QueuedConnection
, would that somehow change the behavior of the program? Let me try. - No, my main window instance is on the stack inside the
main()
function. Here's the complete listing of my sample program demonstrating the issue:
#include <QtWidgets> struct MainWindow : public QMainWindow { MainWindow() { auto menuBar = new QMenuBar(this); auto exitAction = menuBar->addAction("Exit"); exitAction->setMenuRole(QAction::QuitRole); connect(exitAction, &QAction::triggered, qApp, &QApplication::quit); setMenuBar(menuBar); } protected: void closeEvent(QCloseEvent* e) override { QMessageBox::information(this, "", "Close event received."); QMainWindow::closeEvent(e); } }; int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow mw; mw.resize(400, 300); mw.show(); return a.exec(); }
- I did not use
-
Qt::QueuedConnection
I think have a read of https://forum.qt.io/topic/9917/order-of-signals-on-application-quitting/2 etc.
-
P. S. Tried
Qt::QueuedConnection
, no change: still nocloseEvent()
when pressing the "Exit". -
@Violet-Giraffe
first of, that is not the correct way to derive from a QObject based class !! You'll need to fix that,2nd
I can reproduce the issue, mhh.this line however
connect(exitAction, &QAction::triggered, qApp, &QApplication::closeAllWindows, Qt::QueuedConnection);
should work as long as the flag "exit when all windows closed" is set.
-
@J.Hilk said in QApplication::quit() does not trigger closeEvent() of the main window: by design or bug?:
@Violet-Giraffe
first of, that is not the correct way to derive from a QObject based class !! You'll need to fix that,How do you mean?
-
at the very least:
#include <QtWidgets> class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) { auto menuBar = new QMenuBar(this); auto exitAction = menuBar->addAction("Exit"); exitAction->setMenuRole(QAction::QuitRole); connect(exitAction, &QAction::triggered, qApp, &QApplication::closeAllWindows, Qt::QueuedConnection); setMenuBar(menuBar); } protected: void closeEvent(QCloseEvent* e) override { QMessageBox::information(this, "", "Close event received."); QMainWindow::closeEvent(e); } }; int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow mw; mw.resize(400, 300); mw.show(); return a.exec(); }
-
@J-Hilk : I don't believe the
Q_OBJECT
macro is strictly always required. It's only required when the class needs to use the Qt meta compiler (e. g. toemit
a signal), but my class does not need that. On the other hand, adding unnecessaryQ_OBJECT
has significant drawbacks in the resulting binary size, memory footprint, compilation time, and, possibly, startup time. -
@Violet-Giraffe
that's not how I learned it :-) -
@J.Hilk, Thanks for the excerpt. I absolutely agree it's required for signals, as well as for old-style slots (with the
slots:
macro), but with the new C++11 connection syntax it's no longer required for the connection target to haveQ_OBJECT
.
Let me try adding this macro and seeing if the program's behavior changes. -
@Violet-Giraffe said in QApplication::quit() does not trigger closeEvent() of the main window: by design or bug?:
but with the new C++11 connection syntax it's no longer required for the connection target to have
Q_OBJECT
.It's sadly a bit more complicated than that.
You don't need to to specify slots any longer, true, but the target class/object needs Qobjects somewhere as its base class, or it won't work.
You can trick the system somewhat, by using a Lambda, but that falls also flat if you to to do the connect via the static call of Qobject::connect, inside the class that is not based on QObject.
-
@J.Hilk, you're right, the connection target also has to be a
QObject
, but it does not need to have theQ_OBJECT
macro. In practice, this means most classes don't need the macro, especially if you take a little effort to avoid needing it. The compilation speedup from omitting the macro is not insignificant.Now, back to the issue at hand: I've added
Q_OBJECT
to my main window subclass, I seta.setQuitOnLastWindowClosed(true);
, and I useQt::QueuedConnection
on the qApp->quit() slot. Guess what? Still no close event. -
@Violet-Giraffe said in QApplication::quit() does not trigger closeEvent() of the main window: by design or bug?:
Is it by design that exiting the application with
QApplication::quit()
does not triggercloseEvent()
of the main window?I'm not sure. The best way to find out is to ask the Qt engineers at the Interest mailing list (you must subscribe before posting)
I was under the impression that
qApp->quit()
is the right way to exit the app, and the right slot to connect the "File -> Exit" menu action to.Agreed.
But it bypasses
QMainWindows::closeEvent()
, which often triggers, directly or indirectly, some cleanup and finalization. Bypassing it causes bugs in applications.I think this is a Qt bug, but I want to make sure before I post a bug report.
I consider quitting an app and closing a window as 2 different, independent actions. I don't think it's a bug, since the user did not explicitly close a window.
Shouldn't should cleanup/finalization be triggered from the window class' destructor instead of a close event?
-
@JKSH said in QApplication::quit() does not trigger closeEvent() of the main window: by design or bug?:
Shouldn't should cleanup/finalization be triggered from the window class' destructor instead of a close event?
A common use of
QMainWindow::closeEvent()
(I think suggested in docs somewhere) is to ask user if certain settings should be saved for next time, etc. I think that should be happening incloseEvent()
, while window is still around and things are relatively safe, not in some final, low-level destructor. -
ok 2
fixes
connect(exitAction, &QAction::triggered, qApp, &QApplication::closeAllWindows);
or
//main.cpp QObject::connect(qApp, &QApplication::aboutToQuit, &mw, &MainWindow::close);
-
@Violet-Giraffe said in QApplication::quit() does not trigger closeEvent() of the main window: by design or bug?:
On the other hand, adding unnecessary Q_OBJECT has significant drawbacks in the resulting binary size, memory footprint, compilation time, and, possibly, startup time.
You should put your money where your mouth is.
@JKSH said in QApplication::quit() does not trigger closeEvent() of the main window: by design or bug?:
I consider quitting an app and closing a window as 2 different, independent actions. I don't think it's a bug, since the user did not explicitly close a window.
I agree.
@JonB said in QApplication::quit() does not trigger closeEvent() of the main window: by design or bug?:
A common use of QMainWindow::closeEvent() (I think suggested in docs somewhere) is to ask user if certain settings should be saved for next time, etc. I think that should be happening in closeEvent(), while window is still around and things are relatively safe, not in some final, low-level destructor.
And if you need a quick exit, then what do you do if you handle things in the close event? Intercept it with an event filter? And if you have 3-4 top level windows that do things in their close events? It kind of gets spaghetti.
@Violet-Giraffe said in QApplication::quit() does not trigger closeEvent() of the main window: by design or bug?:
Is it by design that exiting the application with QApplication::quit() does not trigger closeEvent() of the main window?
I believe so, yes. You either close the window, or you don't, you chose not to. My advice - connect the button to the window's close slot.
-
@kshegunov said in QApplication::quit() does not trigger closeEvent() of the main window: by design or bug?:
And if you need a quick exit, then what do you do if you handle things in the close event? Intercept it with an event filter? And if you have 3-4 top level windows that do things in their close events? It kind of gets spaghetti.
I don't understand. Where do you save things if not in the
closeEvent()
? Where else do you get to reject a requested close? BTW, the docs proposed pattern example I was thinking of is at http://doc.qt.io/qt-5/qtwidgets-mainwindows-application-example.html#close-event-handler -
@JonB said in QApplication::quit() does not trigger closeEvent() of the main window: by design or bug?:
I don't understand. Where do you save things if not in the closeEvent()?
I didn't say don't save things in
closeEvent
, I wrote that closing a window and saving its state (or w/e) isn't part of the program exit semantically - you can have 2 or more top level windows, and you can also want to quit quickly without saving whatever it is - just exit. So if you block incloseEvent
and Qt pulls the rug under you by callingclose
on each window before exiting the event loop, how would you exit quickly? -
@kshegunov
I'm still not quite with you, and you know I do value your comments!- Does that mean you do not agree with/like the example at http://doc.qt.io/qt-5/qtwidgets-mainwindows-application-example.html#close-event-handler
When the user attempts to close the window, we call the private function maybeSave() to give the user the possibility to save pending changes.
?
-
I am unsure whether you are saying
closeEvent
should not save state/block/allow user to cancel, or whether you are sayingQApplication::quit()
should not go via/raise that (if it does) so that it can be a fast exit (with which I do not disagree)? -
If you answer is that
closeEvent
cannot block etc. because it could prevent fast-quit, how do you propose to deal with user clicking main windowX
button instead of going File -> Exit ? You can choose what the latter does, but (so far as I know) you cannot help that the former just callscloseEvent
.