无论你有什么理由使用processEvents
代替exec
都是错误的。两者不等价。exec()
例如,将处理延迟删除事件,而processEvents
不会。正如您刚刚发现的那样,lastWindowClosed
也没有发出信号。这应该在那里告诉你你做错了。
每次事件循环进行另一次迭代时,Qt 惯用的做法是使用零超时计时器。这些是不使用操作系统资源的虚拟计时器,它们是内部 Qt 构造。
下面的示例说明了以下内容:
在QObject
.
使用状态机框架来管理应用程序的状态。我们有三个状态:
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"