问题描述
有一个 QApplication :: lastWindowClosed()
信号。 说:
当最后一个可见主窗口[...]关闭时,此信号从QApplication :: exec()
发出。但是,我使用 QApplication :: processEvents()$ c $ c>而不是 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()< 退出请求;
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);
}
}
还有一个好的方法可以找出,如果最后这样的窗口被关闭,甚至会得到一个信号?
解决方案 c> processEvents
代替 exec
错误。这两个是不是等效的。 exec()
将会处理延迟删除事件,而 processEvents
则不会。正如你刚刚发现的, lastWindowClosed
信号也不会发出。这应该告诉你,你在做错了。
每次事件循环进行另一次迭代时,惯用的Qt方式是使用零超时定时器。这些是不使用操作系统资源的虚拟计时器,它们是一个内部Qt结构。
下面的例子说明了以下内容:
-
在 QObject
中使用零超时计时器。
-
使用管理应用程序的状态。我们有三个状态:
-
sWindows
窗口仍然显示。
-
sSetup
是最后一个窗口关闭时达到的状态。在这种状态下,我们要求我们的 Object
发送其通知信号与它执行零超时定时器的次数。这将在消息
标签(C ++ 11代码)或计数
标签中设置正确的计数码)。状态机自动转换到以下状态。
-
sMessage
是消息标签
使用状态机导致声明性代码:你告诉状态机如何行为,而不实现所有的行为。您只需实现特定于您的应用程序的行为,而不是由Qt提供。状态机管理的对象可以非常多地解耦,并且声明机器的行为的代码是粘性的 - 它可以全部在一个功能中,而不是散布。这被认为是一个好的软件设计。
请注意,零超时定时器是非常勤奋的:它会强制你的处理程序代码不断执行每当事件循环为空时。这将强制GUI线程恰好在执行的核心上的100%CPU消耗。如果你没有什么可做,你应该 stop()
计时器。
Qt 5 C ++ 11代码
// https://github.com/KubaO/stackoverflown/tree/master/问题/关闭进程-19343325
#include< QtWidgets>
int main(int argc,char ** argv)
{
QApplication app {argc,argv};
QLabel widget {关闭我:)};
QLabel message {Last window was closed};
int counter = 0;
auto worker = [&] {
counter ++;
};
QTimer workerTimer;
QObject :: connect(& workerTimer,& QTimer :: timeout,worker);
workerTimer.start(0);
QStateMachine机器;
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最后一个窗口被关闭。计数为%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对象: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 counted(m_counter);
}
Q_SIGNAL void countedTo(int)const;
};
int main(int argc,char ** argv)
{
QApplication app {argc,argv};
对象对象;
QLabel widget {关闭我:)};
QLabel message {Last window was closed};
QLabel count;
QStateMachine机器;
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(& s Message);
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()
}
#includemain.moc
There is a QApplication::lastWindowClosed()
signal. The Qt docs say:
This signal is emitted from QApplication::exec()
when the last visible primary window [...] is closed.
However, I used QApplication::processEvents()
instead of QApplication::exec()
in a loop. See this minimal example. (save as qapp.h
, it must end on .h
, and run 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);
}
}
Is there still a good way to find out, or to even get a signal, if the last such window is being closed?
解决方案 Whatever reason you have for using processEvents
in place of exec
is wrong. The two are not equivalent. exec()
will, for example, process the deferred deletion events, while processEvents
will not. As you've just found out, the lastWindowClosed
signal is not emitted either. This should be telling you right there that you're doing it wrong.
The idiomatic Qt way of doing something each time the event loop goes for another iteration, is to use a zero-timeout timer. Those are virtual timers that do not use operating system resources, they are an internal Qt construct.
The example below illustrates the following:
Use of a zero-timeout timer within a QObject
.
Use of the State Machine Framework to manage the state of the application. We have three states:
sWindows
is the state when the application windows are still shown. The application is set not to quit on the last window being closed.
sSetup
is the state reached when the last of the windows was closed. In this state we ask our Object
to send its notification signal with the number of times it executed the zero-timeout timer. This will set the proper count in the message
label (C++11 code) or in the count
label (legacy code). The state machine automatically transitions to the following state.
sMessage
is the state when the message labels are shown, and the application is set to quit upon the last window being closed.
The use of a state machine leads to declarative code: you tell the state machine how to behave, without implementing all of the behavior. You only have to implement the behaviors that are specific to your application and not already provided by Qt. The objects that the state machine manages can be very much decoupled, and the code that declares the behavior of the machine is cohesive - it can be all in one function, instead of being spread around. This is considered to be good software design.
Do note that the zero-timeout timer is very diligent: it will force your handler code to execute constantly whenever the event loop is empty. This will force 100% CPU consumption on the core where the GUI thread happens to be executing. If you have nothing to do, you should stop()
the timer.
Qt 5 C++11 Code
// 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 Code
#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"
这篇关于在Qt中,发现当使用QApplication :: processEvents()时,所有的窗口都关闭了?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!