En Qt, descubra que todas las ventanas están cerradas, cuando se usa QApplication :: processEvents ()?

Hay una QApplication::lastWindowClosed() . Los documentos de Qt dicen:

 This signal is emitted from QApplication::exec() when the last visible primary window [...] is closed. 

Sin embargo, usé QApplication::processEvents() lugar de QApplication::exec() en un bucle. Vea este ejemplo mínimo. (Guardar como qapp.h , debe terminar en .h , y ejecutar qmake -project && qmake && make )

 #include  #include  #include  #include  int exitprogram = 0; class MyMainWindow : public QMainWindow { Q_OBJECT public slots: void request_exit() { qDebug() << "exit requested"; exitprogram = 1; } }; int main(int argc, char** argv) { QApplication app(argc, argv); MyMainWindow w; QObject::connect(&app, SIGNAL(lastWindowClosed()), &w, SLOT(request_exit())); w.show(); while(!exitprogram) { app.processEvents(QEventLoop::AllEvents, 20); } } 

¿Todavía hay una buena forma de averiguarlo, o incluso de obtener una señal, si se cierra la última ventana de este tipo?

Cualquier razón que tenga para usar processEvents en lugar de exec es incorrecta. Los dos no son equivalentes. exec() , por ejemplo, procesará los eventos de eliminación diferida, mientras que processEvents no lo hará. Como acabas de descubrir, la señal lastWindowClosed tampoco se emite. Esto debería decirte allí mismo que lo estás haciendo mal.

La forma idiomática de Qt de hacer algo cada vez que el bucle de eventos se realiza en otra iteración, es usar un temporizador de tiempo cero. Esos son temporizadores virtuales que no usan recursos del sistema operativo, son una construcción interna de Qt.

El siguiente ejemplo ilustra lo siguiente:

  1. Uso de un temporizador de tiempo de espera cero dentro de un QObject .

  2. Uso de State Machine Framework para gestionar el estado de la aplicación. Tenemos tres estados:

    • sWindows es el estado en el que aún se muestran las ventanas de la aplicación. La aplicación está configurada para no cerrarse en la última ventana que se está cerrando.

    • sSetup es el estado alcanzado cuando se cerró la última de las ventanas. En este estado, le pedimos a nuestro Object que envíe su señal de notificación con la cantidad de veces que ejecutó el temporizador de tiempo cero. Esto establecerá el recuento adecuado en la etiqueta del message (código C ++ 11) o en la etiqueta del count (código heredado). La máquina de estado cambia automáticamente al siguiente estado.

    • sMessage es el estado en el que se muestran las tags de los mensajes y la aplicación está configurada para salir cuando se cierra la última ventana.

El uso de una máquina de estados conduce al código declarativo: usted le dice a la máquina de estados cómo debe comportarse, sin implementar todo el comportamiento. Solo tiene que implementar los comportamientos que son específicos para su aplicación y que Qt aún no ha proporcionado. Los objetos que maneja la máquina de estados pueden estar muy desacoplados, y el código que declara el comportamiento de la máquina es cohesivo: puede estar todo en una función, en lugar de estar distribuido. Esto se considera un buen diseño de software.

Tenga en cuenta que el temporizador de tiempo de espera cero es muy diligente: forzará que el código del controlador se ejecute constantemente cuando el bucle de eventos esté vacío. Esto forzará un consumo de CPU del 100% en el núcleo donde se está ejecutando el subproceso de la GUI. Si no tiene nada que hacer, debe stop() el temporizador.

Código Qt 5 C ++ 11

 // https://github.com/KubaO/stackoverflown/tree/master/questions/close-process-19343325 #include  int main(int argc, char** argv) { QApplication app{argc, argv}; QLabel widget{"Close me :)"}; QLabel message{"Last window was closed"}; int counter = 0; auto worker = [&]{ counter++; }; QTimer workerTimer; QObject::connect(&workerTimer, &QTimer::timeout, worker); workerTimer.start(0); QStateMachine machine; QState sWindows{&machine}; QState sSetup {&machine}; QState sMessage{&machine}; sWindows.assignProperty(qApp, "quitOnLastWindowClosed", false); sWindows.addTransition(qApp, &QGuiApplication::lastWindowClosed, &sSetup); QObject::connect(&sSetup, &QState::entered, [&]{ workerTimer.stop(); message.setText(QString("Last window was closed. Count was %1.").arg(counter)); }); sSetup.addTransition(&sMessage); sMessage.assignProperty(&message, "visible", true); sMessage.assignProperty(qApp, "quitOnLastWindowClosed", true); machine.setInitialState(&sWindows); machine.start(); widget.show(); return app.exec(); } 

Código Qt 4/5 C ++ 11

 #include  #include  #include  #include  class Object : public QObject { Q_OBJECT QBasicTimer m_timer; int m_counter = 0; protected: void timerEvent(QTimerEvent * ev) { if (ev->timerId() == m_timer.timerId()) m_counter ++; } public: Object(QObject * parent = 0) : QObject{parent} { m_timer.start(0, this); } Q_SLOT void stop() const { m_timer.stop(); emit countedTo(m_counter); } Q_SIGNAL void countedTo(int) const; }; int main(int argc, char** argv) { QApplication app{argc, argv}; Object object; QLabel widget{"Close me :)"}; QLabel message{"Last window was closed"}; QLabel count; QStateMachine machine; QState sWindows{&machine}; QState sSetup{&machine}; QState sMessage{&machine}; sWindows.assignProperty(qApp, "quitOnLastWindowClosed", false); sWindows.addTransition(qApp, "lastWindowClosed()", &sSetup); object.connect(&sSetup, SIGNAL(entered()), SLOT(stop())); count.connect(&object, SIGNAL(countedTo(int)), SLOT(setNum(int))); sSetup.addTransition(&sMessage); sMessage.assignProperty(&message, "visible", true); sMessage.assignProperty(&count, "visible", true); sMessage.assignProperty(qApp, "quitOnLastWindowClosed", true); machine.setInitialState(&sWindows); machine.start(); widget.show(); return app.exec(); } #include "main.moc"