在一些Qt示例中,我看到他们使用了
QTimer::singleShot(0, this , SLOT(funcA()))
,为什么不直接拨打广告位funcA
?使用QMetaMethod::invoke
调用带参数的函数也是同样的问题。
答案 0 :(得分:23)
以下几行在功能上都是等效的:
QTimer::singleShot(0, object, &Class::funcA); // Qt 5
QTimer::singleShot(0, object, SLOT(funcA())); // Qt 4
QMetaObject::invokeMethod(object, "funcA", Qt::QueuedConnection);
现在很明显,目的是在事件循环中执行调用。排队的通话会导致向QMetaCallEvent
发布object
。此事件由QObject::event
处理,并导致调用所需方法。因此,以下完全等效,即使后者是私有实现细节 - 让我跳过the details of instantiating the event:
QMetaObject::invokeMethod(object, "funcA", Qt::QueuedConnection);
QCoreApplication::postEvent(object, new QMetaCallEvent{...});
这在各种情况下都很方便。例如:
在处理完所有已发布的事件后执行一些代码。
仅在事件循环开始后执行。
调用由于C ++访问修饰符而无法访问的可调用方法。可调用的方法是:信号,槽和声明为Q_INVOKABLE
的方法。
当QObject
驻留在另一个线程中时,直接调用是不安全的(读取:错误!),除非您明确调用记录为线程安全的方法。
如果您希望确保事件循环立即退出,则排队调用是必需的:如果循环尚未运行,则直接quit()
调用是无操作。
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
app.quit(); // this is a no-op since the event loop isn't running yet
return app.exec(); // will not quit as desired
}
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection);
return app.exec(); // will return immediately
}
理想情况下,您使用this answer中的postToThread
,它提供了在其他线程中调用方法的最低成本方式:
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
postToThread([]{ qApp->quit(); });
}
另一种方法是使用QObject
作为信号源:
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
{
QObject src;
src.connect(&src, &QObject::destroyed, &app, &QCoreApplication::quit,
Qt::QueuedConnection);
}
return app.exec(); // will return immediately
}
另一种方法是使用自定义事件并在其析构函数中执行操作:
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
struct QuitEvent : QEvent {
QuitEvent() : QEvent(QEvent::None) {}
~QuitEvent() { qApp->quit(); }
};
QCoreApplication::postEvent(&app, new QuitEvent);
return app.exec(); // will return immediately
}
答案 1 :(得分:3)
每个系统都有一个事件循环来处理事件。比如说
application::processEvents()
{
// process event list..
}
现在,您编写QTimer::singleShot(0, this, SLOT(doSomething()));
的位置可能位于某个处理事件中。
完成此循环后,将再次调用processEvents,并执行doSomething()。
所以这就像在下一个事件循环中调用doSomething,而不是立即调用它。 希望你明白这个主意。
答案 2 :(得分:0)
这些方法还可用于从一个需要公共访问的范围调用类的受保护和私有成员(如果它们被定义为插槽)。