Qt invokeMethod asynchronous call

summary

In the program, we often call functions. If the called functions take a long time, synchronous calls will cause the blockage of the main program. Qt provides a convenient function QMetaObject::invokeMethod, which is convenient for us to call asynchronously to solve this problem. This article only describes how to use QMetaObject::invokeMethod.

Function prototype

bool QMetaObject::invokeMethod(QObject *obj, const char *member, 
                            Qt::ConnectionType type, 
                            QGenericReturnArgument ret,
                            QGenericArgument val0 = QGenericArgument(nullptr), 
                            QGenericArgument val1 = QGenericArgument(), 
                            QGenericArgument val2 = QGenericArgument(), 
                            QGenericArgument val3 = QGenericArgument(), 
                            QGenericArgument val4 = QGenericArgument(),
                            QGenericArgument val5 = QGenericArgument(),
                            QGenericArgument val6 = QGenericArgument(), 
                            QGenericArgument val7 = QGenericArgument(),
                            QGenericArgument val8 = QGenericArgument(),
                            QGenericArgument val9 = QGenericArgument())

This function is used to call the member (signal or slot) of the object. Returns true if the member can be called. If there are no such members or the parameters do not match, false is returned.
QMetaObject::invokeMethod has five overloaded functions in addition to the above function, which will not be repeated here.
Parameter Description:
obj: pointer to the called object
Member: name of member method
type: connection method. The default value is Qt::AutoConnection

  • Qt::DirectConnection, the member will be called immediately. (synchronous call)
  • Qt::QueuedConnection, a QEvent will be sent and the member will be called immediately after the application enters the main event loop. (asynchronous call)
  • Qt::BlockingQueuedConnection, the method will be called in the same way as QT:: queuedconnection, except that the current thread will block until the event is passed. Using this connection type to communicate between objects in the same thread will cause a deadlock. (asynchronous call)
  • Qt::AutoConnection, if obj and the caller are in the same thread, the member will be called synchronously; Otherwise, it will call the member asynchronously.

ret: receive the return value of the called function
val0~val9: pass in the parameters of the called function, up to ten parameters
Note: Q must be used_ RETURN_ Arg () macro to encapsulate function return value, Q_ARG() macro to encapsulate function parameters.

Example

If an object obj has a slot function func(QString,int) and the return value is bool, the call method is as follows:

bool result;
//Synchronous call
QMetaObject::invokeMethod(obj, "func", Qt::DirectConnection,
                          Q_RETURN_ARG(bool, result),
                          Q_ARG(QString, "test"),
                          Q_ARG(int, 100);
//Asynchronous call
QMetaObject::invokeMethod(obj, "func", Qt::QueuedConnection,
                          Q_ARG(QString, "test"),
                          Q_ARG(int, 100);

Note: using Qt::QueuedConnection asynchronous call, the return value cannot be obtained, because this connection method is only responsible for handing the event to the event queue and then returning immediately. Therefore, the return value of the function cannot be determined.
However, we can use the Qt::BlockingQueuedConnection connection method mentioned above. This connection method will block the thread transmitting the signal until the queue connection slot returns, so as to ensure that we can get the return value of the function. Use the following:

bool result;
QMetaObject::invokeMethod(obj, "func", Qt::BlockingQueuedConnection,
                          Q_RETURN_ARG(bool, result),
                          Q_ARG(QString, "test"),
                          Q_ARG(int, 100);

It should be noted that there is a reminder in the qt official document that using this connection type to communicate between objects in the same thread will lead to deadlock

Finally, because the default value of connection type is Qt::AutoConnection, when the called obj is not in the same thread as the caller, you can directly call:

//This Tcpserver is a separate thread
//Call reportImageResult() in the main program, because the TcpServer object and the caller's main thread are not in the same thread.
//Qt::AutoConnection this connection method will be automatically called asynchronously
void TcpServer::reportImageResult(int imgId, const QImage &image, int result)
{
    QMetaObject::invokeMethod(this, "reportImageResultAsync",
                              Q_ARG(int, imgId),
                              Q_ARG(QImage, image),
                              Q_ARG(int, result);
}

Further discussion

When QMetaObject::invokeMethod is used to call a function, the program will report an error when the parameters of the function have user-defined types, because the types of call must be signals, slots, and types recognized by the Qt meta object system. Custom types can be registered using qRegisterMetaType() provided by Qt named types.
Examples are as follows:

//Header file
#include <QMetaType>

//Custom type
struct AsynResults {
    int imgId;
    QImage image;
    int result;
};

//When a class is constructed, the type is registered
qRegisterMetaType<AsynResults>("AsynResults");

//QMetaObject::invokeMethod call
QMetaObject::invokeMethod(this, "reportImageResultAsync",
                              Q_ARG(AsynResults, asynresults);

Added by shivani.shm on Mon, 03 Jan 2022 00:02:11 +0200