1 Overview
Qt Remote Object, called qtro for short, is a new module officially launched after Qt5.9, which is specially used for inter process communication (IPC). Qtro is essentially a point-to-point communication network. Each process accesses the qtro network through QRemoteObjectNode. The function providing node (which can be understood as a server) needs to use QRemoteObjectHost to register a QObject derived class that provides actual functions into the qtro network, and then other programs using the function connect to the Host through their respective QRemoteObjectNode, and then acquire a Replica of the function object. After the Replica is initialized, the program can use the signals, slots and attributes in the Replica as if the function class were local. Qtro is divided into two types of Replica: dynamic Replica and static Replica.
2 static and dynamic comparison
Static Replica:
advantage
It has a clear definition and is more suitable for use in C + + (because there are header files generated by repc).
Support the definition of complex structures such as POD.
More efficient. Because the structure definitions have been defined in C + +, there is no need for dynamic transmission and construction, which saves the cost.
inferiority
The Source side and the Replica side must strictly use the same version of the rep file, even if only one line of comments is added in the rep file, otherwise it will not be connected.
Dynamic Replica
advantage
Because the Client side does not need rep files, the Server side can modify them at any time, which avoids the disadvantages of static mode.
inferiority
Complex structure definitions such as POD are not supported.
It can only be used after initialization, which increases the additional complexity of programming and the additional overhead of building connections. This is determined by the dynamic feature.
pod types are simply understood as float, int, string, class, struct and other types.
3 static Replica
Server
1 create rep file
rep file is a kind of DSL (Domain Specific Language), which is specially used to define QtRO interface. When compiling, the file will first be processed by the program repc.exe to generate the corresponding header file and source file.
commoninterface.rep
class CommonInterface { SIGNAL(sigMessage(QString msg)) //The server sends a message to the client SLOT(void onMessage(QString msg)) //The server receives messages from the client }
2 compilation
Add QtRO module
QT += remoteobjects
Add rep file
REPC_SOURCE += \ ./Reps/CommonInterface.rep
Compile, and then find the generated rep header file in the output directory of the program
3 implementation function class
Create a class, inherit from the automatically generated class, and implement all virtual functions in it.
CommonInterface.h
#ifndef COMMONINTERFACE_H #define COMMONINTERFACE_H #include "rep_CommonInterface_source.h" class CommonInterface:public CommonInterfaceSource { Q_OBJECT public: explicit CommonInterface(QObject * parent = nullptr); //receive messages void onMessage(QString msg) override; //send message void sendMsg(QString msg); signals: void sigReceiveMsg(QString); }; #endif // COMMONINTERFACE_H
CommonInterface.cpp
#include "CommonInterface.h" CommonInterface::CommonInterface(QObject *parent): CommonInterfaceSource(parent) { } void CommonInterface::onMessage(QString msg) { emit sigReceiveMsg(msg); } void CommonInterface::sendMsg(QString msg) { sigMessage(msg); }
4. Implement the main logic of the Server
Widget.h
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include "CommonInterface.h" #include <QRemoteObjectHost> #include <QDateTime> QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACE class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget(); private: void init(); private slots: void onReceiveMsg(QString msg); void sendBtnClicked(); void lineRetrunPressed(); private: Ui::Widget *ui; CommonInterface* m_pInterface=nullptr; QRemoteObjectHost* m_pHost=nullptr; }; #endif // WIDGET_H
Widget.cpp
#include "Widget.h" #include "ui_Widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); init(); } Widget::~Widget() { delete ui; } void Widget::init() { setWindowTitle("This is Server"); ui->inputLine->setFocus(); m_pHost = new QRemoteObjectHost(this); m_pHost->setHostUrl(QUrl("local:interfaces")); m_pInterface = new CommonInterface(this); m_pHost->enableRemoting(m_pInterface); connect(m_pInterface,&CommonInterface::sigReceiveMsg,this,&Widget::onReceiveMsg); connect(ui->sendBtn,&QPushButton::clicked,this,&Widget::sendBtnClicked); connect(ui->inputLine,&QLineEdit::returnPressed,this,&Widget::lineRetrunPressed); } void Widget::onReceiveMsg(QString msg) { QDateTime curDateTime=QDateTime::currentDateTime(); QString temp=curDateTime.toString("yyyy-MM-dd hh:mm:ss"); ui->textEdit->append(temp+QString(" Client: ") + msg); } void Widget::sendBtnClicked() { QString msg = ui->inputLine->text(); if(!msg.isEmpty()){ m_pInterface->sendMsg(msg); } QDateTime curDateTime=QDateTime::currentDateTime(); QString temp=curDateTime.toString("yyyy-MM-dd hh:mm:ss"); ui->textEdit->append(temp+QString(" Server: ") + msg); ui->inputLine->clear(); } void Widget::lineRetrunPressed() { sendBtnClicked(); }
be careful
m_pHost->setHostUrl(QUrl("local:interfaces"));
The string format is "local:xxxx", where xxxx must be a unique string. It conflicts with other programs, otherwise it will not be connected.
The server is OK here.
client
1 create rep folder
Clients share a rep file, and then configure the por file
QT += remoteobjects REPC_REPLICA += \ ./Rep/CommonInterface.rep
Note that the addition method here is different from that on the server side. The server side is REPC_SOURCE.
2 compilation
Find the corresponding generated rep header file
Different from the server side, the client side does not need to re implement the function class, but only needs to connect to the server side in the main window.
3. Implement the main logic of the Client
Widget.h
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include <QRemoteObjectNode> #include "rep_CommonInterface_replica.h" #include <QDateTime> QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACE class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget(); private: void init(); private slots: void onReceiveMsg(QString msg); void sendBtnClicked(); void lineRetrunPressed(); private: Ui::Widget *ui; QRemoteObjectNode * m_pRemoteNode = nullptr; CommonInterfaceReplica * m_pInterface = nullptr; }; #endif // WIDGET_H
Widget.cpp
#include "Widget.h" #include "ui_Widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); init(); } Widget::~Widget() { delete ui; } void Widget::init() { setWindowTitle("This is Client"); ui->inputLine->setFocus(); m_pRemoteNode = new QRemoteObjectNode(this); m_pRemoteNode->connectToNode(QUrl("local:interfaces")); m_pInterface = m_pRemoteNode->acquire<CommonInterfaceReplica>(); connect(m_pInterface,&CommonInterfaceReplica::sigMessage, this,&Widget::onReceiveMsg); connect(ui->sendBtn,&QPushButton::clicked,this,&Widget::sendBtnClicked); connect(ui->inputLine,&QLineEdit::returnPressed,this,&Widget::lineRetrunPressed); } void Widget::onReceiveMsg(QString msg) { QDateTime curDateTime=QDateTime::currentDateTime(); QString temp=curDateTime.toString("yyyy-MM-dd hh:mm:ss"); ui->textEdit->append(temp+QString(" Server: ") + msg); } void Widget::sendBtnClicked() { QString msg = ui->inputLine->text(); if(!msg.isEmpty()){ m_pInterface->onMessage(msg); //Call the slot to send a message to the server } QDateTime curDateTime=QDateTime::currentDateTime(); QString temp=curDateTime.toString("yyyy-MM-dd hh:mm:ss"); ui->textEdit->append(temp+QString(" Client: ") + msg); ui->inputLine->clear(); } void Widget::lineRetrunPressed() { sendBtnClicked(); }
be careful
m_pRemoteNode->connectToNode(QUrl("local:interfaces")); m_pInterface = m_pRemoteNode->acquire<CommonInterfaceReplica>();
The connection address here must be consistent with that of the server, and then through acquire(); Connect to the server side.
The client is OK here.
Running screenshot
3 dynamic Replica
Change the above example to dynamic Replica.
Server side changes
m_pHost->enableRemoting(m_pInterface,QStringLiteral("Interfaces1"));
The second parameter name must be passed in.
Client side change
The rep file is not required. It is directly removed from the pro file
#REPC_REPLICA += \ # ./Rep/CommonInterface.rep
Then, when obtaining Replica, you need to use a dynamic version
QRemoteObjectDynamicReplica * m_pInterface = nullptr;
m_pInterface = m_pRemoteNode->acquireDynamic("Interfaces1");//Dynamic acquisition
In addition, there is a key point that needs to be changed, because without the rep file, the program doesn't know what the connected Server looks like when it starts. Therefore, the internal principle of dynamic Replica is to first obtain the meta information of the Source side after establishing a connection, and then dynamically build attributes, signals and slots on the Replica side. After these structures are completed, Replica will send an initialized signal, and then the Replica can be really used.
Only when Replica sends the initialized signal can it have the Source side meta information (attributes, signals and slots) and be used.
setWindowTitle("This is Client"); ui->inputLine->setFocus(); m_pRemoteNode = new QRemoteObjectNode(this); m_pRemoteNode->connectToNode(QUrl("local:interfaces")); m_pInterface=m_pRemoteNode->acquireDynamic("Interfaces1");//Dynamic acquisition //Only when Replica is initialized can it be used. Otherwise, connect is invalid connect(m_pInterface, &QRemoteObjectDynamicReplica::initialized, this, &Widget::onInitConnect);
It'll be all changed by now
Client complete code
Widget.h
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include <QRemoteObjectNode> #include "rep_CommonInterface_replica.h" #include <QDateTime> QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACE class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget(); private: void init(); private slots: void onReceiveMsg(QString msg); void sendBtnClicked(); void lineRetrunPressed(); void onInitConnect(); signals: void sigSendMsg(QString); private: Ui::Widget *ui; QRemoteObjectNode * m_pRemoteNode = nullptr; QRemoteObjectDynamicReplica * m_pInterface = nullptr; }; #endif // WIDGET_H
Widget.cpp
#include "Widget.h" #include "ui_Widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); init(); } Widget::~Widget() { delete ui; } void Widget::init() { setWindowTitle("This is Client"); ui->inputLine->setFocus(); m_pRemoteNode = new QRemoteObjectNode(this); m_pRemoteNode->connectToNode(QUrl("local:interfaces")); m_pInterface=m_pRemoteNode->acquireDynamic("Interfaces1");//Dynamic acquisition //Only when Replica is initialized can it be used. Otherwise, connect is invalid connect(m_pInterface, &QRemoteObjectDynamicReplica::initialized, this, &Widget::onInitConnect); } void Widget::onReceiveMsg(QString msg) { QDateTime curDateTime=QDateTime::currentDateTime(); QString temp=curDateTime.toString("yyyy-MM-dd hh:mm:ss"); ui->textEdit->append(temp+QString(" Server: ") + msg); } void Widget::sendBtnClicked() { QString msg = ui->inputLine->text(); if(!msg.isEmpty()){ emit sigSendMsg(msg); } QDateTime curDateTime=QDateTime::currentDateTime(); QString temp=curDateTime.toString("yyyy-MM-dd hh:mm:ss"); ui->textEdit->append(temp+QString(" Client: ") + msg); ui->inputLine->clear(); } void Widget::lineRetrunPressed() { sendBtnClicked(); } void Widget::onInitConnect() { connect(m_pInterface,SIGNAL(sigMessage(QString)),this,SLOT(onReceiveMsg(QString))); connect(this,SIGNAL(sigSendMsg(QString)),m_pInterface,SLOT(onMessage(QString))); connect(ui->sendBtn,&QPushButton::clicked,this,&Widget::sendBtnClicked); connect(ui->inputLine,&QLineEdit::returnPressed,this,&Widget::lineRetrunPressed); }