有一个QApplication::lastWindowClosed()
信号。 Qt docs说:
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);
}
}
如果关闭最后一个这样的窗口,是否还有一个很好的方法可以找到或甚至得到一个信号?
答案 0 :(得分:4)
使用processEvents
代替exec
的原因是错误的。这两个不等价。例如,exec()
将处理延迟删除事件,而processEvents
则不会。正如您刚刚发现的那样,lastWindowClosed
信号也不会发出。这应该告诉你,你做错了。
每次事件循环进行另一次迭代时,做某事的惯用Qt方法是使用零超时计时器。这些是不使用操作系统资源的虚拟计时器,它们是内部Qt结构。
以下示例说明了以下内容:
在QObject
。
使用State Machine Framework来管理应用程序的状态。我们有三种状态:
sWindows
是仍然显示应用程序窗口的状态。应用程序设置 not 以在最后关闭的窗口上退出。
sSetup
是最后一个窗口关闭时达到的状态。在这种状态下,我们要求Object
发送其通知信号,其执行零超时计时器的次数。这将在message
标签(C ++ 11代码)或count
标签(遗留代码)中设置正确的计数。状态机自动转换到以下状态。
sMessage
是显示消息标签的状态,应用程序设置为在最后一个窗口关闭时退出。
状态机的使用导致声明性代码:您告诉状态机如何行为,而不实现所有行为。您只需要实现特定于您的应用程序的行为,而不是Qt已经提供的行为。状态机管理的对象可以非常分离,并且声明机器行为的代码是有凝聚力的 - 它可以是一个函数,而不是传播。这被认为是很好的软件设计。
请注意,零超时计时器非常勤奋:只要事件循环为空,它将强制处理程序代码始终执行 。这将强制执行GUI线程的核心100%的CPU消耗。如果你无事可做,你应该stop()
计时器。
// 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();
}
#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"
答案 1 :(得分: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();
}
};