QT中使用DBUS

6 篇文章 0 订阅

D-Bus和QT

本文以一个实作为例,介绍D-Bus在QT下的绑定。在实作中,我们会在Session Bus上注册一个Hotel Service,通过这个Service,可以实现check in,check out以及query的动作。

为避免歧义,本文对D-Bus中的一些关键术语的表述依然采用英文。这些术语包括:D-Bus, IPC, Message, Message Bus, System Bus, Session Bus, Service, Object, Method, Signal, Interface, Unique Connection Name, Well-known Name等。

D-Bus概述

什么是D-Bus?

D-Bus是一种进程间通信的机制,它被设计成为一种低开销、低延迟的IPC,并被多种桌面环境(如KDE、GNOME等)所采用。

关于D-Bus的详细介绍可以参考freedesktop.org提供的两份文档, D-Bus Tutorial 和 D-Bus Specification 。

基本概念

D-Bus提供了多种Message Bus用于应用程序之间的通信。通常,Linux发行版都会提供两种Message Bus:System Bus和Session Bus。System Bus 主要用于内核和一些系统全局的service之间通信;Session Bus 主要用于桌面应用程序之间的通信。

D-Bus中用于通信的基本单元叫做Message,Message的具体格式可以参考   D-Bus Specification 。

当应用程序连接到M essage Bus上时,D-Bus会分配一个unique connection name,这个unique name通常的格式如":34-907"。Unique name以":"开头,后面的数字没有特别的意义,只是为了保证这个unique name的唯一性。

另外,应用程序还可以向Message Bus请求一个well-known name,格式如同一个反置的域名,例如"com.mycompany.myapp"。当一个应用程序连接到Message Bus上时,可以拥有两种名称:unique connection name和well-known name。这两种名称的关系可以理解为网络上的IP地址和域名的关系。

在D-Bus规范里,unique connection name和well-known name都叫做Bus Name。这点比较奇怪,也比较拗口,Bus Name并不是Message Bus的名称,而是应用程序和Message Bus之间的连接的名称。

应用程序和Message Bus之间的连接也被称为Service,这样一来,把Bus Name称作Service Name在概念上会更清晰一点。

当应用程序连接到Message Bus上时,该应用程序可以在Bus上创建一到多个Object(我们可以把D-Bus的object理解成面向对象语言里的object)。Service通过Object 为其他应用程序提供访问接口。因为在Message Bus上,一个应用程序可以对应多个Object,所以不同的Object必须由Object Path(类似于文件系统的路径)来区分。Object Path的格式如"/foo/bar"。

对于Service Name和Object Path,QT4文档中有一个类比还是比较直观的,如下图所示:

图中的ftp.example.com可以看作是Service Name,/pub/something可以看作是Object Path。

D-Bus通过Signal/Method来发送和接收Message。Signal/Method可以理解为QT4中的Signal/Slot这个概念。一个Object可以提供多个Method/Signal,这些Method/Signal的集合又组成了Interface。

因此,D-Bus的这些概念从大到小可以表示为:Message Bus->Service->Object->[Interface]->Method/Signal。

其中,Interface是可选的。

D-Bus 调试工具

常用的D-Bus调试工具有 D-Feet、qdbusviewer等。

在C onsole窗口中键入qdbusviewer命令可以打开QT4自带的qdbusviewer 。

如上图所示,我们可以通过qdbusviewer来调用Object在Message Bus上发布的所有Method。

D -Bus 的 QT 绑定

下面,我们通过一个实例来介绍D-Bus的QT绑定。(参见hotel.pro)

我们在Session bus上创建一个"com.test.hotel" service,通过这个service可以执行check in,check out和query三个动作。

创建Service并且注册Object

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

int main(int argc, char *argv[])

{

    QCoreApplication a(argc, argv);

    // 用于建立到session bus的连接

    QDBusConnection bus = QDBusConnection::sessionBus();

    // 在session bus上注册名为"com.test.hotel"的service

    if (!bus.registerService("com.test.hotel")) {

            qDebug() << bus.lastError().message();

            exit(1);

    }

    Hotel my_hotel;

    // 注册名为"/hotel/registry"的object。

    // "QDBusConnection::ExportAllSlots"表示把类Hotel的所有Slot都导出为这个Object的method

    bus.registerObject("/hotel/registry", &my_hotel,

                       QDBusConnection::ExportAllSlots);

    return a.exec();

}

我们再看一下Hotel类的定义。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

class Hotel : public QObject

{

    Q_OBJECT

    // 定义Interface名称为"com.test.hotel.registry"

    Q_CLASSINFO("D-Bus Interface", "com.test.hotel.registry")

public:

    Hotel() { m_rooms = MAX_ROOMS; }

public slots:

    // Check in,参数为房间数,返回成功拿到的房间数

    int checkIn(int num_room);

    // Check out,参数为房间数,返回成功退回的房间数

    int checkOut(int num_room);

    // Query,用于查询目前还剩下的房间数

    int query();

private:

    int m_rooms;

    QReadWriteLock m_lock;

};

运行这个程序,我们可以使用qdbusviewer查看和操作这个Object。

通过QDBusMessage访问Service

在QT中,用QDBusMessage表示在D-Bus上发送和接收的Message。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

// 用来构造一个在D-Bus上传递的Message

QDBusMessage m = QDBusMessage::createMethodCall("com.test.hotel",

                                              "/hotel/registry",

                                              "com.test.hotel.registry",

                                              "checkIn");

if (argc == 2) {

        // 给QDBusMessage增加一个参数;

        // 这是一种比较友好的写法,也可以用setArguments来实现

        m << QString(argv[1]).toInt();

}

// 发送Message

QDBusMessage response = QDBusConnection::sessionBus().call(m);

// 判断Method是否被正确返回

if (response.type() == QDBusMessage::ReplyMessage) {

        // QDBusMessage的arguments不仅可以用来存储发送的参数,也用来存储返回值;

        // 这里取得checkIn的返回值

        int num_room = response.arguments().takeFirst().toInt();

        printf("Got %d %s\n", num_room, (num_room > 1) ? "rooms" : "room");

} else {

        fprintf(stderr, "Check In fail!\n");

}

通过QDBusInterface 访问Service

在QT4中,QDBusInterface可以更加方便的访问Service。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

// 创建QDBusInterface

QDBusInterface iface( "com.test.hotel", "/hotel/registry",

                      "com.test.hotel.registry", QDBusConnection::sessionBus());

if (!iface.isValid()) {

        qDebug() << qPrintable(QDBusConnection::sessionBus().lastError().message());

        exit(1);

}

// 呼叫远程的checkIn,参数为num_room

QDBusReply<int> reply = iface.call("checkIn", num_room);

if (reply.isValid()) {

        num_room = reply.value();

        printf("Got %d %s\n", num_room, (num_room > 1) ? "rooms" : "room");

} else {

        fprintf(stderr, "Check In fail!\n");

}

看,用QDBusInterface来访问Service是不是更加方便?

从D-Bus XML自动生成Proxy类

用QDB usInterface访问Service已经非常方便了,但还不够直观。还有没有更直观的方法,就像访问本地类成员变量的方式访问远程的method?答案是Proxy。 

Proxy Object提供了一种更加直观的方式来访问Service,就好像调用本地对象的方法一样。 

概括的说,达成上述目标需要分三步走:

(1)使用工具qdbuscpp2xml从hotel.h生成XML文件;

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

(2)使用工具qdbusxml2cpp从XML文件生成继承自QDBusInterface的类;

            qdbusxml2cpp com.test.hotel.xml -i hotel.h -p hotelInterface

       这条命令会生成两个文件:hotelInterface.cpp和hotelInterface.h

(3)调用(2)生成的类来访问Service。

下面是举例:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

// 初始化自动生成的Proxy类com::test::hotel::registry

com::test::hotel::registry myHotel("com.test.hotel",

                                   "/hotel/registry",

                                   QDBusConnection::sessionBus());

// 调用checkIn

QDBusPendingReply<int> reply = myHotel.checkIn(num_room);

// qdbusxml2cpp生成的Proxy类是采用异步的方式来传递Message,

// 所以在此需要调用waitForFinished来等到Message执行完成

reply.waitForFinished();

if (reply.isValid()) {

        num_room = reply.value();

        printf("Got %d %s\n", num_room, (num_room > 1) ? "rooms" : "room");

} else {

        fprintf(stderr, "Check In fail!\n");

使用Adapter注册Object

如前文所述,我们可以直接把class Hotel注册为Message Bus上的一个Object,但这种方式并不是QT4所推荐的。QT4推荐使用Adapter来注册Object。

很多情况下,我们可能只需要把我们定义的类里的方法有选择的发布到Message Bus上,使用Adapter可以很方便的实现这种意图。

以前文中的Hotel为例,假设我们只需要把checkIn和checkOut发布到Message Bus上,应该怎么办?

(1)使用工具 qdbuscpp2xml从hotel.h生成XML文件;

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

(2)编辑com.test.hotel.xml,把其中的query部分去掉;

        即去掉以下三条语句:
        <method name="query">

               <arg type="i" direction="out"/>

        </method>

(3)使用工具qdbusxml2cpp从XML文件生成继承自QDBusInterface的类;

            qdbusxml2cpp com.test.hotel.xml -i hotel.h -a hotelAdaptor

       这条命令会生成两个文件:hotelAdaptor.cpp和hotelAdaptor.h

(4)调用(3)生成的类来注册Object。

(参见hotel2.pro)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

int main(int argc, char *argv[])

{

    QCoreApplication a(argc, argv);

    QDBusConnection bus = QDBusConnection::sessionBus();

    Hotel myHotel;

    // RegistryAdaptor是qdbusxml2cpp生成的Adaptor类

    RegistryAdaptor myAdaptor(&myHotel);

    if (!bus.registerService("com.test.hotel")) {

            qDebug() << bus.lastError().message();

            exit(1);

    }

    bus.registerObject("/hotel/registry", &myHotel);

    return a.exec();

}

运行这个应用程序,我们从qdbusviewer上可以看到,只有checkIn和checkOut两个method被发布。如下图所示:

自动启动Service

D-Bus系统提供了一种机制可以在访问某个service时,自动把该程序运行起来。

我们需要在/usr/share/dbus-1/services下面建立com.test.hotel.service文件,文件的内容如下:

[D-BUS Service]

Name=com.test.hotel

Exec=/path/to/your/hotel

这样,我们在访问Hotel的method之前,就不必手动运行该应用程序了。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在LinuxQt可以通过DBus(D-Bus)连接到系统的进程间通信机制。DBusLinux一个很常用的消息总线系统,它允许各个进程之间进行通信。 要在Qt连接DBus,需要使用一个叫做QDBus的模块。该模块提供了与DBus通信的类和方法,使得开发DBus的应用程序变得更加方便和简单。 连接DBus的步骤如下: 1. 首先在Qt项目添加QDBus模块的头文件和库文件。 2. 使用QDBusConnection类连接DBus服务,该类提供了一种与DBus通信的方法。可以使用QDBusConnection::systemBus()或QDBusConnection::sessionBus()方法连接到系统或会话DBus总线。 3. 通过QDBusMessage类创建DBus消息(即DBus消息协议)。该类提供了用于创建DBus消息的方法,例如调用DBus服务,获取DBus服务接口等。 4. 通过QDBusConnection类发送DBus消息。 5. 最后,通过QDBusReply类获取DBus服务的响应数据。 总之,在Qt连接DBus需要使用QDBus模块提供的类和API。这些API可以方便地与DBus通信,并在Qt应用程序和系统的其他程序之间传递消息和数据。 ### 回答2: DBus(D-Bus)是Linux操作系统基于消息的系统总线,用于让进程之间相互通信。而QT是一种广泛使用的C++跨平台开发工具,它可以用于开发多种类型的程序。在LinuxQT可以使用DBus来实现不同进程之间的通信。下面是如何在Linux使用QT连接DBus的方法。 1.安装DBus 首先,要在Linux系统安装DBus。如果您的系统已经安装了DBus,请跳过此步骤。 $ sudo apt-get install dbus 2.创建DBus接口 首先,需要创建一个DBus接口。DBus接口是描述DBus消息如何传输的规则,它定义了DBus服务提供的所有函数。您可以使用dbus-binding-tool工具来创建DBus接口。 $ dbus-binding-tool --mode=glib-server --prefix=my.prefix --output=my-proxy.c my.proxy.xml 这将生成一个C语言的DBus代理文件,其包含DBus接口。您可以在my-proxy.c文件查看DBus接口。 3.创建QT应用程序 接下来,需要创建一个QT应用程序。您可以使用QT Creator来创建一个QT应用程序。确保在QT应用程序包含DBus消息总线库。 4.连接DBus总线 在QT应用程序,需要连接DBus消息总线。您可以在应用程序使用QDBusConnection类来连接DBus。以下是如何连接DBus消息总线的代码: QDBusConnection bus = QDBusConnection::sessionBus(); if (!bus.isConnected()) { qDebug() << "Cannot connect to the D-Bus session bus."; return 1; } 5.连接DBus接口 一旦与DBus总线连接成功,就可以连接DBus接口了。下面是如何连接DBus接口的代码: MyProxy* proxy = new MyProxy("my.prefix", "/my/object/path", bus, this); if (!proxy->isValid()) { qDebug() << "Cannot connect to the D-Bus interface."; return 1; } 在以上代码,MyProxy是您使用dbus-binding-tool工具生成的DBus代理名称。/my/object/path是DBus接口的对象路径。 6.调用DBus函数 现在,您可以使用代理对象来调用DBus函数。以下是如何调用DBus函数的代码: QDBusPendingReply<QDBusVariant> reply = proxy->myDBusFunction(argument); reply.waitForFinished(); if (reply.isError()) { qDebug() << "Error calling the D-Bus function: " << reply.error().message(); return 1; } QDBusVariant result = reply.value(); qDebug() << "DBus function result: " << result.variant().toString(); 在这里,myDBusFunction是DBus接口的一个函数。您可以传递任何参数,并获取函数的返回值。 以上是如何在Linux使用QT连接DBus的方法。通过以上步骤,您可以使用QT来连接DBus,实现不同进程之间的高效通信。 ### 回答3: 在Linux使用Qt连接DBUS是一个比较常见的需求。DBus是一个用于在进程之间进行通信的系统总线,它允许不同进程之间的通信和协作。在Linux系统DBus已经成为了标准的进程间通信方式,因此在使用Qt开发Linux应用时,将DBus集成到Qt程序是必不可少的。 下面我们来介绍一下如何在linux使用Qt连接DBus: 1. 首先要在程序加载DBus库: #include <QDBusInterface> 2. 然后就可以建立DBus连接了: QDBusConnection bus = QDBusConnection::sessionBus(); 其,QDBusConnection::sessionBus() 是一个静态函数,返回一个表示本地DBUS总线连接的QDBusConnection对象。 3. 如果需要调用其他进程的DBus接口,可以使用QDBusInterface类: QDBusInterface iface("com.example.MyService", "/MyServicePath", "com.example.MyServiceInterface", bus); 其,第一个参数是DBus服务的名称,第二个参数是DBus服务的路径,第三个参数是DBus服务暴露的接口名称,第四个参数是DBus总线连接对象。 4. 调用DBus接口: QDBusReply<QString> reply = iface.call("Hello"); 其,QDBusReply是DBus回复的封装类,表示DBus调用的返回结果。call() 函数被用于调用DBus服务的远程方法。 至此,我们已经介绍了在Linux使用Qt连接DBus的基本方法。需要注意,在使用DBus时要遵循DBus协议,包括DBus服务名、DBus对象路径、DBus接口名和DBus方法名等命名规则,否则会导致DBus通信失败。另外,DBus通信也可能会存在一些性能瓶颈,因此在使用时,应该遵循DBus的最佳实践,以获得最佳的性能和效率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值