Отличать событие закрытия от кнопки «X» и от выключения ОС в Windows с помощью Qt

Я поймал closeEvent от QMainWindow в своем приложении, чтобы показать «Вы уверены?» всплывающее окно для пользователя, если он закрывает приложение с помощью кнопки «Х».

К сожалению, всплывающее окно создает проблемы, если приложение закрывается из самой Windows во время выключения.

Я хотел бы различать поведение, если приложение было закрыто с помощью кнопки «X» или самой ОС.

Является ли это возможным?

Я пытался использовать сигнал aboutToQuit из Qt, но всплывающее окно появляется до того, как сигнал получен из моего приложения. Так что этот сигнал мне не помогает...

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
53
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете использовать QEvent::spontaneous(), чтобы отличить X нажатую кнопку (True) от закрытия ОС.

ПРИМЕЧАНИЕ. Закрытие процесса из диспетчера задач делает логическое значение истинным, но процесс все равно принудительно закрывается (очевидно, как и ожидалось)

Проверено на 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();
    }

}

Привет Базз. Спасибо за Ваш ответ. К сожалению, пока я его тестировал, я увидел, что мы также ловим closing(QQuickCloseEvent*) для QML. Это сигнал, который я получаю, но QQuickCloseEvent не является общедоступным, поэтому я не могу вызвать spotaneous(), см. https://bugreports.qt.io/browse/QTBUG-36453. У вас есть решение на случай QQuickCloseEvent? Спасибо за вашу помощь

n3mo 21.04.2023 12:00

Проверял вашу идею на событии от bool QQuickWindow::event(QEvent *e). Но, к сожалению, мне это не помогает. Если я нажму на X, я получу событие spontaneous. Но если я выключу операционную систему, я также получу событие spontaneous. Затем, поскольку всплывающее окно блокирует выключение системы, и я принудительно закрываю приложение, я получаю другое событие, и на этот раз это not spontaneous... Любая подсказка/подсказка/идея, как решить проблему? Спасибо

n3mo 21.04.2023 15:04
Ответ принят как подходящий

В Windows при запуске всем окнам отправляются два сообщения: WM_QUERYENDSESSION и WM_ENDSESSION, они трансформируются в QtWindows::QueryEndSessionApplicationEvent и QtWindows::EndSessionApplicationEvent. Эти события обрабатываются в QWindowsContext::windowsProc: первое вызывает QApplication::commitData в экземпляре приложения, которое выдает commitDataRequest() из экземпляра диспетчера сеансов, а второе выдает aboutToQuit() из экземпляра приложения.

Соответствующая часть Qt называется Session Management и включает в себя набор классов QSessionManager для конкретной платформы. Вот что рекомендует документация:

Начните с подключения слота к сигналу QGuiApplication::commitDataRequest(), чтобы позволить вашему приложению принять участие в процессе корректного выхода из системы. Если вы ориентируетесь только на платформу Microsoft Windows, это все, что вы можете и должны предоставить. В идеале ваше приложение должно предоставлять диалоговое окно завершения работы.

Вот пример из документации, как вы можете это сделать:

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
    }
}

Спасибо за Ваш ответ. Я надеялся, что есть что-то более простое, этот менеджер сеансов кажется более сложным в приложении, работающем на многих ОС.

n3mo 21.04.2023 13:26

Другие вопросы по теме