0. Preface
Qt Remote Objects is an RPC remote call module added from Qt5.9. For details, you can learn from the document:
https://doc.qt.io/qt-5/qtremoteobjects-gettingstarted.html
From the perspective of personal use practice, it also has some advantages over other popular RPC frameworks:
- Qt like: if grpc is used, some Qt's own types, such as QString and QByteArray, need to be converted into several basic types supported by grpc. There is a risk of inconsistent string encoding and decoding during serialization. Using QtRO naturally supports these types of QTS, and the defined interface supports signal slots.
- Portability: QtRO module is a lightweight module and easy to use.
There are also some problems:
- Too few functions: at present, only interface definitions on some meta objects, such as attributes and signal slots, are supported. There is no streaming interface, no common class members or functions.
- The host cannot directly access the currently connected nodes. The server is shared by all connected nodes. If the host source sends a signal, all connected nodes will receive this signal. From this point of view, QtRO is more suitable for process interaction of a single client than concurrent access of multiple clients. If multiple clients want to operate independently, signals should not be used. The results can be returned through the return value of slot function.
1. Interface definition rep file
Before using the QtRO module, add remoteobjects to the pro file to enable the module
QT += remoteobjects
There are two ways to use, static souce and dynamic replica. This article is a note on the static source. The basic usage process is: write the interface definition file rep and generate Replica (client) and source (server) interfaces are implemented, and the generated interface is used in the code for remote call.
Refer to the official document for interface definition:
https://doc.qt.io/qt-5/qtremoteobjects-repc.html
The definition of attribute and signal slot is similar to that of QObject subclass.
Here are some problems I have encountered:
- ENUM must be defined in class, otherwise it cannot be accessed directly, because it will set a layer outside QObject class.
- POD members are attributes. If you want to use a custom structure and cannot write it directly in the rep, you need to include the structure header file, so you can use it as long as the custom types supported by the signal slot.
- To use a custom type, you need to write a comparison operator to support comparison, such as in the automatically generated attribute set function Input and output serialization of QDataStream.
Here are my simple.rep interface definition file and Define.h structure header file:
//rep document https://doc.qt.io/qt-5/qtremoteobjects-repc.html //Import header file of structure definition #include "Define.h" //POD is similar to a structure, but its members are attributes POD MyPod(QString name, int age, bool sex) //MODEL is related to QAbstractItemModel. It's too chicken rib. It's omitted //class class MyObject{ //attribute PROP(int value) } //Two interfaces are generated //[class name Replica] to the client //[class name Source] and [class name SimpleSource] to the server class Interface{ //attribute PROP(QString name="gongjianbo" READONLY) PROP(QString tag) //Use the class defined in rep as the attribute CLASS subObject(MyObject) //signal SIGNAL(dataChanged(const QString &data)) //Slot function. If the return value is omitted, it will be void SLOT(void setData(const QString &data)) SLOT(QString getData()) //Enumeration. If it is not placed in the class, it will automatically set another layer of QObject class, which is inconvenient to access ENUM MyEnum{ EnumA = 0, EnumB } SLOT(void testEnum(MyEnum t)) //Use custom structures SLOT(void testStruct(MyStruct t)) }
#pragma once #include <QObject> #include <QDataStream> //The user-defined structure is supported, but it can only be defined in include, not in rep //Since the automatically generated code needs to be compared and serialized, some functions need to be overloaded struct MyStruct { QString name; int age; bool sex; }; Q_DECLARE_METATYPE(MyStruct) bool operator==(const MyStruct &left, const MyStruct &right); bool operator!=(const MyStruct &left, const MyStruct &right); //Overloads input and output operators to support datastream serialization custom structures QDataStream& operator <<(QDataStream& out, const MyStruct& item); QDataStream& operator >>(QDataStream& in, MyStruct& item);
After definition, the following is introduced at the interface Caller:
REPC_REPLICA += $$PWD/simple.rep
After construction, it will be generated in the output directory rep_simple_replica.h, which implements the interface QRemoteObjectReplica subclass.
Introduce at the interface server:
REPC_SOURCE += $$PWD/simple.rep
After construction, it will be generated in the output directory rep_simple_source.h, which contains an interface class that implements some interfaces (slots need to be inherited and rewritten).
Of course, it can also be used REPC_ Merge is introduced, and two sets of interfaces for calling and service will be generated at the same time.
2. Basic operation
After the interface class file is generated, it is introduced at the calling end and the server end respectively. The server may need to implement some virtual functions, which can be used directly by the caller.
The first is the basic operation of the caller (note that this Interface is called Interface because I define the rep Interface):
QRemoteObjectNode *remoteNode = new QRemoteObjectNode(this); remoteNode->connectToNode(QUrl("local:qro_test")); InterfaceReplica *replica = remoteNode->acquire<InterfaceReplica>(); //The caller can judge the connection and disconnection, and the server has no corresponding interface to query the caller list connect(irep,&InterfaceReplica::stateChanged, this,[this](QRemoteObjectReplica::State state, QRemoteObjectReplica::State oldState){ }); //Connect server signal connect(replica,&InterfaceReplica::dataChanged,[]{}); //Call the interface of the server if(replica->isReplicaValid()) replica->setData("hello");
Unfortunately, the caller cannot send signals directly, but can only receive signals from the server and call slot functions. After the connecttonode is called, if the server is not turned on, it will automatically detect. You can also call the waitForSource interface of replica to take the initiative. After the server is restarted, the node will be automatically reconnected.
Basic operations of the server:
QRemoteObjectHost *host = new QRemoteObjectHost(this); MySource source; //Inherit the generated class and implement some virtual functions host->setHostUrl(QUrl("local:qro_test")); host->enableRemoting(&source); //After the service is started, it can send a signal or be called emit source.dataChanged("hello");
See the official example for the complete code:
https://doc.qt.io/qt-5/remoteobjects-example-static-source.html
Then share my Demo:
https://github.com/gongjianbo/MyTestCode2021/tree/master/Qt/QtRemoteObjects
3. Reference
file: https://doc.qt.io/qt-5/qtremoteobjects-gettingstarted.html