QTimer :: singleShot和QMetaMethod :: invoke

use*_*805 14 qt

在一些Qt示例中,我看到他们使用 QTimer::singleShot(0, this , SLOT(funcA())),为什么不funcA直接调用插槽?用于QMetaMethod::invoke调用带参数的函数也是同样的问题.

Rei*_*ica 23

以下几行在功能上都是等效的:

QTimer::singleShot(0, object, &Class::funcA); // Qt 5
QTimer::singleShot(0, object, SLOT(funcA())); // Qt 4
QMetaObject::invokeMethod(object, "funcA", Qt::QueuedConnection); 
Run Code Online (Sandbox Code Playgroud)

现在很明显,目的是在事件循环中执行调用.排队的呼叫导致发布QMetaCallEventobject.此事件由QObject::event所需方法调用并导致调用所需方法.因此,以下内容完全等效,即使后者是私有实现细节 - 让我跳过实例化事件的细节:

QMetaObject::invokeMethod(object, "funcA", Qt::QueuedConnection);
QCoreApplication::postEvent(object, new QMetaCallEvent{...});
Run Code Online (Sandbox Code Playgroud)

这在各种情况下都很方便.例如:

  • 在处理了所有迄今发布的事件之后执行一些代码.

  • 仅在事件循环开始后执行.

  • 调用由于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
}
Run Code Online (Sandbox Code Playgroud)

理想情况下,你会使用postToThread这个答案,它提供调用其他线程方法的成本最低的方式:

int main(int argc, char ** argv) {
  QCoreApplication app{argc, argv};
  postToThread([]{ qApp->quit(); });
}
Run Code Online (Sandbox Code Playgroud)

另一种方法是使用a 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
}
Run Code Online (Sandbox Code Playgroud)

另一种方法是使用自定义事件并在其析构函数中执行操作:

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
}
Run Code Online (Sandbox Code Playgroud)