qdbus

6 篇文章 0 订阅


qdbus是对dbus的进一步封装,dbus是基于c实现的,在这里不做过多介绍,一些基本的概念可以参考以下链接

IPC之十一:使用D-Bus实现客户端向服务端请求服务的实例
QtDBus快速入门

## 一些简单的使用(重要)

qdbus 服务(连接) 对象 DBus接口GetAll方法 接口名字
参数的具体值可以通过qdbusviewer查看

qdbus --literal org.freedesktop.FileManager1 /org/freedesktop/FileManager1 org.freedesktop.DBus.Properties.GetAll org.freedesktop.FileManager1 

在这里插入图片描述

qdbus --literal org.freedesktop.FileManager1 /org/freedesktop/FileManager1 org.freedesktop.DBus.Properties.Get org.freedesktop.FileManager1 OpenLocations

在这里插入图片描述

查看org.freedesktop.FileManager1服务下 /org/freedesktop/FileManager1对象的所有method和signal

qdbus org.freedesktop.FileManager1 /org/freedesktop/FileManager1

在这里插入图片描述

QtDBus编程

1、创建服务并创建对象

test.h

#ifndef TEST_H
#define TEST_H
#include <QObject>

class test: public QObject
{
    Q_OBJECT
    //定义Interface名称为com.scorpio.test.value
    Q_CLASSINFO("D-Bus Interface", "com.scorpio.test.value")
public:
    test(int value);

public slots:
    int maxValue();
    int minValue();
    int value();
private:
    int m_value;
};

#endif // TEST_H

test.cpp

#include "test.h"

test::test(int value)
{
    m_value = value;
}

int test::maxValue()
{
    return 100;
}
int test::minValue()
{
    return 0;
}
int test::value()
{
    return m_value;
}

main.cpp

#include <QCoreApplication>
#include <QDBusConnection>
#include <QDebug>
#include <QDBusError>
#include "test.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    //建立到session bus的连接
    QDBusConnection connection = QDBusConnection::sessionBus();
    //在session bus上注册名为com.scorpio.test的服务
    if(!connection.registerService("com.scorpio.test"))
    {
        qDebug() << "error:" << connection.lastError().message();
        exit(-1);
    }
    test object(60);
    //注册名为/test/objects的对象,把类Object所有槽函数导出为object的method
    connection.registerObject("/test/objects", &object,QDBusConnection::ExportAllSlots);

    return a.exec();
}

可以看到服务已经注册,其实我们说的服务,也叫做总线上的一个连接
在这里插入图片描述

2、通过QDBusMessage访问Service

确保com.scorpio.test服务运行在总线上。
编写一个控制台程序,使用消息访问com.scorpio.test服务。

#include <QCoreApplication>
#include <QDBusMessage>
#include <QDBusConnection>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    //构造一个method_call消息,服务名称为:com.scorpio.test,对象路径为:/test/objects
    //接口名称为com.scorpio.test.value,method名称为value
    QDBusMessage message = QDBusMessage::createMethodCall("com.scorpio.test",
                           "/test/objects",
                           "com.scorpio.test.value",
                           "value");
    //发送消息
    QDBusMessage response = QDBusConnection::sessionBus().call(message);
    //判断method是否被正确返回
    if (response.type() == QDBusMessage::ReplyMessage)
    {
        //从返回参数获取返回值
        int value = response.arguments().takeFirst().toInt();
        qDebug() << QString("value =  %1").arg(value);
    }
    else
    {
        qDebug() << "value method called failed!";
    }

    return a.exec();
}

3、通过QDBusInterface 访问Service

编写一个控制台程序,使用接口访问com.scorpio.test服务。

#include <QCoreApplication>
#include <QDBusMessage>
#include <QDBusConnection>
#include <QDBusReply>
#include <QDBusInterface>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    // 创建QDBusInterface接口
    QDBusInterface interface("com.scorpio.test", "/test/objects",
                             "com.scorpio.test.value",
                             QDBusConnection::sessionBus());
    if (!interface.isValid())
    {
        qDebug() << qPrintable(QDBusConnection::sessionBus().lastError().message());
        exit(1);
    }
    //调用远程的value方法
    QDBusReply<int> reply = interface.call("value");
    if (reply.isValid())
    {
        int value = reply.value();
        qDebug() << QString("value =  %1").arg(value);
    }
    else
    {
        qDebug() << "value method called failed!";
    }

    return a.exec();
}

4、从D-Bus XML自动生成Proxy类

Proxy Object提供了一种更加直观的方式来访问Service,如同调用本地对象的方法一样。
生成Proxy类的流程如下:

A、使用工具qdbuscpp2xml从object.h生成XML文件;

qdbuscpp2xml -M test.h -o com.scorpio.test.xml

B、使用工具qdbusxml2cpp从XML文件生成继承自QDBusInterface的类

qdbusxml2cpp com.scorpio.test.xml -p valueInterface

生成两个文件:valueInterface.cpp和valueInterface.h

调用Proxy类访问Service如下:

#include <QCoreApplication>
#include <QDBusMessage>
#include <QDBusConnection>
#include <QDBusReply>
#include <QDBusInterface>
#include <QDebug>
#include "valueInterface.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    // 初始化自动生成的Proxy类com::scorpio::test::value
    com::scorpio::test::value test("com.scorpio.test",
                                   "/test/objects",
                                   QDBusConnection::sessionBus());
    // 调用value方法
    QDBusPendingReply<int> reply = test.value();
    //qdbusxml2cpp生成的Proxy类是采用异步的方式来传递Message,
    //所以需要调用waitForFinished来等到Message执行完成
    reply.waitForFinished();
    if (reply.isValid())
    {
        int value = reply.value();
        qDebug() << QString("value =  %1").arg(value);
    }
    else
    {
        qDebug() << "value method called failed!";
    }

    return a.exec();
}

5、使用Adapter注册Object

可以直接把test类注册为消息总线上的一个Object,但QT4不推荐。QT4推荐使用Adapter来注册Object。
大多数情况下,可能只需要把自定义的类里的方法有选择的发布到消息总线上,使用Adapter可以很方便的实现选择性发布。
生成Adapter类的流程如下:
A、使用工具 qdbuscpp2xml从test.h生成XML文件

qdbuscpp2xml -M test.h -o com.scorpio.test.xml 

B、编辑com.scorpio.test.xml,选择需要发布的method,不需要发布的删除。
C、使用工具qdbusxml2cpp从XML文件生成继承自QDBusInterface的类

qdbusxml2cpp com.scorpio.test.xml -i test.h -a valueAdaptor

生成两个文件:valueAdaptor.cpp和valueAdaptor.h

调用Adaptor类注册Object对象如下:

#include <QCoreApplication>
#include <QDBusConnection>
#include <QDebug>
#include <QDBusError>
#include "test.h"
#include "valueAdaptor.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QDBusConnection connection = QDBusConnection::sessionBus();
    test object(60);
    //ValueAdaptor是qdbusxml2cpp生成的Adaptor类
    ValueAdaptor valueAdaptor(&object);
    if (!connection.registerService("com.scorpio.test"))
    {
        qDebug() << connection.lastError().message();
        exit(1);
    }
    connection.registerObject("/test/objects", &object);
    return a.exec();
}

6、自动启动Service

D-Bus系统提供了一种机制可以在访问某个service时,自动把应用程序运行起来。
需要在/usr/share/dbus-1/services下面建立com.scorpio.test.service文件,文件的内容如下:

[D-BUS Service]
Name=com.scorpio.test
Exec=/path/to/scorpio/test

在访问test的method前,不必手动运行应用程序。

6我没有试过,还不清楚是怎么个情况。

signal

signal其实也是类似的道理

客户端
client.h

#ifndef CLIENT_H
#define CLIENT_H
#include <QtCore/QObject>
#include <QTextStream>
#include <QCoreApplication>
#include <QtDBus>
#include <QDebug>
#include <thread>

class Client: public QObject
{
    Q_OBJECT
public:
    Client();
    void client_listen();
public slots:
    void client_get(void);
signals:
    void send_to_service(QString st);
};

#endif // CLIENT_H

client.cpp

#include "client.h"

Client::Client()
{
    QDBusConnection::sessionBus().connect(QString(), QString("/citos/path"), "com.citos.test", "send_to_client", this, SLOT(client_get(void)));
}

void Client::client_get()
{
    qDebug() << "Client get!";
}

void Client::client_listen()
{
    // while(true)
    // {
        // getchar();
        qDebug() << "Message send!";
        QDBusMessage message =QDBusMessage::createSignal("/citos/path", "com.citos.test", "send_to_service");
        message << QString("Hello world!");
        QDBusConnection::sessionBus().send(message);
    // }
}



main.cpp

#include "mainwindow.h"
#include "client.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Client client;
    client.client_listen();
    return a.exec();
}

服务端
server.h

#ifndef SERVICE_H
#define SERVICE_H
#include <QtCore/QObject>
#include <QTextStream>
#include <QCoreApplication>
#include <QtDBus>
#include <QDebug>
#include <thread>
class Service: public QObject
{
    Q_OBJECT
    Q_CLASSINFO("D-Bus Interface", "com.citos.test")
public:
    Service();
    void service_listen();
public slots:
    void service_get(QString st);

signals:
    void send_to_client(void);
};

#endif // SERVICE_H

server.cpp

#include "service.h"

Service::Service()
{
    QDBusConnection::sessionBus().unregisterService("com.citos.service");
    QDBusConnection::sessionBus().registerService("com.citos.service");
    QDBusConnection::sessionBus().registerObject("/citos/path", this,QDBusConnection :: ExportAllSlots | QDBusConnection :: ExportAllSignals);
    QDBusConnection::sessionBus().connect(QString(), QString("/citos/path"), "com.citos.test", "send_to_service", this, SLOT(service_get(QString)));
}
void Service::service_get(QString st)
{
    qDebug() << "Message get from client: "<< st;
}

void Service::service_listen()
{
    // while(true)
    // {
    //     getchar();

    //     break;
    // }

    qDebug() << "Message send!";
    QDBusMessage message =QDBusMessage::createSignal("/citos/path", "com.citos.test", "send_to_client");
    QDBusConnection::sessionBus().send(message);
    // QThread::msleep(88000); // 让当前线程暂停5000毫秒(5秒)
}




main.cpp

#include "mainwindow.h"
#include "service.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Service service;

    service.service_listen();
    return a.exec();
}

总线上看到相应的signal和method,信号还是按照上述格式写,这样在client创建信号,service才能正确接收到。
在这里插入图片描述
但是实际使用的时候,你会发现,connect对应的object、interface、signal,哪怕不是默认的,且不存在,也是能接收到的。最好还是用对应的写明的object、interface和signal,这样方便管理。。。

cmakelist需要加一些依赖这里就省略了,可以发现,client只有一个send_to_service的信号,但是本身会绑定send_to_client这个信号,并有一些响应的处理函数。server正好相反

关于signal循环问题

在上一小节中,如果你用这样写client和server

void Client::client_listen()
{
    while(true)
    {
        // getchar();
        qDebug() << "I am client!";
        QDBusMessage message =QDBusMessage::createSignal("/path", "com.citos.test", "send_to_service");
        message << QString("Hello world!");
        QDBusConnection::sessionBus().send(message);
        QThread::msleep(1000);
        // QCoreApplication::processEvents(); // 处理事件循环
    }
}

void Service::service_listen()
{
    while(true)
    {
    qDebug() << "I am server";
    QDBusMessage message =QDBusMessage::createSignal("/citos/path", "com.citos.test", "send_to_client");
    message << QString("hello client");
    QDBusConnection::sessionBus().send(message);
    QThread::msleep(5000); // 让当前线程暂停5000毫秒(5秒)
    // QCoreApplication::processEvents(); // 处理事件循环
    }
}

你会发现相互都会收不到消息,去总线上看,也是都正常的。原因在于两个程序可能都忙于发消息了,我们可以考虑使用QCoreApplication::processEvents()函数处理事件循环,以便在发送消息的同时处理接收到的消息。

还有一点值得注意的是,一般注册服务和注册对象会结合一起用。都能完成method 或 signal的调用,但是注册服务会在总线上显示。

更好的解决方法应该是利用多线程,即设置两个子线程,一个发消息,一个读消息。

在这里读消息是默认触发的,就不再单独开线程了,只开发消息的线程,由于是while死循环,加个sleep方便观察,实际上,不加sleep还观察不到。。。

client

client.cpp修改如下
void Client::client_listen()
{
    while(true)
    {
        getchar();
        qDebug() << "Message send!";
        QDBusMessage message =QDBusMessage::createSignal("/citos/path", "com.citos.test", "send_to_service");
        message << QString("client say Hello service!");
        QDBusConnection::sessionBus().send(message);
        QThread::msleep(5000); // 让当前线程暂停5000毫秒(5秒)
    }
}

main修改如下
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    Client client;

    std::thread th1(&Client::client_listen, &client);

    th1.detach();

    return a.exec();
}

server

server.cpp修改如下
void Service::service_listen()
{
    while(true)
    {
        getchar();
        qDebug() << "Message send!";
        QDBusMessage message =QDBusMessage::createSignal("/citos/path", "com.citos.test", "send_to_client");
        QDBusConnection::sessionBus().send(message);
        QThread::msleep(100); // 让当前线程暂停5000毫秒(5秒)

    }
}

main修改如下
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Service service;

    std::thread th1(&Service::service_listen, &service);
    th1.detach();

    return a.exec();
}
  • 7
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值