17

In some Qt examples, I see they use QTimer::singleShot(0, this , SLOT(funcA())), why not to call the slot funcA directly? also the same question for using QMetaMethod::invoke to call function with parameters.

user3450805
  • 661
  • 6
  • 16

3 Answers3

26

The following lines are all functionally equivalent:

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

As is now apparent, the intent is to execute the call within the event loop. The queued call results in the posting of an QMetaCallEvent to the object. This event is handled by QObject::event and results in the call of the desired method. Thus, the following are exactly equivalent, even if the latter is a private implementation detail - letting me skip the details of instantiating the event:

QMetaObject::invokeMethod(object, "funcA", Qt::QueuedConnection);
QCoreApplication::postEvent(object, new QMetaCallEvent{...});

This comes handy in various situations. For example:

  • To execute some code after all hitherto posted events have been handled.

  • To execute only after the event loop has started.

  • To call an invokable method that's not accessible due to C++ access modifiers. The invokable methods are: signals, slot, and methods declared Q_INVOKABLE.

  • The direct call is unsafe (read: an error!) when a QObject resides in another thread, unless you're explicitly calling a method documented as thread-safe.

The queued call is a necessity if you wish to ensure that an event loop quits immediately: a direct quit() call is a no-op if the loop is not running yet.

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
}

Ideally, you'd use postToThread from this answer, it offers the lowest-cost way of calling methods in other threads:

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

An alternative way of doing it is using a QObject as a signal source:

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
}

Yet another way would be to use a custom event and act in its destructor:

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
}
Community
  • 1
  • 1
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • I am getting `QObject::startTimer: Timers can only be used with threads started with QThread` when using QTimer::singleShot in a pure C++ function (not inside a QTthread) while `QMetaObject::invokeMethod` doesn't have this issue. Is there any way to use QTimer::singleShot (or some equivalent method) which supports compile-time checking? – Isaac Aug 15 '16 at 00:03
  • @Isaac Yes. See the edit. The most efficient and foolproof way of doing it is given in the TL;DR of [this answer](http://stackoverflow.com/questions/21646467/how-to-execute-a-functor-in-a-given-thread-in-qt-gcd-style/21653558#21653558). – Kuba hasn't forgotten Monica Aug 15 '16 at 13:25
3

Every system has a event loop where events are processed. Say like

application::processEvents()
{
// process event list..
}

Now the place where you write QTimer::singleShot(0, this, SLOT(doSomething())); might be inside some processing event. When this loop is done, processEvents will be called again and in that the doSomething() will be executed.

So this is like calling doSomething in the next event loop, rather than calling it immediately. Hope you get the idea.

Thomas Ayoub
  • 29,063
  • 15
  • 95
  • 142
  • Once `QCoreApplication::exec` is entered, your gui thread code is **by definition** executed due to an event (or an asynchronous Unix-signal-like platform-specific mechanism). So it is really obfuscating things to say "might be inside some processing event". Once `app.exec()` is called from `main()`, events are the only reason for *any* UI code to execute, pretty much. – Kuba hasn't forgotten Monica Jun 11 '14 at 20:21
  • When you're processing any posted (as opposed to sent) events in Qt, the event loop's event dispatcher is on the call stack somewhere below the ultimate call to `Foo::event(QEvent*)`. When your code finishes, the control returns to the dispatcher, and the dispatcher picks up another event, and invokes the target object's `event(QEvent*)` method again - or goes to sleep waiting for more events to come. – Kuba hasn't forgotten Monica Jun 11 '14 at 20:22
  • "events are the only reason for any UI code to execute" disclaimer: Unix signals and Windows I/O completion ports notwithstanding. – Kuba hasn't forgotten Monica Jul 16 '15 at 17:16
0

These methods can also be used to invoke protected and private members of a class (if they are defined as slots) from a scope that would otherwise require public access.

Jason
  • 389
  • 2
  • 6