QApplication: как корректно завершить работу с помощью Ctrl-C

#c #qt #unix

#c #qt #unix

Вопрос:

У меня есть QApplication , которое, в зависимости от параметров командной строки, иногда на самом деле не имеет окна GUI, а просто запускается без GUI. В этом случае я хочу корректно завершить работу, если был нажат CTRLC. В основном мой код выглядит следующим образом:

 int main(int argc, char* argv[])
{
    QApplication app(argc, argv);

    ... // parse command line options

    if (no_gui) {
        QObject::connect(amp;app, SIGNAL(unixSignal(int)),
                         amp;app, SLOT(quit()));
        app.watchUnixSignal(SIGINT, true);
        app.watchUnixSignal(SIGTERM, true);
    }

    ... 

    return app.exec();
}
  

Однако это не работает. CTRLC кажется, что это перехвачено (приложение не завершается), но оно также не завершается. Чего мне не хватает?

Комментарии:

1. Это очень хорошо задокументировано. Воспользуйтесь документацией.

Ответ №1:

Возможно, есть способ сделать это изначально с помощью Qt — я немного покопался в документах QKeySequence, прежде чем сдаться, но вы можете просто использовать signal . На данный момент на моем компьютере нет настроек Qt / C , но у меня есть привязки к Python.

 import sys, signal
from PyQt4 import QtGui

app = QtGui.QApplication(sys.argv)
signal.signal(signal.SIGINT, signal.SIG_DFL)

sys.exit(app.exec_())
  

Это работает и закроет приложение, когда я выполню CtrlC. Итак, я считаю, что ваше приложение могло бы адаптировать этот код, и в итоге получилось бы что-то вроде этого:

 #include <signal.h>

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);

    ... // parse command line options

    if (no_gui) {
        signal(SIGINT, SIG_DFL);
    }

    ... 

    return app.exec();
}
  

К сожалению, я не могу скомпилировать это, поэтому, вероятно, потребуется несколько исправлений, но это должно дать вам общее представление. Используя SIG_DFL обработчик, вы даете вашей программе указание использовать действие по умолчанию, связанное с CtrlC.

Ответ №2:

Поскольку это не задокументировано, QApplication::watchUnixSignal не следует использовать. И, судя по прочтению кода, он не будет работать должным образом при использовании диспетчера событий glib (который используется в Linux по умолчанию).

Однако, в целом, вы можете безопасно перехватывать сигналы Unix в приложениях Qt, вам просто нужно написать немного кода самостоятельно. В документации даже есть пример — вызов функций Qt из обработчиков сигналов Unix.

Комментарии:

1. Поскольку нет способа прокомментировать эту страницу, я просто задам здесь краткий вопрос в надежде, что он будет полезен другим: почему часть инициализации была выделена в собственный статический метод «static int setup_unix_signal_handlers()» ?

2. Я не вижу никаких причин, по которым setup_unix_signal_handlers() является статическим, и он отлично работает как часть метода экземпляра. Массивы файловых дескрипторов также могут быть нестатическими, но только при наличии синтаксиса инициализации C 11 или более поздней версии.

Ответ №3:

Вы можете использовать QApplication::instance() (или более короткую версию макроса qApp ), оба доступны, по крайней мере, с Qt4:

 #include <QApplication>
#include <csignal>

void sigHandler(int s)
{
    std::signal(s, SIG_DFL);
    qApp->quit();
}

int main(int argc, char * argv[])
{
    QApplication app(argc, argv);

    std::signal(SIGINT,  sigHandler);
    std::signal(SIGTERM, sigHandler);

    return app.exec();
}
  

Ответ №4:

Я не нашел больше QApplication::watchUnixSignal документации, за исключением однострочника для Qt 4.0; особенно это не задокументировано в более поздних версиях Qt. Таким образом, похоже, что эта функциональность не рекламируется (и, следовательно, предполагается) для работы. При выполнении этого «Qt way», очевидно, хорош, я бы просто вернулся к использованию вместо этого системного вызова signal.

Ответ №5:

Как упоминал Jerkface Jones, похоже, что это не работает с использованием обработчика событий по умолчанию в Linux.

Если Qt использует необработанный обработчик событий Unix (не glib), Qt сразу же перехватит и обработает ^ C в своем обработчике сигналов, но сигнал unixSignal (int) не будет отправлен, пока Qt не выполнит обработку событий.

Если у вас запущен код (а не простаивает в ожидании отправки сигналов Qt), то вам нужно будет вызвать QApplication::processEvents(), чтобы Qt отправил сигнал.