在一些 Qt 示例中,我看到他们使用
QTimer::singleShot(0, this , SLOT(funcA()))
,为什么不直接调用槽funcA
?使用 QMetaMethod::invoke
调用带参数的函数也是同样的问题。
最佳答案
以下几行在功能上都是等效的:
QTimer::singleShot(0, object, &Class::funcA); // Qt 5
QTimer::singleShot(0, object, SLOT(funcA())); // Qt 4
QMetaObject::invokeMethod(object, "funcA", Qt::QueuedConnection);
现在很明显,其目的是在事件循环内执行调用。排队的调用会导致将 QMetaCallEvent
发布到对象
。该事件由QObject::event
处理并导致调用所需的方法。因此,以下内容完全等效,即使后者是私有(private)实现细节 - 让我跳过 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
}
关于QTimer::singleShot 和 QMetaMethod::invoke,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24142450/