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

目录
相关文章
|
1月前
|
存储 Linux C语言
(2)Qt中的字符串类型
本文介绍了Qt中的字符串类型QByteArray和QString,包括它们的构造函数、数据操作方法、查找操作、遍历操作以及与其他类型之间的转换,并解释了它们之间的区别。
106 5
(2)Qt中的字符串类型
WK
|
9天前
|
开发框架 开发工具 C++
C++跨平台框架Qt
Qt是一个功能强大的C++跨平台应用程序开发框架,支持Windows、macOS、Linux、Android和iOS等操作系统。它提供了250多个C++类,涵盖GUI设计、数据库操作、网络编程等功能。Qt的核心特点是跨平台性、丰富的类库、信号与槽机制,以及良好的文档和社区支持。Qt Creator是其官方IDE,提供了一整套开发工具,方便创建、编译、调试和运行应用程序。Qt适用于桌面、嵌入式和移动应用开发。
WK
32 5
|
1月前
|
存储 Windows
(13) Qt事件系统(two)
文章详细介绍了Qt事件系统,包括事件分发、自定义事件、事件传播机制、事件过滤以及事件与信号的区别。
75 3
(13) Qt事件系统(two)
|
1月前
|
编解码 程序员
(12)Qt事件系统(one)
本文详细介绍了Qt事件系统,包括各种系统事件、鼠标事件、键盘事件、定时器等的处理方法和示例代码。
70 0
(12)Qt事件系统(one)
|
1月前
|
存储 编译器 程序员
C++类型参数化
【10月更文挑战第1天】在 C++ 中,模板是实现类型参数化的主要工具,用于编写能处理多种数据类型的代码。模板分为函数模板和类模板。函数模板以 `template` 关键字定义,允许使用任意类型参数 `T`,并在调用时自动推导具体类型。类模板则定义泛型类,如动态数组,可在实例化时指定具体类型。模板还支持特化,为特定类型提供定制实现。模板在编译时实例化,需放置在头文件中以确保编译器可见。
32 11
|
2月前
|
C++
【C++案例】一个项目掌握C++基础-通讯录管理系统
这篇文章通过一个通讯录管理系统的C++项目案例,详细介绍了如何使用C++实现添加、显示、删除、查找、修改和清空联系人等功能。
42 3
|
3月前
|
Rust 安全 C++
系统编程的未来之战:Rust能否撼动C++的王座?
【8月更文挑战第31天】Rust与C++:现代系统编程的新选择。C++长期主导系统编程,但内存安全问题频发。Rust以安全性为核心,通过所有权和生命周期概念避免内存泄漏和野指针等问题。Rust在编译时确保内存安全,简化并发编程,其生态系统虽不及C++成熟,但发展迅速,为现代系统编程提供了新选择。未来有望看到更多Rust驱动的系统级应用。
61 1
|
3月前
|
C++
C++ Qt开发:QUdpSocket网络通信组件
QUdpSocket是Qt网络编程中一个非常有用的组件,它提供了在UDP协议下进行数据发送和接收的能力。通过简单的方法和信号,可以轻松实现基于UDP的网络通信。不过,需要注意的是,UDP协议本身不保证数据的可靠传输,因此在使用QUdpSocket时,可能需要在应用层实现一些机制来保证数据的完整性和顺序,或者选择在适用的场景下使用UDP协议。
151 2
|
3月前
|
API
Qt绘图之Paint系统
Qt绘图之Paint系统
59 2
|
2月前
|
安全 程序员 C语言
C++(四)类型强转
本文详细介绍了C++中的四种类型强制转换:`static_cast`、`reinterpret_cast`、`const_cast`和`dynamic_cast`。每种转换都有其特定用途和适用场景,如`static_cast`用于相关类型间的显式转换,`reinterpret_cast`用于低层内存布局操作,`const_cast`用于添加或移除`const`限定符,而`dynamic_cast`则用于运行时的类型检查和转换。通过具体示例展示了如何正确使用这四种转换操作符,帮助开发者更好地理解和掌握C++中的类型转换机制。