使用Qt D-Bus适配器:
适配器是附加到任何q对象派生类的特殊类,并使用D-Bus提供对外部世界的接口。适配器被设计为轻量级类,其主要目的是传递对真实对象的调用,可能验证或转换来自外部世界的输入,从而保护真实的对象。与多重继承不同,适配器可以在任何时候添加到任何对象(但不删除),这允许在导出现有类时获得更大的灵活性。适配器的另一个优点是在不同的接口中提供类似但不完全相同的功能,这种情况在向对象添加新版本的标准接口时是非常常见的。为了使用适配器,必须创建一个继承 QDBusAbstractAdaptor适配器的类。因为这是一个标准的qobject派生类,所以Q_OBJECT 宏必须出现在声明中,并且源文件必须使用moc工具进行处理。该类必须包含一个带有“D-Bus接口”名称的Q_CLASSINFO条目,声明它正在导出哪个接口。每个类只支持一个条目。
该类中的任何公共槽都可以通过总线访问方法调用类型的消息。(请参阅D-Bus适配器的声明槽以获得更多信息)。课堂上的信号将通过D-Bus自动转播。然而,并不是所有类型都是允许的信号或插槽的参数列表:查看Qt D-Bus类型系统以获得更多信息。
而且,任何用Q_PROPERTY声明的属性都将自动暴露在D-Bus的属性接口上。由于QObject属性系统不允许不可读的属性,所以不可能使用适配器来声明只写写属性。
在D-Bus中声明槽函数
在D-Bus中声明信号
Qt D-Bus 类型系统
在D-Bus中声明槽函数:
内容:异步槽、仅输入槽、输入输出槽、自动应答、延迟应答
在D-Bus中声明槽函数:
D-Bus适配器的插槽被声明为普通的公共插槽,但是它们的参数必须遵循一定的规则(更多信息请参阅Qt D-Bus类型系统)。那些参数不遵循这些规则或不公开的插槽将无法通过D-Bus访问。
插槽可以有一个类型const QDBusMessage,它必须出现在输入参数列表的末尾,在任何输出参数之前。这个参数,如果存在的话,将用正在处理的当前消息的副本初始化,这允许被调者获取关于调用者的信息,比如它的连接名称。
有个三种槽:异步槽、只输入槽、输入输出槽。
异步槽:
异步槽是那些通常不返回给调用者的回复的。出于这个原因,他们不能接受任何输出参数。在大多数情况下,当运行槽的第一行运行时,调用者函数已经恢复工作。然而,槽函数不能依赖于这种行为。调度和消息调度问题可能会改变槽运行的顺序。打算与调用者同步的代码应该提供它自己的同步方法。异步槽在方法签名中由关键字Q_NOREPLY标记,在void返回类型和槽名之前。
只输入槽:
只有输入的槽是正常的槽,它们取值传递的参数或常量引用。然而,与异步槽不同的是,调用者通常在恢复操作之前等待callee的完成。因此,非异步槽不应该阻塞或应该声明它们可能这样做的文档。只有输入的插槽在他们的签名中没有特殊的标记,除非他们只接受通过值或常量引用的参数。可选地,插槽可以作为最后一个参数使用QDBusMessage参数,它可以用来对方法调用消息执行额外的分析。
输入输出槽:
就像输入-唯一的插槽,输入-输出槽是调用者等待回复的。不过,与只输入的数据不同,这个回复将包含数据。输出数据的槽可能包含非常量引用,也可能返回一个值。然而,输出参数必须全部出现在参数列表的末尾,并且可能没有输入参数交错。可选地,QDBusMessage参数可能出现在输入和输出参数之间。
自动应答:
方法回复是由Qt D-Bus实现的输出参数(如果有的话)自动生成的。插槽不必担心构造合适的QDBusMessage对象,并将它们发送到连接上。
然而,这样做的可能性依然存在。如果这个槽发现它需要发送一个特别的回复,或者甚至是一个错误,它可以使用QDBusMessage::createReply()或QDBusMessage::createErrorReply()在QDBusMessage参数上,并使用QDBusConnection::send()发送它。如果槽位这样做,Qt D-Bus实现将不会产生任何应答。
警告:当调用者放置一个方法调用并等待应答时,它只会等待有限的时间。想要花很长时间完成的槽应该在文档中清楚地说明这一点,以便调用者适当地设置更高的超时。
延迟应答:
在某些情况下,被调用的槽可能无法立即处理请求。当请求涉及到一个可能阻塞的输入/输出或网络操作时,通常会出现这种情况。
如果是这种情况,插槽应该将控制权返回给应用程序的主回路,以避免冻结用户界面,并在以后恢复该进程。为了实现这一点,它应该在输入参数列表的末尾使用额外的QDBusMessage参数,并请求延迟回复。
我们通过编写一个槽来将请求数据存储在一个持久的结构中,并使用QDBusMessage::setDelayedReply(true)来指示调用者,稍后将发送响应。
struct RequestData
{
QString request;
QString processedData;
QDBusMessage reply;
};
QString processRequest(const QString &request, const QDBusMessage &message)
{
RequestData *data = new RequestData;
data->request = request;
message.setDelayedReply(true);
data->reply = message.createReply();
QDBusConnection::sessionBus().send(data->reply);
appendRequest(data);
return QString();
}
使用QDBusConnection::sessionBus().send(data->reply)需要显式通知调用者,响应将被延迟。在这种情况下,返回值不重要;我们返回一个任意值来满足编译器。
当请求被处理并且有一个应答可用时,应该使用获得的QDBusMessage对象来发送它。在我们的例子中,应答代码可以是如下的:
void sendReply(RequestData *data)
{
// data->processedData has been initialized with the request's reply
QDBusMessage &reply = &data->reply;
// send the reply over D-Bus:
reply << data->processedData;
QDBusConnection::sessionBus().send(reply);
// dispose of the transaction data
delete data;
}
正如在示例中所看到的,当延迟回复就绪时,来自槽的返回值(s)将被Qt D-Bus忽略。它们仅用于在将适配器的描述与远程应用程序通信时确定槽的签名,或者在槽内的代码决定不使用延迟应答的情况下。
延迟回复本身是通过Qt D-Bus请求的,它通过调用QDBusMessage::reply()在原始消息上。然后,它就变成了被调用的代码的可重性,最终向调用者发送一个应答。
警告:当调用者放置一个方法调用并等待应答时,它只会等待有限的时间。想要花很长时间完成的槽应该在文档中清楚地说明这一点,以便调用者适当地设置更高的超时。
在D-Bus适配器中声明信号:
从qdbusabstract适配器派生的类中的任何信号都将被自动转发到D-Bus,前提是信号的参数符合某些规则(请参阅Qt D-Bus类型系统以获得更多信息)。不需要特殊的代码来进行这个继电器。
然而,信号仍然必须被释放。发送适配器信号最简单的方法是将另一个信号连接到它,这样Qt的信号和插槽机制也会自动发出适配器信号。这可以在适配器的构造器中完成,就像在D-Bus适配器示例中所做的那样。
QDBusAbstractAdaptor::setAutoRelaySignals()便利函数也可用于在真实对象的信号和适配器中相应的信号之间建立和断开连接。它将检查两个类中的信号列表,并连接那些参数匹配的参数。
Qt D-Bus 类型系统:
D-Bus有一个可扩展的类型系统,它基于几个原语和数组和结构中的原语的组成。Qt D-Bus通过qdbus参数类实现该类型系统的接口,允许用户程序在总线上发送和接收几乎所有C++类型。
基本类型:
Qt type D-Bus equivalent type
uchar BYTE
bool BOOLEAN
short INT16
ushort UINT16
int INT32
uint UINT32
qlonglong INT64
qulonglong UINT64
double DOUBLE
QString STRING
QDBusVariant VARIANT
QDBusObjectPath OBJECT_PATH
QDBusSignature SIGNATURE
- 复合类型:
D-Bus指定了三种类型的原始类型的聚合,允许人们创建复合类型。它们是数组、结构体和地图/字典。
数组是同一类型的零个或多个元素的集合,而结构是一组固定数量的元素,每一种类型。映射或字典是作为一对元素的数组实现的,因此在一个映射中可以有零个或更多的元素。
扩展的类型系统:
为了使用Qt D-Bus本身的类型,必须将类型声明为Qt元类型,并使用qregister emetatype()宏,并在qDBusRegisterMetaType()函数中注册。流媒体操作员操作员和操作人员将自动在注册系统中找到。Qt D-Bus为阵列和地图提供了模板专门化,用于Qt的容器类,如QMap和QList,因此没有必要为这些类编写流话务员函数。对于其他类型,特别是对于类型的实现结构,必须显式地实现运算符。