Deliver Quit event when calling QCoreApplication::quit()
Instead of QCoreApplication::quit() directly calling exit(0), which would leave QGuiApplication and client code out of the loop, we now send the Quit event, and let it pass through event delivery, before finally ending up in QCoreApplication::event(), where we call exit(0). This has the advantage that QGuiApplication can ensure all windows are closed before quitting, and if any of those windows ignore the close event the quit will be aborted. This aligns the behavior of synthetic quits via QCoreApplication::quit() with spontaneous quits from the platform via QGuiApplicationPrivate::processApplicationTermination. Clients who wish to exit the application without any event delivery or potential user interaction can call the lower level exit() function directly. [ChangeLog][QtGui] Application termination via qApp->quit() will now deliver Quit events to the application, which in turn will result in application windows being closed as part of the application quit, with an option to cancel the application quit by ignoring the close event. Clients who explicitly want to exit the application without any user interaction should call QCoreApplication::exit() explicitly. Task-number: QTBUG-45262 Task-number: QTBUG-33235 Task-number: QTBUG-72013 Task-number: QTBUG-59782 Change-Id: Id4b3907e329b9ecfd936fe9a5f8a70cb66b76bb7 Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io> Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
parent
0ae5b8af9c
commit
89f7a2759c
@ -83,7 +83,7 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
fileMenu->addAction(tr("&Detect File Type..."), this, &MainWindow::detectFile);
|
fileMenu->addAction(tr("&Detect File Type..."), this, &MainWindow::detectFile);
|
||||||
detectFileAction->setShortcuts(QKeySequence::Open);
|
detectFileAction->setShortcuts(QKeySequence::Open);
|
||||||
|
|
||||||
QAction *exitAction = fileMenu->addAction(tr("E&xit"), qApp, &QApplication::closeAllWindows);
|
QAction *exitAction = fileMenu->addAction(tr("E&xit"), qApp, &QApplication::quit);
|
||||||
exitAction->setShortcuts(QKeySequence::Quit);
|
exitAction->setShortcuts(QKeySequence::Quit);
|
||||||
|
|
||||||
QMenu *findMenu = menuBar()->addMenu(tr("&Edit"));
|
QMenu *findMenu = menuBar()->addMenu(tr("&Edit"));
|
||||||
|
@ -369,7 +369,7 @@ void MainWindow::createActions()
|
|||||||
|
|
||||||
//! [0]
|
//! [0]
|
||||||
const QIcon exitIcon = QIcon::fromTheme("application-exit");
|
const QIcon exitIcon = QIcon::fromTheme("application-exit");
|
||||||
QAction *exitAct = fileMenu->addAction(exitIcon, tr("E&xit"), qApp, &QApplication::closeAllWindows);
|
QAction *exitAct = fileMenu->addAction(exitIcon, tr("E&xit"), qApp, &QApplication::quit);
|
||||||
exitAct->setShortcuts(QKeySequence::Quit);
|
exitAct->setShortcuts(QKeySequence::Quit);
|
||||||
exitAct->setStatusTip(tr("Exit the application"));
|
exitAct->setStatusTip(tr("Exit the application"));
|
||||||
fileMenu->addAction(exitAct);
|
fileMenu->addAction(exitAct);
|
||||||
|
@ -227,7 +227,7 @@ void MainWindow::createActions()
|
|||||||
closeAct->setStatusTip(tr("Close this window"));
|
closeAct->setStatusTip(tr("Close this window"));
|
||||||
|
|
||||||
const QIcon exitIcon = QIcon::fromTheme("application-exit");
|
const QIcon exitIcon = QIcon::fromTheme("application-exit");
|
||||||
QAction *exitAct = fileMenu->addAction(exitIcon, tr("E&xit"), qApp, &QApplication::closeAllWindows);
|
QAction *exitAct = fileMenu->addAction(exitIcon, tr("E&xit"), qApp, &QApplication::quit);
|
||||||
exitAct->setShortcuts(QKeySequence::Quit);
|
exitAct->setShortcuts(QKeySequence::Quit);
|
||||||
exitAct->setStatusTip(tr("Exit the application"));
|
exitAct->setStatusTip(tr("Exit the application"));
|
||||||
|
|
||||||
|
@ -1920,7 +1920,7 @@ void QCoreApplicationPrivate::removePostedEvent(QEvent * event)
|
|||||||
bool QCoreApplication::event(QEvent *e)
|
bool QCoreApplication::event(QEvent *e)
|
||||||
{
|
{
|
||||||
if (e->type() == QEvent::Quit) {
|
if (e->type() == QEvent::Quit) {
|
||||||
quit();
|
exit(0);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return QObject::event(e);
|
return QObject::event(e);
|
||||||
@ -1944,12 +1944,18 @@ void QCoreApplicationPrivate::maybeQuit()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Tells the application to exit with return code 0 (success).
|
Asks the application to quit.
|
||||||
Equivalent to calling QCoreApplication::exit(0).
|
|
||||||
|
|
||||||
It's common to connect the QGuiApplication::lastWindowClosed() signal
|
The request may be ignored if the application prevents the quit,
|
||||||
to quit(), and you also often connect e.g. QAbstractButton::clicked() or
|
for example if one of its windows can't be closed. The application
|
||||||
signals in QAction, QMenu, or QMenuBar to it.
|
can affect this by handling the QEvent::Quit event on the application
|
||||||
|
level, or QEvent::Close events for the individual windows.
|
||||||
|
|
||||||
|
If the quit is not interrupted the application will exit with return
|
||||||
|
code 0 (success).
|
||||||
|
|
||||||
|
To exit the application without a chance of being interrupted, call
|
||||||
|
exit() directly.
|
||||||
|
|
||||||
It's good practice to always connect signals to this slot using a
|
It's good practice to always connect signals to this slot using a
|
||||||
\l{Qt::}{QueuedConnection}. If a signal connected (non-queued) to this slot
|
\l{Qt::}{QueuedConnection}. If a signal connected (non-queued) to this slot
|
||||||
@ -1962,12 +1968,19 @@ void QCoreApplicationPrivate::maybeQuit()
|
|||||||
|
|
||||||
\snippet code/src_corelib_kernel_qcoreapplication.cpp 1
|
\snippet code/src_corelib_kernel_qcoreapplication.cpp 1
|
||||||
|
|
||||||
\sa exit(), aboutToQuit(), QGuiApplication::lastWindowClosed()
|
\sa exit(), aboutToQuit()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void QCoreApplication::quit()
|
void QCoreApplication::quit()
|
||||||
{
|
{
|
||||||
exit(0);
|
if (!self)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (QThread::currentThread() == self->d_func()->mainThread()) {
|
||||||
|
QEvent quitEvent(QEvent::Quit);
|
||||||
|
QCoreApplication::sendEvent(self, &quitEvent);
|
||||||
|
} else {
|
||||||
|
QCoreApplication::postEvent(self, new QEvent(QEvent::Quit));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -119,7 +119,7 @@ void MainWindow::setupMenus()
|
|||||||
QAction *exitAct = new QAction(tr("E&xit"), this);
|
QAction *exitAct = new QAction(tr("E&xit"), this);
|
||||||
exitAct->setShortcut(tr("Ctrl+Q"));
|
exitAct->setShortcut(tr("Ctrl+Q"));
|
||||||
exitAct->setStatusTip(tr("Exit the application"));
|
exitAct->setStatusTip(tr("Exit the application"));
|
||||||
connect(exitAct, &QAction::triggered, qApp, &QApplication::closeAllWindows);
|
connect(exitAct, &QAction::triggered, qApp, &QApplication::quit);
|
||||||
|
|
||||||
QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
|
QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
|
||||||
fileMenu->addAction(exitAct);
|
fileMenu->addAction(exitAct);
|
||||||
|
@ -1605,19 +1605,17 @@ retry:
|
|||||||
Closes all top-level windows.
|
Closes all top-level windows.
|
||||||
|
|
||||||
This function is particularly useful for applications with many top-level
|
This function is particularly useful for applications with many top-level
|
||||||
windows. It could, for example, be connected to a \uicontrol{Exit} entry in the
|
windows.
|
||||||
\uicontrol{File} menu:
|
|
||||||
|
|
||||||
\snippet mainwindows/mdi/mainwindow.cpp 0
|
|
||||||
|
|
||||||
The windows are closed in random order, until one window does not accept
|
The windows are closed in random order, until one window does not accept
|
||||||
the close event. The application quits when the last window was
|
the close event. The application quits when the last window was successfully
|
||||||
successfully closed; this can be turned off by setting
|
closed, unless \l quitOnLastWindowClosed is set to false. To trigger application
|
||||||
\l quitOnLastWindowClosed to false.
|
termination from e.g. a menu, use QCoreApplication::quit() instead of this
|
||||||
|
function.
|
||||||
|
|
||||||
\sa quitOnLastWindowClosed, lastWindowClosed(), QWidget::close(),
|
\sa quitOnLastWindowClosed, lastWindowClosed(), QWidget::close(),
|
||||||
QWidget::closeEvent(), lastWindowClosed(), QCoreApplication::quit(), topLevelWidgets(),
|
QWidget::closeEvent(), lastWindowClosed(), QCoreApplication::quit(),
|
||||||
QWidget::isWindow()
|
topLevelWidgets(), QWidget::isWindow()
|
||||||
*/
|
*/
|
||||||
void QApplication::closeAllWindows()
|
void QApplication::closeAllWindows()
|
||||||
{
|
{
|
||||||
|
@ -617,6 +617,72 @@ void tst_QCoreApplication::processEventsAlwaysSendsPostedEvents()
|
|||||||
} while (t.elapsed() < 1000);
|
} while (t.elapsed() < 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class QuitBlocker : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool eventFilter(QObject *, QEvent *event)
|
||||||
|
{
|
||||||
|
if (event->type() == QEvent::Quit) {
|
||||||
|
event->ignore();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void tst_QCoreApplication::quit()
|
||||||
|
{
|
||||||
|
TestApplication::quit(); // Should not do anything
|
||||||
|
|
||||||
|
{
|
||||||
|
int argc = 1;
|
||||||
|
char *argv[] = { const_cast<char*>(QTest::currentAppName()) };
|
||||||
|
TestApplication app(argc, argv);
|
||||||
|
|
||||||
|
EventSpy spy;
|
||||||
|
app.installEventFilter(&spy);
|
||||||
|
|
||||||
|
{
|
||||||
|
QTimer::singleShot(0, &app, SLOT(quit()));
|
||||||
|
app.exec();
|
||||||
|
QVERIFY(spy.recordedEvents.contains(QEvent::Quit));
|
||||||
|
}
|
||||||
|
|
||||||
|
spy.recordedEvents.clear();
|
||||||
|
|
||||||
|
{
|
||||||
|
QTimer::singleShot(0, qApp, SLOT(quit()));
|
||||||
|
app.exec();
|
||||||
|
QVERIFY(spy.recordedEvents.contains(QEvent::Quit));
|
||||||
|
}
|
||||||
|
|
||||||
|
spy.recordedEvents.clear();
|
||||||
|
|
||||||
|
{
|
||||||
|
QTimer::singleShot(0, [&]{ TestApplication::quit(); });
|
||||||
|
app.exec();
|
||||||
|
QVERIFY(spy.recordedEvents.contains(QEvent::Quit));
|
||||||
|
}
|
||||||
|
|
||||||
|
spy.recordedEvents.clear();
|
||||||
|
|
||||||
|
{
|
||||||
|
QuitBlocker quitBlocker;
|
||||||
|
app.installEventFilter(&quitBlocker);
|
||||||
|
|
||||||
|
QTimer::singleShot(0, [&]{ TestApplication::quit(); });
|
||||||
|
QTimer::singleShot(200, [&]{ TestApplication::exit(); });
|
||||||
|
app.exec();
|
||||||
|
QVERIFY(!spy.recordedEvents.contains(QEvent::Quit));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TestApplication::quit(); // Should not do anything
|
||||||
|
}
|
||||||
|
|
||||||
void tst_QCoreApplication::reexec()
|
void tst_QCoreApplication::reexec()
|
||||||
{
|
{
|
||||||
int argc = 1;
|
int argc = 1;
|
||||||
|
@ -49,6 +49,7 @@ private slots:
|
|||||||
void applicationPid();
|
void applicationPid();
|
||||||
void globalPostedEventsCount();
|
void globalPostedEventsCount();
|
||||||
void processEventsAlwaysSendsPostedEvents();
|
void processEventsAlwaysSendsPostedEvents();
|
||||||
|
void quit();
|
||||||
void reexec();
|
void reexec();
|
||||||
void execAfterExit();
|
void execAfterExit();
|
||||||
void eventLoopExecAfterExit();
|
void eventLoopExecAfterExit();
|
||||||
|
@ -675,7 +675,7 @@ void tst_QApplication::quitOnLastWindowClosed()
|
|||||||
bool quitApplicationTriggered = false;
|
bool quitApplicationTriggered = false;
|
||||||
auto quitSlot = [&quitApplicationTriggered] () {
|
auto quitSlot = [&quitApplicationTriggered] () {
|
||||||
quitApplicationTriggered = true;
|
quitApplicationTriggered = true;
|
||||||
QCoreApplication::quit();
|
QCoreApplication::exit();
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -462,7 +462,7 @@ ControllerWidget::ControllerWidget(QWidget *parent)
|
|||||||
QMenu *fileMenu = menuBar()->addMenu(tr("File"));
|
QMenu *fileMenu = menuBar()->addMenu(tr("File"));
|
||||||
QAction *exitAction = fileMenu->addAction(tr("Exit"));
|
QAction *exitAction = fileMenu->addAction(tr("Exit"));
|
||||||
exitAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q));
|
exitAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q));
|
||||||
connect(exitAction, SIGNAL(triggered()), qApp, SLOT(closeAllWindows()));
|
connect(exitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
|
||||||
|
|
||||||
QString title = QLatin1String("Child Window Geometry test, (Qt ");
|
QString title = QLatin1String("Child Window Geometry test, (Qt ");
|
||||||
title += QLatin1String(QT_VERSION_STR);
|
title += QLatin1String(QT_VERSION_STR);
|
||||||
|
@ -400,7 +400,7 @@ ControllerWidget::ControllerWidget(QWidget *parent)
|
|||||||
QMenu *fileMenu = menuBar()->addMenu(tr("File"));
|
QMenu *fileMenu = menuBar()->addMenu(tr("File"));
|
||||||
QAction *exitAction = fileMenu->addAction(tr("Exit"));
|
QAction *exitAction = fileMenu->addAction(tr("Exit"));
|
||||||
exitAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q));
|
exitAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q));
|
||||||
connect(exitAction, SIGNAL(triggered()), qApp, SLOT(closeAllWindows()));
|
connect(exitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
|
||||||
|
|
||||||
QString title = QLatin1String("Geometry test, (Qt ");
|
QString title = QLatin1String("Geometry test, (Qt ");
|
||||||
title += QLatin1String(QT_VERSION_STR);
|
title += QLatin1String(QT_VERSION_STR);
|
||||||
|
Loading…
Reference in New Issue
Block a user