0

I have caught the closeEvent from QMainWindow in my application, in order to show a "Are you sure?" popup to the user, if he closes the app with the "X" button.

Unfortunately the popup creates problems if the application is closed from Windows itself during the shutdown.

I would like to differentiate the behaviour if the app was closed using the "X" button or by the OS itself.

Is it possible?

I tried to use the aboutToQuit signal from Qt, but the popup appears before the signal is received from my app. So this signal does not help me...

n3mo
  • 663
  • 8
  • 23

2 Answers2

1

You can use QEvent::spontaneous() to discern between X button clicked (True) or OS shutting down.

NOTE: Closing the process from Task Manager makes the boolean value true but the process is forced to be closed anyway (as expected, obviously)

Tested on Windows 11:


void MainWindow::closeEvent(QCloseEvent *event)
{
    qDebug() << "Spontaneous: " << event->spontaneous();
    if (event->spontaneous())
    {
        // X button clicked
        auto res = QMessageBox::question(this, "Sure?", "Sure you want to exit?", QMessageBox::Yes | QMessageBox::No);
        if (res == QMessageBox::Yes)
            event->accept();
        else
            event->ignore();
    }
    else {
        event->accept();
    }

}
Buzz
  • 1,102
  • 1
  • 9
  • 24
  • Hi Buzz. Thanks for your answer. Unfortunately, while I was testing it, I have seen that we catch also the `closing(QQuickCloseEvent*)` for QML. This is the signal that I receive, but the `QQuickCloseEvent` is not public, so I can not call `spotaneous()`, see `https://bugreports.qt.io/browse/QTBUG-36453`. Do you have a solution in case of `QQuickCloseEvent`? Thanks for your help – n3mo Apr 21 '23 at 10:00
  • I was testing your idea using the event from `bool QQuickWindow::event(QEvent *e)`. But unfortunately, it does not help me. If I click on `X`, I get a `spontaneous` event. But if I shut down the operative system, I also get a `spontaneous` event. Then, since the popup is blocking the shut down of the system and I force the application to be closed, I get another event, and this time it is `not spontaneous`...Any clue/hint/idea how to solve the problem? Thanks – n3mo Apr 21 '23 at 13:04
1

In windows two messages are send to all windows when shutdown initiated: WM_QUERYENDSESSION and WM_ENDSESSION, they are transformed into QtWindows::QueryEndSessionApplicationEvent and QtWindows::EndSessionApplicationEvent. This events are handled in QWindowsContext::windowsProc: first one calls QApplication::commitData on application instance, which emits commitDataRequest() from session manager instance, and second one emits aboutToQuit() from application instance.

Relevant part of Qt called Session Management and includes a set of platform specific QSessionManager classes. Here's what documentation recommends:

Start by connecting a slot to the QGuiApplication::commitDataRequest() signal to enable your application to take part in the graceful logout process. If you are only targeting the Microsoft Windows platform, this is all you can and must provide. Ideally, your application should provide a shutdown dialog.

Here's example from documentation how you can do this:

MyMainWidget::MyMainWidget(QWidget *parent)
    :QWidget(parent)
{
    QGuiApplication::setFallbackSessionManagementEnabled(false);
    connect(qApp, &QGuiApplication::commitDataRequest,
            this, &MyMainWidget::commitData);
}

void MyMainWidget::commitData(QSessionManager& manager)
{
    if (manager.allowsInteraction()) {
        int ret = QMessageBox::warning(
                    mainWindow,
                    tr("My Application"),
                    tr("Save changes to document?"),
                    QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);

        switch (ret) {
        case QMessageBox::Save:
            manager.release();
            if (!saveDocument())
                manager.cancel();
            break;
        case QMessageBox::Discard:
            break;
        case QMessageBox::Cancel:
        default:
            manager.cancel();
        }
    } else {
        // we did not get permission to interact, then
        // do something reasonable instead
    }
}
mugiseyebrows
  • 4,138
  • 1
  • 14
  • 15
  • Thanks for your answer. I hoped there was something simpler, this session manager seems to have more complexity in a app running on many OS. – n3mo Apr 21 '23 at 11:26