Qt·DBus快速入门

目录

一、QtDBus简介

二、QtDBus类型系统

1、QtDBus类型系统简介

2、原生类型

3、复合类型

4、类型系统的使用

5、扩展类型系统

三、QtDBus常用类

1、QDBusMessage

2、QDBusConnection

3、QDBusInterface

4、QDBusReply

5、QDBusAbstractAdaptor

6、QDBusAbstractInterface

7、QDBusArgument

8、QDBusConnectionInterface

9、QDBusVariant

四、QtDBus工具

1、qdbusviewer

2、qdbuscpp2xml

3、qdbusxml2cpp

五、QtDBus编程

1、创建服务并注册对象

2、通过QDBusMessage访问Service

3、通过QDBusInterface 访问Service

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

5、使用Adapter注册Object

6、自动启动Service


一、QtDBus简介

QtDBus是一个使用D-Bus协议进行进程间通信的仅在Unix运行的库,是对D-Bus底层API的封装实现。
QtDBus模块提供了使用Qt信号槽机制扩展的接口。要使用QtDBus模块,需要在头文件中加入以下代码:

#include <QtDBus>

如果使用qmake构建程序,需要在工程文件中增加下列代码来链接QtDBus库。

QT += qdbus

本文的编译环境为:银河麒麟V10服务器——Kylin Linux Advanced Server release V10 (Tercel),Kernel: 4.19.90-20.2.ky10.x86_64,Build版本:Kylin Linux Advanced Server release V10 (SP1) /(Tercel)-x86_64-Build08/20200902。开发环境:Qt 5.11.1,编译C/C++使用GCC(x86 64bit)。
在该环境下尝试在pro文件中添加以上qdbus语句会提示找不到模块qdbus,改用QT += dbus后编译通过。
以下是系统中dbus相关包以及版本:

[root@localhost qtcreator]# rpm -qa|grep dbus
dbus-tools-1.12.16-13.ky10.x86_64
python3-pydbus-0.6.0-10.ky10.noarch
dbus-glib-0.110-5.ky10.x86_64
python3-dbus-1.2.8-8.ky10.x86_64
dbus-1.12.16-13.ky10.x86_64
lvm2-dbusd-2.02.181-8.ky10.x86_64
dbus-x11-1.12.16-13.ky10.x86_64
qt5-qdbusviewer-5.11.1-4.p01.ky10.x86_64
python2-dbus-1.2.8-8.ky10.x86_64
dbus-libs-1.12.16-13.ky10.x86_64
dbus-common-1.12.16-13.ky10.noarch
dbus-daemon-1.12.16-13.ky10.x86_64

二、QtDBus类型系统

1、QtDBus类型系统简介

D-Bus提供了一种基于“几种原生类型 与 在数组和结构中的原生类型组成的复合类型"的扩展类型系统。QtDBus模块通过QDBusArgument类实现了该类型系统,允许用户通过总线发送和接收每一种C++类型。

2、原生类型

QtDBus通过QDBusArgument支持原生类型,不需要特殊的定制。

Qt类型D-Bus类型
ucharBYTE
boolBOOLEAN
shortINT16
ushortUINT16
intINT32
uintUINT32
qlonglongINT64
qulonglongUINT64
doubleDOUBLE
QStringSTRING
QDBusVariantVARIANT
QDBusObjectPathOBJECT_PATH
QDBusSignatureSIGNATURE

除了原生类型,QDBusArgument也支持在Qt应用中广泛使用的两种非原生类型,QStringList和QByteArray。

3、复合类型

D-Bus指定由原生类型聚合而成的三种复合类型:ARRAY、STRUCT和 maps/dictionaries。ARRAY是零个或多个相同元素的集合,STRUCT是由不同类型的固定数量的元素组成的集合,Maps or dictionaries是元素对的数组,一个map中可以有零个或多个元素。

4、类型系统的使用

为了在QtDBus模块使用自定义类型,自定义类型必须使用Q_DECLARE_METATYPE( )声明为Qt元类型,使用qDBusRegisterMetaType( )函数注册。流操作符会被注册系统自动找到。
QtDBus模块为Qt容器类使用数组和map提供了模板特化,例如QMap和QList,不必实现流操作符函数。对于其它的类型,流操作符必须显示实现。

5、扩展类型系统

QtDBus定义的所有类型能用于通过总线发送和接收消息。不能使用上述类型之外的任何类型,包括typedefs定义的列表类型,如

QList<QVariant> 和 QMap<QString,QVariant>

三、QtDBus常用类

1、QDBusMessage

QDBusMessage类表示D-Bus总线发送或接收的一个消息。
QDBusMessage对象代表总线上四种消息类型中的一种,四种消息类型如下:
A、Method calls
B、Method return values
C、Signal emissions
D、Error codes
可以使用静态函数createError()、createMethodCall()、createSignal()创建消息。使用QDBusConnection::send() 函数发送消息。

2、QDBusConnection

QDBusConnection代表到D-Bus总线的一个连接,是一个D-Bus会话的起始点。通过QDBusConnection连接对象,可以访问远程对象、接口,连接远程信号到本地槽函数,注册对象等。
D-Bus连接通过connectToBus()函数创建,connectToBus()函数会创建一个到总线服务端的连接,完成初始化工作,并关联一个连接名到连接。
使用disconnectFromBus()函数会断开连接。一旦断开连接后,调用connectToBus()函数将不会重建连接,必须创建新的QDBusConnection实例。
作为两种最常用总线类型的辅助,sessionBus()和systemBus()函数分别创建到会话在总线和系统总线的连接并返回,会在初次使用时打开,在QCoreApplication析构函数调用时断开。
D-Bus支持点对点通信,不必使用总线服务。两个应用程序可以直接交流和交换消息。可以通过传递一个地址到connectToBus()函数实现。

以下是常用方法的简介:

  • QDBusConnection connectToBus(BusType type, const QString & name)
    打开一个type类型的连接,并关联name连接名,返回关联本连接的QDBusConnection对象。
  • QDBusConnection connectToBus(const QString & address, const QString & name)
    打开一个地址为address的私有总线,并关联name连接名,返回关联本连接的QDBusConnection对象。
  • QDBusConnection connectToPeer(const QString & address, const QString & name)
    打开一个点对点的连接到address地址,并关联name连接名,返回关联本连接的QDBusConnection对象。
  • void disconnectFromBus(const QString & name)
    关闭名为name的总线连接
  • void disconnectFromPeer(const QString & name)
    关闭名为name的对等连接
  • QByteArray localMachineId()
    返回一个D-Bus总线系统知道的本机ID
  • QDBusConnection sender()
    返回发送信号的连接
  • QDBusConnection sessionBus()
    返回一个打开到session总线的QDBusConnection对象
  • QDBusConnection systemBus()
    返回一个打开到system总线的QDBusConnection对象
  • QDBusPendingCall asyncCall(const QDBusMessage & message, int timeout = -1)const
    发送message消息到连接,并立即返回。本函数只支持method调用。返回一个用于追踪应答的QDBusPendingCall对象。
  • QDBusMessage call(const QDBusMessage & message, QDBus::CallMode mode = QDBus::Block, int timeout = -1 ) const
    通过本连接发送消息message,并且阻塞,等待应答。
  • bool registerObject(const QString & path, QObject * object, RegisterOptions options = ExportAdaptors)
    注册object对象到路径path,options选项指定由多少对象会被暴露到D-Bus总线,如果注册成功,返回true。
  • bool registerService(const QString & serviceName)
    试图在D-Bus总线上注册serviceName服务,如果注册成功,返回true;如果名字已经在其它应用被注册,则注册失败。

3、QDBusInterface

QDBusInterface是远程对象接口的代理。
QDBusInterface是一种通用的访问器类,用于调用远程对象,连接到远程对象导出的信号,获取/设置远程属性的值。当没有生成表示远程接口的生成代码时时,QDBusInterface类对远程对象的动态访问非常有用。
调用通常是通过使用call()函数来实现,call函数构造消息,通过总线发送消息,等待应答并解码应答。信号使用QObject::connect()函数进行连接。最终,使用QObject::property()和QObject::setProperty()函数对属性进行访问。

4、QDBusReply

QDBusReply类用于存储对远程对象的方法调用的应答。
一个QDBusReply对象是方法调用的应答QDBusMessage对象的一个子集。QDBusReply对象只包含第一个输出参数或错误代码,并由QDBusInterface派生类使用,以允许将错误代码返回为函数的返回参数。

QDBusReply<QString> reply = interface->call("RemoteMethod");
 if (reply.isValid())
     // use the returned value
     useValue(reply.value());
 else
     // call failed. Show an error condition.
     showError(reply.error());

对于没有输出参数或返回值的远程调用,使用isValid()函数测试应答是否成功。

5、QDBusAbstractAdaptor

QDBusAbstractAdaptor类使用D-Bus Adaptor基类。
QDBusAbstractAdaptor类是用于使用D-Bus向外部提供接口的所有对象的起点。可以通过将一个或多个派生自QDBusAbstractAdaptor的类附加到一个普通QObject对象上,使用QDBusConnection::registerObject注册QObject对象可以实现。QDBusAbstractAdaptor是一个轻量级封装,主要用于中继调用实际对象及其信号。
每个QDBusAbstractAdaptor派生类都应该使用类定义中的Q_CLASSINFO宏来定义D-Bus接口。注意,这种方式只有一个接口可以暴露。
QDBusAbstractAdaptor使用了信号、槽、属性的标准QObject机制来决定哪些信号、槽、属性被暴露到总线。任何QDBusAbstractAdaptor派生类发送的信号通过任何D-Bus连接自动中继到注册的对象上。
QDBusAbstractAdaptor派生类对象必须使用new创建在堆上,不必由用户删除。

6、QDBusAbstractInterface

QDBusAbstractInterface是QtDBus模块中允许访问远程接口的所有D-Bus接口的基类。
自动生成的代码类也继承自QDBusAbstractInterface,此描述的所有方法在生成的代码中也有效。除了此处的描述,生成代码类为远程方法提供了成员函数,允许在编译时检查正确参数和返回值,以及匹配的属性类型和匹配的信号参数。

QDBusPendingCall asyncCall(const QString & method, 
                           const QVariant & arg1 = QVariant(), 
                           const QVariant & arg2 = QVariant(), 
                           const QVariant & arg3 = QVariant(), 
                           const QVariant & arg4 = QVariant(),
                           const QVariant & arg5 = QVariant(), 
                           const QVariant & arg6 = QVariant(), 
                           const QVariant & arg7 = QVariant(), 
                           const QVariant & arg8 = QVariant())

调用本接口中的method方法,传递参数到远程的method。
要调用的参数会通过D-Bus输入参数传递到远程方法,返回的QDBusPendingCall对象用于定义应答信息。
本函数最多有8个参数,如果参数多于8个,或是传递可变数量的参数,使用asyncCallWithArgumentList()函数。

QString value = retrieveValue();
QDBusPendingCall pcall = interface->asyncCall(QLatin1String("Process"), value);

QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this);

QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
                 this, SLOT(callFinishedSlot(QDBusPendingCallWatcher*)));

7、QDBusArgument

QDBusArgument类用于整理和分发D-Bus参数。QDBusArgument用于通过D-Bus发送参数到远程应用,并接收返回。
QDBusArgument是QtDBus类型系统的核心类,QtDBus类型系统用于解析和原生类型。复合类型可以通过在数组、词典或结构中使用一个或多个原生类型创建。
下列代码展示了使用QtDBus类型系统构造的包含一个整数和字符串的结构。

struct MyStructure
 {
     int count;
     QString name;
 };
 Q_DECLARE_METATYPE(MyStructure)
// Marshall the MyStructure data into a D-Bus argument
QDBusArgument &operator<<(QDBusArgument &argument, const MyStructure &mystruct)
{
    argument.beginStructure();
    argument << mystruct.count << mystruct.name;
    argument.endStructure();
    return argument;
}

// Retrieve the MyStructure data from the D-Bus argument
const QDBusArgument &operator>>(const QDBusArgument &argument, MyStructure &mystruct)
{
    argument.beginStructure();
    argument >> mystruct.count >> mystruct.name;
    argument.endStructure();
    return argument;
}

在QDBusArgument使用这个结构前,必须使用qDBusRegisterMetaType()函数进行注册。因此,在程序中应该则增加如下代码:
qDBusRegisterMetaType<MyStructure>();
一旦注册,类型可以在呼出方法调用QDBusAbstractInterface::call()、来自注册对象的信号发射或来自远程应用的传入调用。

8、QDBusConnectionInterface

QDBusConnectionInterface类提供了对D-Bus总线服务的访问。
D-Bus总线服务端中提供了一个特殊的接口org.freedesktop.DBus,允许客户端运行访问总线的某些属性,例如当前连接的客户端列表,QDBusConnectionInterface类提供对org.freedesktop.DBus接口的访问。
本类中最常用的是使用registerService()和unregisterService()在总线上注册和注销服务名。
QDBusConnectionInterface类定义四个信号,在总线上有服务状态变化时发送。

void callWithCallbackFailed(const QDBusError & error, const QDBusMessage & call)
void serviceOwnerChanged(const QString & name, const QString & oldOwner, const QString & newOwner)
void serviceRegistered(const QString & serviceName)
void serviceUnregistered(const QString & serviceName)

9、QDBusVariant

QDBusVariant类使程序员能够识别由D-Bus类型系统提供的Variant类型。一个使用整数、D-Bus变体类型和字符串作为参数的D-Bus函数可以使用如下的参数列表调用。

QList<QVariant> arguments;
arguments << QVariant(42) << QVariant::fromValue(QDBusVariant(43)) << QVariant("hello");
myDBusMessage.setArguments(arguments);

当D-Bus函数返回一个D-Bus变体类型时,可以使用如下方法获取:

// call a D-Bus function that returns a D-Bus variant
QVariant v = callMyDBusFunction();
// retrieve the D-Bus variant
QDBusVariant dbusVariant = qvariant_cast<QDBusVariant>(v);
// retrieve the actual value stored in the D-Bus variant
QVariant result = dbusVariant.variant();

QDBusVariant中的QVariant需要区分一个正常的D-Bus值和一个QDBusVariant中的值。

四、QtDBus工具

1、qdbusviewer

qdbusviewer用于查看D-Bus总线上的服务、对象、接口以及接口的method。使用方法直接在命令行执行:qdbusviewer

2、qdbuscpp2xml

qdbuscpp2xml会解析QObject派生类的C++头文件或是源文件,生成D-Bus的内省xml文件。qdbuscpp2xml 会区分函数的输入输出,如果参数声明为const则会是输入,否则可能会被当作输出。
qdbuscpp2xml使用语法如下:
qdbuscpp2xml [options...] [files...]
Options参数如下:
-p|-s|-m:只解析脚本化的属性、信号、方法(槽函数)
-P|-S|-M:解析所有的属性、信号、方法(槽函数)
-a:输出所有的脚本化内容,等价于-psm
-A:输出所有的内容,等价于-PSM
-o filename:输出内容到filename文件
解析所有的方法输出到spark.test.xml文件命令如下:
qdbuscpp2xml -M test.h -o spark.test.xml

<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
  <interface name="com.scorpio.test.value">
    <method name="maxValue">
      <arg type="i" direction="out"/>
    </method>
    <method name="minValue">
      <arg type="i" direction="out"/>
    </method>
    <method name="value">
      <arg type="i" direction="out"/>
    </method>
    <method name="setValue">
      <arg name="value" type="i" direction="in"/>
    </method>
  </interface>
</node>

3、qdbusxml2cpp

qdbusxml2cpp根据输入文件中定义的接口,生成C++实现代码。
qdbusxml2cpp可以辅助自动生成继承于QDBusAbstractAdaptor和QDBusAbstractInterface两个类的实现代码,用于进程通信服务端和客户端,简化了开发者的代码设计。
qdbusxml2cpp使用语法如下:
qdbusxml2cpp [options...] [xml-or-xml-file] [interfaces...]
Options参数如下:
-a filename:输出Adaptor代码到filename
-c classname:使用classname作为生成类的类名
-i filename:增加#include到输出
-l classname:当生成Adaptor代码时,使用classname作为父类
-m:在cpp文件中包含 #include "filename.moc"语句
-N:不使用名称空间
-p filename:生成Proxy代码到filename文件
解析spark.test.xml文件,生成Adaptor类ValueAdaptor,文件名称为valueAdaptor.h、valueAdaptor.cpp命令行如下:
qdbusxml2cpp spark.test.xml -i test.h -a valueAdaptor
解析spark.test.xml文件,生成Proxy类ComScorpioTestValueInterface,文件名称为testInterface.h、testInterface.cpp命令行如下:
qdbusxml2cpp spark.test.xml -p testInterface

五、QtDBus编程

分为两个部分,DBus服务和访问DBus服务。

1、创建服务并注册对象

创建DBus服务的大致流程:


test.h文件:

#ifndef TEST_H
#define TEST_H
#include <QObject>
#include <QtDBus>

class test: public QObject
{
    Q_OBJECT
// 这里定义的是DBus连接的接口,client需要这个"spark.test.interface"接口对DBus服务进行访问。
    Q_CLASSINFO("D-Bus Interface", "spark.test.interface")
public:
    test(int value);

public slots:
    void set_value(int value);
    int get_value();
private:
    int m_value;
};

#endif // TEST_H

test.cpp文件:

#include "test.h"

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

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

int test::get_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上注册名为spark.test的服务
    // spark.test是服务的名字,注意和test.h中的接口名字spark.test.interface区别
    if(!connection.registerService("spark.test"))
    {
        qDebug() << "error:" << connection.lastError().message();
        exit(-1);
    }
    test test_object(60);
    // 注册名为/test/test_objects的对象,把类Object所有槽函数导出为object的method
    connection.registerObject("/test/test_objects", &test_object, QDBusConnection::ExportAllSlots);
    
    return a.exec();
}

启动程序后,在命令行打开qdbusviewer,查看session bus。


双击Method方法会调用该方法。这里接口类设置为只设置了两个方法,set_value和get_value,可以通过双击调用,设置和获取test类中的m_value属性值。

2、通过QDBusMessage访问Service

本节使用QDBusMessage来访问服务,调用服务的Method方法。

确保spark.test服务运行在总线上,可用qdbusviewer查看。(注释:可以在DBus服务的工程目录下make一个服务的可执行文件,用终端去执行,就可以随时取消掉。分两个步骤:1.qmake-qt5生成Makefile:qmake-qt5 -o Makefile ***.pro;2.直接make即可生成可执行文件。)
现在,继续创建一个Qt Widgets Application工程,使用消息访问spark.test服务。
其中main.cpp文件如下:

#include "mainwindow.h"
#include <QtGui>
#include <QApplication>
#include <QDesktopWidget>
#include <QCoreApplication>
#include <QDBusMessage>
#include <QDBusConnection>
#include <QMessageBox>
#include <iostream>
#include <unistd.h>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    MainWindow w;
    w.show();
    // 让MainWindow窗口显示在屏幕中央
    w.move((QApplication::desktop()->width() - w.width())/2, (QApplication::desktop()->height() - w.height())/2);
    
    // 构造一个method_call消息,服务名称为:spark.test,对象路径为:/test/test_objects
    // 接口名称为spark.test.interface,method名称为get_value。注意这里信息不能填错,如果接口名或Method名不正确就无法正常通信和调用。
    QDBusMessage message = QDBusMessage::createMethodCall("spark.test",
                           "/test/test_objects",
                           "spark.test.interface",
                           "get_value");
    //发送消息
    QDBusMessage response = QDBusConnection::sessionBus().call(message);
    //判断method是否被正确返回
    if (response.type() == QDBusMessage::ReplyMessage)
    {
        //从返回参数获取返回值
    	int value = response.arguments().takeFirst().toInt();
        QMessageBox::warning(NULL, "message", QObject::tr("value = %1").arg(value));
    }
    else
    {
    	QMessageBox::warning(NULL, "error", QObject::tr("value method called failed!"));
    }

    return a.exec();
}

运行结果:

3、通过QDBusInterface 访问Service

继续创建一个Qt Widgets Application工程,使用接口访问spark.test服务。

#include "mainwindow.h"
#include <QtGui>
#include <QApplication>
#include <QDesktopWidget>
#include <QCoreApplication>
#include <QDBusMessage>
#include <QDBusConnection>
#include <QDBusInterface>
#include <QMessageBox>
#include <QDBusReply>
#include <iostream>
#include <unistd.h>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    // 创建QDBusInterface接口
    QDBusInterface interface("spark.test", 									             "/test/test_objects",
                             "spark.test.interface",
                             QDBusConnection::sessionBus());
    if (!interface.isValid())
    {
        QString mes = QDBusConnection::sessionBus().lastError().message();
        QMessageBox::warning(NULL, "message", QObject::tr("lastError = %1").arg(mes));
        exit(1);
    }
    
    // 调用远程的get_value方法。
    // Todo: set_value带参数方法其中参数不能使用引用,毕竟是两个进程之间传参
    QDBusReply<int> reply = interface.call("get_value");
    if (reply.isValid())
    {
        int value = reply.value();
        QMessageBox::warning(NULL, "message", QObject::tr("value = %1").arg(value));
    }
    else
    {
        QMessageBox::warning(NULL, "message", QObject::tr("value method called failed!"));
    }

    return a.exec();
}

代码运行结果同上一小节。

结合上面三种情况的综合案例:

服务端:

#include <QObject>
#include <QDBusConnection>
#include <QDBusError>
#include <QDebug>
 
class CTestDbus: public QObject
{
    Q_OBJECT
    //定义Interface名称为com.Interface.CTestDbus
    Q_CLASSINFO("D-Bus Interface", "com.Interface.CTestDbus")
public:
    explicit CTestDbus(QObject* parent = nullptr):QObject(parent){}
 
public slots:
    QString testString(QString name)
    {
        QString str = "testString : " + name;
        qDebug() <<"Server: " << str;
        emit testSignal(9);
        return str;
    }
 
signals:
    void testSignal(int);
};
 
 
    //建立到session bus的连接
    QDBusConnection connection = QDBusConnection::sessionBus();
    //在session bus上注册名为com.Server.Server1的服务
    if(!connection.registerService("com.Server.Server1"))
    {
        qDebug() << "error:" << connection.lastError().message();
        return;
    }
    CTestDbus *obj = new CTestDbus;
    //注册名为/com/ObjectPath/CTestDbus的对象,把类Object所有槽函数和信号导出为object的method
    if (!connection.registerObject("/com/ObjectPath/CTestDbus", obj,
          QDBusConnection::ExportAllSlots|QDBusConnection::ExportAllSignals))
    {
        qDebug() << "error:" << connection.lastError().message();
        return;
    }

客户端:

#include <QMainWindow>
#include <QMainWindow>
#include <QDBusConnection>
#include <QDBusError>
#include <QDBusPendingCallWatcher>
#include <QDBusInterface>
#include <QDBusPendingReply>
#include <QDBusReply>
#include <QDebug>
 
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = nullptr);
 
public:
    void dbusclient();
 
signals:
 
public slots:
    void testSlot(int value);
    void callFinishedSlot(QDBusPendingCallWatcher *call);
};
 
 
 
void MainWindow::dbusclient()
{
    //
#ifdef METHOD1 //远程服务调用方法一
    //构造一个method_call消息,服务名称为:com.Server.Server1,对象路径为:/com/ObjectPath/CTestDbus
    //接口名称为com.Interface.CTestDbus,method名称为testString
    QDBusMessage message = QDBusMessage::createMethodCall("com.Server.Server1",
                                                          "/com/ObjectPath/CTestDbus",
                                                          "com.Interface.CTestDbus",
                                                          "testString");
 
    // 传递参数
    message << QString("lalala");
 
    //发送消息
    QDBusMessage response = QDBusConnection::sessionBus().call(message);
    //判断method是否被正确返回
    if (response.type() == QDBusMessage::ReplyMessage)
    {
        //从返回参数获取返回值
        QString value = response.arguments().takeFirst().toString();
        qDebug() << QString("Client get return value =  %1").arg(value);
    }
    else
    {
        qDebug() << "value method called failed!";
    }
 
    //
#elif METHOD2  //远程服务调用方法二
    // 创建QDBusInterface接口
    QDBusInterface interface("com.Server.Server1", "/com/ObjectPath/CTestDbus",
                                                   "com.Interface.CTestDbus",
                             QDBusConnection::sessionBus());
    if (!interface.isValid())
    {
        qDebug() << qPrintable(QDBusConnection::sessionBus().lastError().message());
        exit(1);
    }
    //调用远程的testString方法,第一个参数为lalala2222
    //QDBusReply<QString>返回值类型和setName返回值类型保持一致
    //call是同步调用,远程方法返回后才继续往下执行。
    QDBusReply<QString> reply = interface.call("testString", "lalala2222"); //阻塞,直到远程方法调用完成。
    if (reply.isValid())
    {
        QString value = reply.value();
        qDebug() << QString("debus value =  %1").arg(value);
    }
    else
    {
        qDebug() << "value method called failed!";
    }
 
    //
#else// 远程服务调用方法三,异步调用
    // 异步调用
    // 创建QDBusInterface接口
    QDBusInterface interface("com.Server.Server1", "/com/ObjectPath/CTestDbus",
                                                   "com.Interface.CTestDbus",
                             QDBusConnection::sessionBus());
    if (!interface.isValid())
    {
        qDebug() << qPrintable(QDBusConnection::sessionBus().lastError().message());
        exit(1);
    }
 
    {
        // 方法一:接收服务端的信号,连接槽函数。服务器对象必须注册QDBusConnection::ExportAllSignals
        //   if (!QDBusConnection::sessionBus().connect("com.Server.Server1",
        //                                             "/com/ObjectPath/CTestDbus",
        //                                             "com.Interface.CTestDbus",
        //                                             "testSignal", this,
        //                                             SLOT(testSlot(int))))
 
        //方法二: 接收服务端的信号,连接槽函数。服务器对象必须注册QDBusConnection::ExportAllSignals
        QDBusInterface *pinterface = new QDBusInterface ("com.Server.Server1",
                                                         "/com/ObjectPath/CTestDbus",
                                                         "com.Interface.CTestDbus",
                                                         QDBusConnection::sessionBus());
        QObject::connect(pinterface, SIGNAL(testSignal(int)), this, SLOT(testSlot(int)));
    }
 
    // 这里不阻塞,异步调用。
    QDBusPendingCall async = interface.asyncCall("testString", "lalala33333");
 
    // 等待结束,async.waitForFinished ()
    QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(async, this);
 
    QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
                     this, SLOT(callFinishedSlot(QDBusPendingCallWatcher*)));
#endif
}
 
void MainWindow::testSlot(int value)
{
    qDebug() << "testSlot = " << value;
}
 
void MainWindow::callFinishedSlot(QDBusPendingCallWatcher *call)
{
    QDBusPendingReply<QString> reply = *call;
    if (!reply.isError()) {
        QString name= reply.argumentAt<0>();
        qDebug()<<"QDBusPendingReply name = "<<name;
    }
    call->deleteLater();
}

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

Proxy Object提供了一种更加直观的方式来访问Service,如同调用本地对象的方法一样。
生成Proxy类的流程如下:
A、使用工具qdbuscpp2xml从object.h生成XML文件;
qdbuscpp2xml -M test.h -o spark.test.xml

<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
  <interface name="com.scorpio.test.value">
    <method name="maxValue">
      <arg type="i" direction="out"/>
    </method>
    <method name="minValue">
      <arg type="i" direction="out"/>
    </method>
    <method name="value">
      <arg type="i" direction="out"/>
    </method>
  </interface>
</node>

B、使用工具qdbusxml2cpp从XML文件生成继承自QDBusInterface的类
qdbusxml2cpp spark.test.xml -p valueInterface
生成两个文件:valueInterface.cpp和valueInterface.h
valueInterface.h文件:

/*
* This file was generated by qdbusxml2cpp version 0.7
* Command line was: qdbusxml2cpp com.scorpio.test.xml -p testInterface
*
* qdbusxml2cpp is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
*
* This is an auto-generated file.
* Do not edit! All changes made to it will be lost.
*/
#ifndef TESTINTERFACE_H_1526737677
#define TESTINTERFACE_H_1526737677
#include <QtCore/QObject>
#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
#include <QtDBus/QtDBus>
/*
* Proxy class for interface com.scorpio.test.value
*/
class ComScorpioTestValueInterface:
public QDBusAbstractInterface
{
Q_OBJECTpublic:
    static inline const char *staticInterfaceName()
    {
        return "com.scorpio.test.value";
    }
public:
    ComScorpioTestValueInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = 0);
    ~ComScorpioTestValueInterface();
public Q_SLOTS: // METHODS
    inline QDBusPendingReply<int> maxValue()
    {
        QList<QVariant> argumentList;
        return asyncCallWithArgumentList(QLatin1String("maxValue"), argumentList);
    }
    inline QDBusPendingReply<int> minValue()
    {
        QList<QVariant> argumentList;
        return asyncCallWithArgumentList(QLatin1String("minValue"), argumentList);
    }
    inline QDBusPendingReply<int> value()
    {
        QList<QVariant> argumentList;
        return asyncCallWithArgumentList(QLatin1String("value"), argumentList);
    }
Q_SIGNALS: // SIGNALS

};

namespace com
{
    namespace scorpio
    {
        namespace test
        {
            typedef ::ComScorpioTestValueInterface value;
        }
    }
}
#endif

valueInterface.cpp文件:

/*
 * This file was generated by qdbusxml2cpp version 0.7
 * Command line was: qdbusxml2cpp com.scorpio.test.xml -p testInterface
 *
 * qdbusxml2cpp is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
 *
 * This is an auto-generated file.
 * This file may have been hand-edited. Look for HAND-EDIT comments
 * before re-generating it.
 */

#include "testInterface.h"

/*
 * Implementation of interface class ComScorpioTestValueInterface
 */

ComScorpioTestValueInterface::ComScorpioTestValueInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent)
    : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent)
{
}

ComScorpioTestValueInterface::~ComScorpioTestValueInterface()
{
}

调用Proxy类访问Service如下:

#include <QCoreApplication>
#include <QDBusMessage>
#include <QDBusConnection>
#include <QDBusReply>
#include <QDBusInterface>
#include <QDebug>
#include "testInterface.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/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 spark.test.xml
B、编辑spark.test.xml,选择需要发布的method,不需要发布的删除。
C、使用工具qdbusxml2cpp从XML文件生成继承自QDBusInterface的类
qdbusxml2cpp spark.test.xml -i test.h -a valueAdaptor
生成两个文件:valueAdaptor.cpp和valueAdaptor.h
valueAdaptor.h文件:

/*
 * This file was generated by qdbusxml2cpp version 0.7
 * Command line was: qdbusxml2cpp com.scorpio.test.xml -i test.h -a valueAdaptor
 *
 * qdbusxml2cpp is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
 *
 * This is an auto-generated file.
 * This file may have been hand-edited. Look for HAND-EDIT comments
 * before re-generating it.
 */

#ifndef VALUEADAPTOR_H_1526742670
#define VALUEADAPTOR_H_1526742670

#include <QtCore/QObject>
#include <QtDBus/QtDBus>
#include "test.h"
class QByteArray;
template<class T> class QList;
template<class Key, class Value> class QMap;
class QString;
class QStringList;
class QVariant;

/*
 * Adaptor class for interface com.scorpio.test.value
 */
class ValueAdaptor: public QDBusAbstractAdaptor
{
    Q_OBJECT
    Q_CLASSINFO("D-Bus Interface", "com.scorpio.test.value")
    Q_CLASSINFO("D-Bus Introspection", ""
"  <interface name=\"com.scorpio.test.value\">\n"
"    <method name=\"maxValue\">\n"
"      <arg direction=\"out\" type=\"i\"/>\n"
"    </method>\n"
"    <method name=\"minValue\">\n"
"      <arg direction=\"out\" type=\"i\"/>\n"
"    </method>\n"
"  </interface>\n"
        "")
public:
    ValueAdaptor(QObject *parent);
    virtual ~ValueAdaptor();

public: // PROPERTIES
public Q_SLOTS: // METHODS
    int maxValue();
    int minValue();
Q_SIGNALS: // SIGNALS
};

#endif

valueAdaptor.cpp文件:

/*
 * This file was generated by qdbusxml2cpp version 0.7
 * Command line was: qdbusxml2cpp com.scorpio.test.xml -i test.h -a valueAdaptor
 *
 * qdbusxml2cpp is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
 *
 * This is an auto-generated file.
 * Do not edit! All changes made to it will be lost.
 */

#include "valueAdaptor.h"
#include <QtCore/QMetaObject>
#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QVariant>

/*
 * Implementation of adaptor class ValueAdaptor
 */

ValueAdaptor::ValueAdaptor(QObject *parent)
    : QDBusAbstractAdaptor(parent)
{
    // constructor
    setAutoRelaySignals(true);
}

ValueAdaptor::~ValueAdaptor()
{
    // destructor
}

int ValueAdaptor::maxValue()
{
    // handle method call com.scorpio.test.value.maxValue
    int out0;
    QMetaObject::invokeMethod(parent(), "maxValue", Q_RETURN_ARG(int, out0));
    return out0;
}

int ValueAdaptor::minValue()
{
    // handle method call com.scorpio.test.value.minValue
    int out0;
    QMetaObject::invokeMethod(parent(), "minValue", Q_RETURN_ARG(int, out0));
    return out0;
}

调用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/test_objects", &object);
    return a.exec();
}

使用qdbusviewer查看发布的method。

6、自动启动Service

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

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

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

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值