Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

QTimer::singleShot and QMetaMethod::invoke

Tags:

qt

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.

like image 473
user3450805 Avatar asked Jun 10 '14 13:06

user3450805


People also ask

What does QTimer singleShot do?

[static] void QTimer::singleShot(int msec, const QObject *receiver, const char *member) This static function calls a slot after a given time interval. It is very convenient to use this function because you do not need to bother with a timerEvent or create a local QTimer object.

How do you use QTimer?

The QTimer class provides a high-level programming interface for timers. To use it, create a QTimer, connect its timeout() signal to the appropriate slots, and call start(). From then on, it will emit the timeout() signal at constant intervals.


1 Answers

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
}
like image 135
Kuba hasn't forgotten Monica Avatar answered Oct 05 '22 03:10

Kuba hasn't forgotten Monica