利用QtRemoteObjects在C++与Python之间通讯

本文介绍了如何使用QtRemoteObjects在Python和C++之间实现进程间通讯。通过创建Python的RootComObj类,定义信号和槽函数,结合QRemoteObjectHost实现Python端的远程对象注册。C++端则通过动态Replica方式连接Python服务,实现信号槽的连接,进行数据交互。这种方式基于TCP/IP,提供了类似于Qt的信号槽机制,简化了跨语言通讯的复杂性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

资料:
https://github.com/PyQt5/PyQt/tree/master/QtRemoteObjects
https://github.com/PyQt5/PyQt/blob/master/QtRemoteObjects/simpleswitch/directconnectdynamicclient.py

由于项目要求,需要在python程序与c++程序之间通讯。经过取舍,觉得还是直接用QtRemoteObjects来实现这个通讯会比较合适。

QtRemoteObjects(以下简称QtRO)是一个qt的用于进程间通讯的模块。用QtRO通讯的两个进程(程序)可以在同一台电脑上,也可以在不同的电脑上(不同的电脑时,只需要在QRemoteObjectHost的url上修改一下就行)。
QtRO本质上是基于TCP/IP(可能也用到UDP?)实现的一个通讯协议,他可以让两个不同的进程(程序)也能享受到Qt的信号-槽机制,极大地减低了通讯成本。

关于QtRO的服务端与客户端的C++的实现可以参考这位大神写的介绍, 这里就不做赘述了。

这里主要介绍在python与c++之间的实现(这里是简单实现了一些功能,复杂的可以自行实现)
在python端,我们定义一个类,这个类包含了一个信号和一个槽。

RootComObj.py:

from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal, pyqtProperty
from PyQt5.QtCore import QVariant


class RootComObj(QObject):
    def __init__(self):
        super().__init__()

    # 用来发送信息到C++那边去,主要是告知此时的状态
    serverToClient = pyqtSignal(QVariant)

    # 这个是供c++那边直接调用的槽函数,可以用来设置参数,也可以通过返回值读取参数。
    # 这里只是提供基础的数据通讯,复杂的通讯可以在这个基础上实现自己的封装
    # The slot exposed to a remote client.
    @pyqtSlot(QVariant, result=QVariant)
    def clientToServer(self, cmd):
        return cmd

使用PySide2的版本:

from PySide2.QtCore import QObject, Slot, Signal

class RootComObj(QObject):
    def __init__(self):
        super().__init__()

    # 用来发送信息到C++那边去,主要是告知此时的状态
    serverToClient = Signal("QVariant")

    # 这个是供c++那边直接调用的槽函数,可以用来设置参数,也可以通过返回值读取参数。
    # 这里只是提供基础的数据通讯,复杂的通讯可以在这个基础上实现自己的封装
    # The slot exposed to a remote client.
    @Slot("QVariant", result="QVariant")
    def clientToServer(self, cmd):
        print("c to s:", cmd)
        return cmd

main.py:

import sys

import PyQt5.QtCore
from PyQt5.QtCore import QUrl, QTimer, QDateTime
from PyQt5.QtWidgets import QApplication
from PyQt5.QtRemoteObjects import QRemoteObjectHost, QRemoteObjectNode

from RootComObj import RootComObj

comObj = RootComObj()

def timeout():
    comObj.serverToClient.emit(QDateTime.currentDateTime().toString('yyyyMMdd hh:mm:ss'))
    
def main():
    app = QApplication(sys.argv)

    remoteHost = QRemoteObjectHost(QUrl("local:replica"))
    ret = remoteHost.enableRemoting(comObj, 'demo')

    print(remoteHost, ret, PyQt5.QtCore.PYQT_VERSION_STR)

    tm = QTimer()
    tm.setInterval(1000)
    tm.setSingleShot(False)
    tm.timeout.connect(timeout)
    tm.start()

    return app.exec()


if __name__ == '__main__':
    main()

使用PySide2的版本:

import PySide2.QtCore
from PySide2.QtCore import QUrl, QTimer, QDateTime, QVariantAnimation
from PySide2.QtWidgets import QApplication
from PySide2.QtRemoteObjects import QRemoteObjectHost, QRemoteObjectNode

from RootComObj import RootComObj

comObj = RootComObj()


def main():
    app = QApplication(sys.argv)

    remoteHost = QRemoteObjectHost(QUrl("local:replica"))
    ret = remoteHost.enableRemoting(comObj, 'demo')
    
    return app.exec_()

接着,C++这边采用的是动态Replica的方式(实际上静态也行,只要你写对那个rep文件就行)

Receiver.h

class Receiver: public QObject
{
    Q_OBJECT
public:
    Receiver()
    {

    }

public slots:
    void processServerCmd(QVariant cmd)
    {
        qDebug() << "the server cmd is:" << cmd;
    }
};

main.cpp:

#include "mainwindow.h"
#include "Receiver.h"

#include <QApplication>
#include <QDebug>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    //MainWindow w;
    //w.show();

    Receiver *rev = new Receiver();

    QRemoteObjectNode repNode; // create remote object node
    repNode.connectToNode(QUrl("local:replica")); // connect with remote host node

    auto rep  = repNode.acquireDynamic("demo");

    QObject::connect(rep, &QRemoteObjectReplica::stateChanged, [=](QRemoteObjectReplica::State sta, QRemoteObjectReplica::State oldSta){
        qDebug() << "oldState:" << oldSta;
        qDebug() <<  "state:" << sta;
    });

    //动态replica要等初始化完成之后(也就是连接上服务器+内部初始化完成)才能做信号槽的连接,静态就无所谓
    QObject::connect(rep, &QRemoteObjectReplica::initialized, [=](){

        // 可以利用Qt的反射机制输出一些东西看看
        //        auto obj = rep->metaObject();
        //        for(int i = 0; i < obj->methodCount(); i++)
        //        {
        //            auto method = obj->method(i);
        //            qDebug() << method.name() << method.tag() << method.typeName() << method.methodType();
        //            for(int j = 0; j < method.parameterCount(); j++)
        //            {
        //                qDebug() << "---" << method.parameterType(j) << method.parameterNames() << method.parameterTypes();
        //            }
        //        }

        // 获取属性
//        qDebug() << "initialized:" << rep->property("currState");

        // 连接信号, 动态replica的话,连接信号就不能用lambda表达式了,需要这样直接指明信号槽字符串
        QObject::connect(rep, SIGNAL(serverToClient(QVariant)), rev, SLOT(processServerCmd(QVariant)));

        // clientToServer的返回值并不直接是我们指定的类型,而是返回一个类似操作器的东西,通过这个东西我们再取得返回值。
        QRemoteObjectPendingCall val;
        // 执行槽函数
        QMetaObject::invokeMethod(rep, "clientToServer", Qt::DirectConnection,
                                  Q_RETURN_ARG(QRemoteObjectPendingCall, val),
                                  Q_ARG(QVariant, "sdfdsfdf"));
        val.waitForFinished();
        qDebug() << "return:" << val.returnValue();
    });
    
    return a.exec();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值