DBus类型系统以及在Qt和C++ 中的使用(一)

简介: DBus类型系统以及在Qt和C++ 中的使用

DBus类型系统简介

DBus类型系统简介

DBus是一个跨平台的消息总线系统,旨在为不同程序之间提供一种简单有效的通信机制。它支持Linux、Windows和其他UNIX系统上的多种应用程序,常用于桌面环境、硬件设备驱动和系统服务。DBus类型系统定义了一组数据类型,使得消息能够通过总线进行传输,并且允许应用程序之间以结构化的方式共享数据。

以下是DBus类型系统的基本数据类型:

  1. 基本类型:
  • 字节 (Byte):表示一个8位无符号整数,用大写字母y表示。
  • 布尔 (Boolean):表示真(True)或假(False),用大写字母b表示。
  • 有符号整数 (Signed Integer):有三种长度,分别为16位、32位和64位,分别用大写字母s, ix表示。
  • 无符号整数 (Unsigned Integer):有三种长度,分别为16位、32位和64位,分别用大写字母q, ut表示。
  • 双精度浮点数 (Double):表示一个64位双精度浮点数,用大写字母d表示。
  • 字符串 (String):表示一个以NUL字符结尾的UTF-8字符串,用大写字母s表示。
  1. 复合类型:
  • 数组 (Array):表示一个具有相同类型元素的连续集合,用大写字母a表示。数组类型后跟用于描述元素类型的字符,例如:as表示字符串数组,au表示无符号整数数组。
  • 字典 (Dictionary):表示一个包含键值对的集合,用大写字母a{}表示。字典键必须是简单类型(不可为数组或字典),值可以是任何类型。例如:a{si}表示以字符串为键、整数为值的字典,a{sv}表示以字符串为键、任意类型为值的字典(v表示变体类型)。
  • 结构体 (Struct):表示一个有固定数量且类型已知的元素组成的结构,用一对括号()表示。例如:(is)表示一个结构体,包含一个整数和一个字符串,(ias)表示一个结构体,包含一个整数和一个字符串数组。
  • 变体 (Variant):表示一个动态类型,可以包含任意DBus类型的数据,用大写字母v表示。

DBus类型系统为程序员提供了一种灵活的方式来构建和解析消息,进而支持各种应用程序之间的通信。同时,类型系统的设计使得DBus在不同平台和语言环境下能够高效地进行数据交换,提高了开发者的生产力。

QtDBus 是 Qt 库中的一个模块,用于在 Qt 应用程序中使用 D-Bus IPC (进程间通信) 机制。D-Bus 是一种通用的进程间通信框架,让各个独立的程序可以通过公共的消息总线进行通信。QtDBus 模块提供了对这个机制的封装,使得在 Qt 应用程序中使用 D-Bus 更加方便。

为了在 D-Bus 上通信,程序需要发送和接收数据。QtDBus 模块实现了 D-Bus 的扩展类型系统,支持多种数据类型,包括原生类型和复合类型。这些数据类型可以通过 QDBusArgument 类进行编码和解码。

下面是一些 QtDBus 模块支持的基本类型和复合类型:

基本类型:

  1. bool:布尔类型,用于表示真或假。
  2. qint8, qint16, qint32, qint64:分别表示 8 位、16 位、32 位和 64 位整数。
  3. quint8, quint16, quint32, quint64:分别表示 8 位、16 位、32 位和 64 位无符号整数。
  4. double:双精度浮点数。
  5. QString:表示字符串。

复合类型:

  1. QDBusObjectPath:表示 D-Bus 对象路径。
  2. QDBusSignature:表示 D-Bus 签名。
  3. QDBusVariant:表示可变类型,可以包含任何类型的值。
  4. QVariantList:表示 QVariant 类型的列表,可以存储不同类型的值。
  5. QStringList:表示字符串列表。
  6. QHash:表示一个哈希表,其中键为字符串,值为 QVariant 类型。

要在 D-Bus 总线上发送或接收这些类型的数据,可以使用 QDBusMessage 类。QDBusMessage 支持几种不同类型的消息,如方法调用、方法返回、信号和错误。通过 QDBusMessage,可以设置消息的参数和读取参数。

要将参数添加到 QDBusMessage,可以使用 QDBusArgument 类。QDBusArgument 类提供了一组操作符重载,用于编码和解码 QtDBus 支持的各种类型。这使得在 Qt 应用程序中使用 D-Bus 总线变得非常简单和直观。

总之,QtDBus 模块实现了 D-Bus 的扩展类型系统,使得在 Qt 应用程序中使用 D-Bus 进程间通信变得简单。QDBusArgument 类支持编码和解码多种数据类型,方便应用程序在总线上发送和接收数据。

原生类型(Primitive Types)

DBus 原始类型是构成 DBus 通信协议的基本数据类型。这些原始类型在 DBus 中用于表示方法参数、返回值、信号等数据。以下是对各种原始类型的文字描述:

  1. 字节(BYTE):无符号字节(8位)表示一个字节的数据,范围从 0 到 255。
  2. 布尔(BOOLEAN):布尔值表示真或假的逻辑值,通常用于表示条件、状态或者控制流程。
  3. 有符号16位整数(INT16):有符号 16 位整数表示一个范围为 -32,768 到 32,767 的整数。
  4. 无符号16位整数(UINT16):无符号 16 位整数表示一个范围为 0 到 65,535 的整数。
  5. 有符号32位整数(INT32):有符号 32 位整数表示一个范围为 -2,147,483,648 到 2,147,483,647 的整数。
  6. 无符号32位整数(UINT32):无符号 32 位整数表示一个范围为 0 到 4,294,967,295 的整数。
  7. 有符号64位整数(INT64):有符号 64 位整数表示一个范围为 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 的整数。
  8. 无符号64位整数(UINT64):无符号 64 位整数表示一个范围为 0 到 18,446,744,073,709,551,615 的整数。
  9. 双精度浮点数(DOUBLE):双精度浮点数表示一个具有双精度(64 位)的实数,可以表示大范围的小数值。
  10. 字符串(STRING):字符串表示一个由字符组成的文本序列,采用 UTF-8 编码。字符串在 DBus 中可以用于表示文本、文件名、URL 等信息。
  11. 对象路径(OBJECT_PATH):对象路径是一个特殊类型的字符串,表示 DBus 服务中对象的路径。对象路径类似于 UNIX 文件路径,用于定位 DBus 上的特定对象。
  12. 类型签名(SIGNATURE):类型签名是一种元数据,用于描述数据的类型和结构。它在 DBus 中用于表示接口、方法和信号的参数类型。类型签名由一系列字符组成,每个字符表示一种数据类型。

这些原始类型在 DBus 中广泛使用,它们可以单独使用,也可以与复合类型结合使用,以满足各种复杂的数据传输需求。

DBus 数据类型 Qt 数据类型 C++ 标准库 (std) 数据类型 说明
字节(BYTE) quint8 uint8_t 无符号字节(8位)
布尔(BOOLEAN) bool bool 布尔值(真或假)
有符号16位整数(INT16) qint16 int16_t 有符号 16 位整数
无符号16位整数(UINT16) quint16 uint16_t 无符号 16 位整数
有符号32位整数(INT32) qint32 int32_t 有符号 32 位整数
无符号32位整数(UINT32) quint32 uint32_t 无符号 32 位整数
有符号64位整数(INT64) qint64 int64_t 有符号 64 位整数
无符号64位整数(UINT64) quint64 uint64_t 无符号 64 位整数
双精度浮点数(DOUBLE) double double 双精度浮点数
字符串(STRING) QString std::string 字符串(UTF-8 编码)
对象路径(OBJECT_PATH) QDBusObjectPath 无对应类型 对象路径(类似于 UNIX 文件路径)
类型签名(SIGNATURE) QDBusSignature 无对应类型 类型签名(一种元数据,描述数据的类型和结构)

原生类型综合示例

在 DBus 中,我们主要用这些数据类型来定义接口、方法和信号。接下来我会通过一个简单的例子来说明如何在 DBus 中使用这些数据类型。

假设我们有一个计算器应用,它有一个接口 com.example.Calculator,包含两个方法:AddMultiply。这个接口的定义如下:

<node>
  <interface name="com.example.Calculator">
    <method name="Add">
      <arg type="i" direction="in" name="a"/>
      <arg type="i" direction="in" name="b"/>
      <arg type="i" direction="out" name="result"/>
    </method>
    <method name="Multiply">
      <arg type="i" direction="in" name="a"/>
      <arg type="i" direction="in" name="b"/>
      <arg type="i" direction="out" name="result"/>
    </method>
  </interface>
</node>

在这个例子中,我们使用了 INT32 类型(在 XML 中表示为 i),定义了两个输入参数 ab,以及一个输出参数 resultAdd 方法用于求和,Multiply 方法用于求积。

接下来,我们可以在客户端调用这些方法,例如在 Python 中使用 pydbus 库:

from pydbus import SystemBus
bus = SystemBus()
calculator = bus.get("com.example.Calculator", "/com/example/Calculator")
a = 3
b = 4
sum_result = calculator.Add(a, b)
product_result = calculator.Multiply(a, b)
print(f"Sum: {sum_result}")
print(f"Product: {product_result}")

在这个客户端示例中,我们首先通过 SystemBus 连接到 DBus 总线,然后获取 com.example.Calculator 服务的 /com/example/Calculator 对象路径。接着,我们调用 AddMultiply 方法,并将结果打印出来。

这个例子仅使用了 INT32 类型,但是你可以根据实际需求使用其他 DBus 数据类型。你可以在接口定义中使用任意类型的组合来定义方法和信号。当你需要在方法和信号之间传递数据时,DBus 会自动处理类型转换和序列化。

接下来我将使用 C++ 和 sdbus-c++ 库来展示如何实现上述计算器服务。首先,我们需要定义一个实现 com.example.Calculator 接口的类:

#include <sdbus-c++/sdbus-c++.h>
class Calculator : public sdbus::AdaptorInterfaces<Calculator>
{
public:
    Calculator(sdbus::IConnection& connection, std::string objectPath)
        : AdaptorInterfaces(connection, std::move(objectPath))
    {
        registerAdaptorInterface("com.example.Calculator");
    }
    int32_t Add(int32_t a, int32_t b)
    {
        return a + b;
    }
    int32_t Multiply(int32_t a, int32_t b)
    {
        return a * b;
    }
};

在这个类中,我们定义了 AddMultiply 方法,它们分别接收两个 int32_t 类型的参数,返回 int32_t 类型的结果。

然后,我们需要编写一个主程序来启动这个服务:

#include <iostream>
#include "Calculator.h"
int main()
{
    // 创建 DBus 系统总线连接
    auto connection = sdbus::createSystemBusConnection();
    // 创建 Calculator 对象并导出到 DBus
    Calculator calculator(*connection, "/com/example/Calculator");
    // 运行消息循环
    connection->enterEventLoop();
    return 0;
}

接下来,我们需要编写一个 C++ 客户端来调用这个服务。我们可以使用 sdbus-c++ 库的代理类来实现:

#include <sdbus-c++/sdbus-c++.h>
#include <iostream>
class CalculatorProxy : public sdbus::ProxyInterfaces<CalculatorProxy>
{
public:
    CalculatorProxy(sdbus::IConnection& connection, std::string serviceName, std::string objectPath)
        : ProxyInterfaces(connection, std::move(serviceName), std::move(objectPath))
    {
        registerProxyInterface("com.example.Calculator");
    }
    int32_t Add(int32_t a, int32_t b)
    {
        return callMethod("Add").onInterface("com.example.Calculator").withArguments(a, b).storeResultsIn(a);
    }
    int32_t Multiply(int32_t a, int32_t b)
    {
        return callMethod("Multiply").onInterface("com.example.Calculator").withArguments(a, b).storeResultsIn(a);
    }
};
int main()
{
    // 创建 DBus 系统总线连接
    auto connection = sdbus::createSystemBusConnection();
    // 创建 CalculatorProxy 对象
    CalculatorProxy calculator(*connection, "com.example.Calculator", "/com/example/Calculator");
    int32_t a = 3, b = 4;
    // 调用 Add 方法
    int32_t sum_result = calculator.Add(a, b);
    std::cout << "Sum: " << sum_result << std::endl;
    // 调用 Multiply 方法
    int32_t product_result = calculator.Multiply(a, b);
    std::cout << "Product: " << product_result << std::endl;
    return 0;
}

在客户端示例中,我们首先创建了 CalculatorProxy 类,用于调用 com.example.Calculator 服务的 AddMultiply 方法。

复合类型(Compound Types)

当然可以。除了原始类型,DBus 还提供了一些复合类型,用于表示更复杂的数据结构。以下是对各种复合类型的文字描述:

  1. 数组(ARRAY):数组是一种包含多个相同类型元素的集合。在 DBus 中,数组用于表示一组相同类型的数据,例如整数列表、字符串列表等。数组中的元素类型可以是原始类型,也可以是其他复合类型。
  2. 结构(STRUCT):结构是一种包含多个不同类型元素的集合。在 DBus 中,结构用于表示一组具有固定格式和顺序的不同类型的数据。结构中的元素类型可以是原始类型,也可以是其他复合类型。
  3. 字典(DICT):字典是一种包含键值对的集合。在 DBus 中,字典用于表示一组具有唯一键和对应值的数据。字典的键类型必须是原始类型,而值类型可以是原始类型或其他复合类型。常见的字典类型是以字符串作为键的字典,如:std::map
  4. 变体(VARIANT):变体是一种特殊的数据类型,可以包含任何其他类型的数据。在 DBus 中,变体用于表示动态类型的数据,即数据类型在运行时可以更改。变体可以包含原始类型和复合类型。使用变体时需要小心,因为它可能增加类型安全性的风险。

这些复合类型可以与原始类型结合使用,形成更复杂的数据结构。在 DBus 中,复合类型通常用于表示方法参数、返回值和信号等复杂数据。通过使用复合类型,可以在 DBus 通信协议中表示各种复杂的数据结构和层次关系。

复合类型 英文名称 描述 示例 Qt 数据类型 C++ 标准库数据类型
数组 ARRAY 包含多个相同类型元素的集合。用于表示一组相同类型的数据。元素类型可以是原始类型或其他复合类型。 std::vector<int>std::vector<std::string> QList<T>QVector<T> std::vector<T>
结构 STRUCT 包含多个不同类型元素的集合。用于表示一组具有固定格式和顺序的不同类型的数据。元素类型可以是原始类型或其他复合类型。 std::tuple<int, double, std::string> QPair<T1, T2>QVariantList 或自定义 QVariant 结构 std::tuple<T1, T2, ...> 或自定义结构体
字典 DICT 包含键值对的集合。用于表示一组具有唯一键和对应值的数据。键类型必须是原始类型,值类型可以是原始类型或其他复合类型。 std::map<std::string, int> QMap<Key, Value>QHash<Key, Value> std::map<Key, Value>std::unordered_map<Key, Value>
变体 VARIANT 可以包含任何其他类型的数据。用于表示动态类型的数据,即数据类型在运行时可以更改。变体可以包含原始类型和复合类型。 QVariant QVariant std::variant<T1, T2, ...>

复合类型综合示例

结构体的示例

当然可以。下面是一个综合示例,展示了如何在 DBus 服务中使用这些原始类型和复合类型。我们将实现一个简单的信息服务,提供以下功能:

  1. 添加一名用户(包含 ID、姓名和年龄)
  2. 获取用户信息(根据 ID 获取用户信息)
  3. 获取用户年龄的平均值

首先,我们创建一个包含用户信息的结构:

struct UserInfo
{
    uint32_t id;
    std::string name;
    uint16_t age;
};

接下来,我们定义一个实现 com.example.InfoService 接口的类:

#include <sdbus-c++/sdbus-c++.h>
#include <map>
class InfoService : public sdbus::AdaptorInterfaces<InfoService>
{
public:
    InfoService(sdbus::IConnection& connection, std::string objectPath)
        : AdaptorInterfaces(connection, std::move(objectPath))
    {
        registerAdaptorInterface("com.example.InfoService");
    }
    void AddUser(uint32_t id, const std::string& name, uint16_t age)
    {
        UserInfo user {id, name, age};
        users_[id] = user;
    }
    std::tuple<uint32_t, std::string, uint16_t> GetUser(uint32_t id)
    {
        const auto& user = users_.at(id);
        return std::make_tuple(user.id, user.name, user.age);
    }
    double GetAverageAge()
    {
        uint32_t total_age = 0;
        for (const auto& user : users_)
        {
            total_age += user.second.age;
        }
        return static_cast<double>(total_age) / users_.size();
    }
private:
    std::map<uint32_t, UserInfo> users_;
};

对于 D-Bus 服务,接口定义可以用 XML 格式编写,这是 D-Bus 规范中推荐的方式。使用 XML 可以让接口定义与实现相互独立,便于跨语言和跨平台使用。

然而,在上面的示例中,我们使用了 C++ 语言和 sdbus-c++ 库来实现 D-Bus 服务,该库会自动生成 XML 格式的接口定义。在 sdbus-c++ 中,接口定义以 C++ 类的形式编写,库内部负责将其转换为 D-Bus 的 XML 表示。因此,我们不需要显式地编写 XML 文件,而是直接用 C++ 类定义接口。

但是,如果您希望在其他语言或 D-Bus 库中使用这个服务,您可能需要将接口定义转换为 XML 格式。在这种情况下,您可以使用 D-Feet 等工具在运行时检查 D-Bus 服务的接口定义,或者手动编写对应的 XML 文件。

以下是一个与上面示例相对应的 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.example.InfoService">
    <method name="AddUser">
      <arg type="u" name="id" direction="in"/>
      <arg type="s" name="name" direction="in"/>
      <arg type="q" name="age" direction="in"/>
    </method>
    <method name="GetUser">
      <arg type="u" name="id" direction="in"/>
      <arg type="u" name="user_id" direction="out"/>
      <arg type="s" name="name" direction="out"/>
      <arg type="q" name="age" direction="out"/>
    </method>
    <method name="GetAverageAge">
      <arg type="d" name="average_age" direction="out"/>
    </method>
  </interface>
</node>

这个 XML 文件描述了 com.example.InfoService 接口及其方法和参数。如果需要,您可以将此 XML 文件用于其他 D-Bus 库或编程语言。

然后,我们需要编写一个主程序来启动这个服务:

#include <iostream>
#include "InfoService.h"
int main()
{
    // 创建 DBus 系统总线连接
    auto connection = sdbus::createSystemBusConnection();
    // 创建 InfoService 对象并导出到 DBus
    InfoService info_service(*connection, "/com/example/InfoService");
    // 运行消息循环
    connection->enterEventLoop();
    return 0;
}

接下来,我们需要编写一个 C++ 客户端来调用这个服务。我们可以使用 sdbus-c++ 库的代理类来实现:

#include <sdbus-c++/sdbus-c++.h>
#include <iostream>
class InfoServiceProxy : public sdbus::ProxyInterfaces<InfoServiceProxy>
{
public:
    InfoServiceProxy(sdbus::IConnection& connection, std::string serviceName, std::string objectPath)
        : ProxyInterfaces(connection, std::move(serviceName), std::move(objectPath))
    {
        registerProxyInterface("com.example.InfoService");
    }
    void AddUser(uint32_t id, const std::string& name, uint16_t age)
    {
        callMethod("AddUser").onInterface("com.example.InfoService").withArguments(id, name, age);
    }
    std::tuple<uint32_t, std::string, uint16_t> GetUser(uint32_t id)
    {
        uint32_t user_id;
        std::string name;
        uint16_t age;
        callMethod("GetUser").onInterface("com.example.InfoService").withArguments(id).storeResultsTo(user_id, name, age);
            return std::make_tuple(user_id, name, age);
}
double GetAverageAge()
{
    double average_age;
    callMethod("GetAverageAge").onInterface("com.example.InfoService").storeResultsTo(average_age);
    return average_age;
}
};
int main()
{
// 创建 DBus 系统总线连接
auto connection = sdbus::createSystemBusConnection();
// 创建 InfoService 代理对象
InfoServiceProxy info_service_proxy(*connection, "com.example.InfoService", "/com/example/InfoService");
// 添加用户
info_service_proxy.AddUser(1, "Alice", 30);
info_service_proxy.AddUser(2, "Bob", 35);
// 获取用户信息
auto user_info = info_service_proxy.GetUser(1);
std::cout << "User 1: ID = " << std::get<0>(user_info) << ", Name = " << std::get<1>(user_info) << ", Age = " << std::get<2>(user_info) << std::endl;
// 获取平均年龄
double average_age = info_service_proxy.GetAverageAge();
std::cout << "Average age: " << average_age << std::endl;
return 0;

这个综合示例展示了如何在一个简单的 DBus 服务中使用原始类型和复合类型。服务提供了添加用户、获取用户信息和计算平均年龄的方法,涉及到了整数、字符串、结构和字典等多种数据类型。客户端程序使用代理类调用服务的方法,并处理返回的数据。

下面是使用 Qt DBus 实现相同功能的示例。首先,我们需要在 .pro 文件中添加 QtDBus 模块:

首先,在 .pro 文件中添加 QtDBus 模块:

QT += core dbus

创建一个包含用户信息的结构:

// UserInfo.h
#pragma once
#include <QString>
struct UserInfo
{
    uint32_t id;
    QString name;
    uint16_t age;
};
Q_DECLARE_METATYPE(UserInfo) // 声明自定义类型,使其可用于 Qt DBus

为了在 Qt DBus 中使用自定义数据类型,我们需要为其定义一个 QDBusArgument 的序列化和反序列化函数:

// UserInfo.cpp
#include "UserInfo.h"
#include <QDBusArgument>
QDBusArgument& operator<<(QDBusArgument& argument, const UserInfo& user)
{
    argument.beginStructure();           // 开始一个结构类型
    argument << user.id << user.name << user.age;
    argument.endStructure();             // 结束一个结构类型
    return argument;
}
const QDBusArgument& operator>>(const QDBusArgument& argument, UserInfo& user)
{
    argument.beginStructure();           // 开始一个结构类型
    argument >> user.id >> user.name >> user.age;
    argument.endStructure();             // 结束一个结构类型
    return argument;
}

接下来,创建一个实现 com.example.InfoService 接口的类:

// InfoService.h
#pragma once
#include <QDBusAbstractAdaptor>
#include <QMap>
#include "UserInfo.h"
class InfoService : public QDBusAbstractAdaptor
{
    Q_OBJECT
    Q_CLASSINFO("D-Bus Interface", "com.example.InfoService")
public:
    InfoService(QObject* parent = nullptr)
        : QDBusAbstractAdaptor(parent)
    {
    }
public slots:
    void AddUser(uint32_t id, const QString& name, uint16_t age)
    {
        UserInfo user {id, name, age};
        users_[id] = user;
    }
    Q_SCRIPTABLE UserInfo GetUser(uint32_t id)
    {
        return users_[id];
    }
    double GetAverageAge()
    {
        uint32_t total_age = 0;
        for (const auto& user : users_)
        {
            total_age += user.age;
        }
        return static_cast<double>(total_age) / users_.size();
    }
private:
    QMap<uint32_t, UserInfo> users_;
};

编写一个主程序来启动这个服务:

// main_server.cpp
#include <QCoreApplication>
#include <QDBusConnection>
#include "InfoService.h"
#include "UserInfo.h"
int main(int argc, char* argv[])
{
    QCoreApplication app(argc, argv);
    qDBusRegisterMetaType<UserInfo>(); // 注册自定义类型
    InfoService info_service;
    QDBusConnection::sessionBus().registerObject("/com/example/InfoService", &info_service, QDBusConnection::ExportAllSlots);
    return app.exec();
}

最后,编写一个 Qt 客户端来调用这个服务。我们可以使用 QDBusInterface 类来实现:

// main_client.cpp
#include <QCoreApplication>
#include <QDBusInterface>
#include <QDBusReply>
#include <QDebug>
#include "UserInfo.h"
int main(int argc, char* argv[])
{
    QCoreApplication app(argc, argv);
    qDBusRegisterMetaType<UserInfo>(); // 注册自定义类型
    // 创建一个 DBus 代理接口对象,用于访问远程服务
    QDBusInterface info_service("com.example.InfoService", "/com/example/InfoService", "com.example.InfoService", QDBusConnection::sessionBus());
    if (info_service.isValid()) // 检查接口是否有效
    {
        // 调用服务的 AddUser 方法添加用户
        info_service.call("AddUser", 1, "Alice", 30);
        info_service.call("AddUser", 2, "Bob", 35);
        // 调用服务的 GetUser 方法获取用户信息
        QDBusReply<UserInfo> reply = info_service.call("GetUser", 1);
        if (reply.isValid()) // 检查返回值是否有效
        {
            UserInfo user = reply.value();
            qDebug() << "User 1: ID =" << user.id << ", Name =" << user.name << ", Age =" << user.age;
        }
        // 调用服务的 GetAverageAge 方法获取平均年龄
        QDBusReply<double> average_age_reply = info_service.call("GetAverageAge");
        if (average_age_reply.isValid()) // 检查返回值是否有效
        {
            double average_age = average_age_reply.value();
            qDebug() << "Average age:" << average_age;
        }
    }
    else
    {
        qDebug() << "Failed to connect to InfoService";
    }
    return 0;
}

这个客户端示例展示了如何使用 Qt DBus 连接到远程服务,调用方法,并处理返回的数据。我们首先创建了一个 QDBusInterface 对象来代表远程服务的接口。然后,我们检查接口是否有效,之后调用服务的方法,包括添加用户、获取用户信息和计算平均年龄。最后,我们处理方法的返回值,并在控制台上打印相关信息。


DBus类型系统以及在Qt和C++ 中的使用(二)https://developer.aliyun.com/article/1464206

目录
相关文章
|
26天前
|
开发框架 Linux C语言
C、C++、boost、Qt在嵌入式系统开发中的使用
C、C++、boost、Qt在嵌入式系统开发中的使用
32 1
|
1月前
|
开发框架 Java 编译器
【Qt 元对象系统 01 】深入探索Qt的元对象系统:核心地位、功能与构成
【Qt 元对象系统 01 】深入探索Qt的元对象系统:核心地位、功能与构成
52 0
|
1月前
|
算法 程序员 C语言
【C++ 随机数分布类型 】深入探索C++随机数分布:原理、应用与实践(二)
【C++ 随机数分布类型 】深入探索C++随机数分布:原理、应用与实践
56 0
【C++ 随机数分布类型 】深入探索C++随机数分布:原理、应用与实践(二)
|
1月前
|
存储 网络协议 C语言
【C/C++ 串口编程 】深入探讨C/C++与Qt串口编程中的粘包现象及其解决策略
【C/C++ 串口编程 】深入探讨C/C++与Qt串口编程中的粘包现象及其解决策略
83 0
|
1月前
|
存储 传感器 安全
【串口通信】使用C++和Qt设计和实现串口协议解析器(二)
【串口通信】使用C++和Qt设计和实现串口协议解析器
54 0
|
1月前
|
存储 开发框架 算法
【串口通信】使用C++和Qt设计和实现串口协议解析器(一)
【串口通信】使用C++和Qt设计和实现串口协议解析器
109 0
|
1月前
|
算法 编译器 数据库
【C++ 泛型编程 高级篇】使用SFINAE和if constexpr灵活处理类型进行条件编译
【C++ 泛型编程 高级篇】使用SFINAE和if constexpr灵活处理类型进行条件编译
247 0
|
1月前
|
机器学习/深度学习 算法 编译器
【C++ 泛型编程 中级篇】深度解析C++:类型模板参数与非类型模板参数
【C++ 泛型编程 中级篇】深度解析C++:类型模板参数与非类型模板参数
47 0
|
16天前
|
存储 算法 Linux
【实战项目】网络编程:在Linux环境下基于opencv和socket的人脸识别系统--C++实现
【实战项目】网络编程:在Linux环境下基于opencv和socket的人脸识别系统--C++实现
40 6
|
1月前
|
安全 数据处理 C++
【Qt 底层之事件驱动系统】深入理解 Qt 事件机制:主事件循环与工作线程的交互探究,包括 QML 的视角
【Qt 底层之事件驱动系统】深入理解 Qt 事件机制:主事件循环与工作线程的交互探究,包括 QML 的视角
118 3

推荐镜像

更多