0

有一个QApplication::lastWindowClosed()信号。Qt 文档说:

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

但是,我在循环中使用QApplication::processEvents()而不是。QApplication::exec()请参阅此最小示例。(另存为qapp.h,它必须以 结束.h,然后运行qmake -project && qmake && make

#include <QApplication>
#include <QDebug>
#include <QObject>
#include <QMainWindow>

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

如果最后一个这样的窗口正在关闭,是否还有一个好方法可以找出,甚至得到一个信号?

4

2 回答 2

3

无论你有什么理由使用processEvents代替exec都是错误的。两者等价。exec()例如,将处理延迟删除事件,而processEvents不会。正如您刚刚发现的那样,lastWindowClosed也没有发出信号。这应该在那里告诉你你做错了。

每次事件循环进行另一次迭代时,Qt 惯用的做法是使用零超时计时器。这些是不使用操作系统资源的虚拟计时器,它们是内部 Qt 构造。

下面的示例说明了以下内容:

  1. QObject.

  2. 使用状态机框架来管理应用程序的状态。我们有三个状态:

    • sWindows是仍然显示应用程序窗口时的状态。应用程序设置为在关闭最后一个窗口时退出。

    • sSetup是最后一个窗口关闭时达到的状态。在这种状态下,我们要求我们Object发送它的通知信号以及它执行零超时计时器的次数。message这将在标签(C++11 代码)或count标签(旧代码)中设置正确的计数。状态机自动转换到以下状态。

    • sMessage是显示消息标签时的状态,应用程序设置为在最后一个窗口关闭时退出。

状态机的使用导致声明性代码:您告诉状态机如何表现,而不实现所有行为。您只需实现特定于您的应用程序且 Qt 尚未提供的行为。状态机管理的对象可以非常解耦,并且声明机器行为的代码是内聚的——它可以全部在一个函数中,而不是分散在各处。这被认为是好的软件设计。

请注意,零超时计时器非常勤奋:只要事件循环为空,它将强制您的处理程序代码不断执行这将强制在 GUI 线程恰好正在执行的内核上消耗 100% 的 CPU。如果你无事可做,你应该stop()计时器。

Qt 5 C++11 代码

// https://github.com/KubaO/stackoverflown/tree/master/questions/close-process-19343325
#include <QtWidgets>

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();
}

Qt 4/5 C++11 代码

#include <QApplication>
#include <QLabel>
#include <QStateMachine>
#include <QBasicTimer>

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"
于 2013-10-13T15:22:52.893 回答
0

我修改了你的代码,现在它可以工作了。我覆盖closeEvent

==> test.cpp <==
#include "windows.hpp"

int exitprogram = 0;

int main(int argc, char** argv)
{
    QApplication app(argc, argv);
    MyMainWindow w;
    w.show();
    while(!exitprogram)
    {
        app.processEvents(QEventLoop::AllEvents, 20);
    }
}

==> test.pro <==
TEMPLATE = app
TARGET = test
QT += widgets
INCLUDEPATH += .
HEADERS += windows.hpp

# Input
SOURCES += test.cpp

==> windows.hpp <==
#include <QApplication>
#include <QDebug>
#include <QObject>
#include <QMainWindow>
#include <QCloseEvent>

extern int exitprogram;

class MyMainWindow : public QMainWindow
{
    Q_OBJECT
public slots:
    void closeEvent(QCloseEvent *event) override {
        exitprogram = 1;
        event->accept();
    }
};
于 2021-03-15T14:23:35.743 回答