我为systemd-timedated编写了一个界面:
#include <QtDBus>
#include <dbus/dbus.h>
Q_DECLARE_LOGGING_CATEGORY(timeDateInterfaceCategory)
#define TIMEDATE_DBUS_SERVICE "org.freedesktop.timedate1"
#define TIMEDATE_DBUS_PATH "/org/freedesktop/timedate1"
#define TIMEDATE_DBUS_INTERFACE "org.freedesktop.timedate1"
class TimeDateInterface
: public QDBusAbstractInterface
{
Q_OBJECT
Q_PROPERTY(bool CanNTP MEMBER CanNTP NOTIFY CanNTPChanged)
Q_PROPERTY(bool LocalRTC MEMBER LocalRTC NOTIFY LocalRTCChanged)
Q_PROPERTY(bool NTP MEMBER NTP NOTIFY NTPChanged)
Q_PROPERTY(bool NTPSynchronized MEMBER NTPSynchronized NOTIFY NTPSynchronizedChanged)
Q_PROPERTY(qulonglong RTCTimeUSec MEMBER RTCTimeUSec NOTIFY RTCTimeUSecChanged)
Q_PROPERTY(qulonglong TimeUSec MEMBER TimeUSec NOTIFY TimeUSecChanged)
Q_PROPERTY(QString Timezone MEMBER Timezone NOTIFY TimezoneChanged)
public :
explicit
TimeDateInterface(QObject * const parent = Q_NULLPTR)
: QDBusAbstractInterface{{TIMEDATE_DBUS_SERVICE}, {TIMEDATE_DBUS_PATH}, TIMEDATE_DBUS_INTERFACE,
QDBusConnection::systemBus(),
parent}
{
qDBusRegisterMetaType< QVariantMap >();
if (!isValid()) {
qCCritical(timeDateInterfaceCategory).noquote()
<< tr("Unable to create interface %1: %2")
.arg(service(), lastError().message());
return;
}
if (!connection().connect({service()}, path(), {DBUS_INTERFACE_PROPERTIES}, {"PropertiesChanged"},
//QString::fromLatin1(QMetaObject::normalizedSignature("PropertiesChanged(QString,QVariantMap,QStringList)")),
this, SLOT(propertiesChanged(QString, QVariantMap, QStringList)))) {
Q_ASSERT(false);
}
}
public Q_SLOTS :
Q_SCRIPTABLE
void SetLocalRTC(bool localRtc, bool fixSystem, bool userInteraction)
{
const auto message = call(QDBus::BlockWithGui, {"SetLocalRTC"},
QVariant::fromValue(localRtc),
QVariant::fromValue(fixSystem),
QVariant::fromValue(userInteraction));
QDBusPendingReply<> pendingReply = message;
Q_ASSERT(pendingReply.isFinished());
if (pendingReply.isError()) {
qCWarning(timeDateInterfaceCategory).noquote()
<< tr("Asynchronous call finished with error: %1")
.arg(pendingReply.error().message());
return;
}
}
Q_SCRIPTABLE
void SetNTP(bool useNtp, bool userInteraction)
{
const auto message = call(QDBus::BlockWithGui, {"SetNTP"},
QVariant::fromValue(useNtp),
QVariant::fromValue(userInteraction));
QDBusPendingReply<> pendingReply = message;
Q_ASSERT(pendingReply.isFinished());
if (pendingReply.isError()) {
qCWarning(timeDateInterfaceCategory).noquote()
<< tr("Asynchronous call finished with error: %1")
.arg(pendingReply.error().message());
return;
}
}
Q_SCRIPTABLE
void SetTime(qlonglong usecUtc, bool relative, bool userInteraction)
{
const auto message = call(QDBus::BlockWithGui, {"SetTime"},
QVariant::fromValue(usecUtc),
QVariant::fromValue(relative),
QVariant::fromValue(userInteraction));
QDBusPendingReply<> pendingReply = message;
Q_ASSERT(pendingReply.isFinished());
if (pendingReply.isError()) {
qCWarning(timeDateInterfaceCategory).noquote()
<< tr("Asynchronous call finished with error: %1")
.arg(pendingReply.error().message());
return;
}
}
Q_SCRIPTABLE
void SetTimezone(QString timezone, bool userInteraction)
{
const auto message = call(QDBus::BlockWithGui, {"SetTimezone"},
QVariant::fromValue(timezone),
QVariant::fromValue(userInteraction));
QDBusPendingReply<> pendingReply = message;
Q_ASSERT(pendingReply.isFinished());
if (pendingReply.isError()) {
qCWarning(timeDateInterfaceCategory).noquote()
<< tr("Asynchronous call finished with error: %1")
.arg(pendingReply.error().message());
return;
}
}
private Q_SLOTS :
void propertyChanged(QString const & propertyName)
{
const auto signature = QStringLiteral("%1Changed()").arg(propertyName);
const int signalIndex = staticMetaObject.indexOfSignal(QMetaObject::normalizedSignature(qUtf8Printable(signature)).constData());
if (signalIndex < 0) {
qCCritical(timeDateInterfaceCategory).noquote()
<< tr("There is no signal with %1 signature")
.arg(signature);
return;
}
const auto signal = staticMetaObject.method(signalIndex);
if (!signal.invoke(this, Qt::DirectConnection)) {
qCCritical(timeDateInterfaceCategory).noquote()
<< tr("Unable to emit %1 signal for %2 property")
.arg(signature, propertyName);
}
}
void propertiesChanged(QString interfaceName, QVariantMap changedProperties, QStringList invalidatedProperties)
{
if (interfaceName != interface()) {
return;
}
QMapIterator< QString, QVariant > i{changedProperties};
while (i.hasNext()) {
propertyChanged(i.next().key());
}
for (QString const & invalidatedProperty : invalidatedProperties) {
propertyChanged(invalidatedProperty);
}
}
Q_SIGNALS :
void CanNTPChanged();
void LocalRTCChanged();
void NTPChanged();
void NTPSynchronizedChanged();
void RTCTimeUSecChanged();
void TimeUSecChanged();
void TimezoneChanged();
private :
bool CanNTP;
bool LocalRTC;
bool NTP;
bool NTPSynchronized;
qulonglong RTCTimeUSec;
qulonglong TimeUSec;
QString Timezone;
};
在我的系统qdbus --system org.freedesktop.timedate1 /org/freedesktop/timedate1
上:
method QString org.freedesktop.DBus.Peer.GetMachineId()
method void org.freedesktop.DBus.Peer.Ping()
method QString org.freedesktop.DBus.Introspectable.Introspect()
signal void org.freedesktop.DBus.Properties.PropertiesChanged(QString interface, QVariantMap changed_properties, QStringList invalidated_properties)
method QDBusVariant org.freedesktop.DBus.Properties.Get(QString interface, QString property)
method QVariantMap org.freedesktop.DBus.Properties.GetAll(QString interface)
method void org.freedesktop.DBus.Properties.Set(QString interface, QString property, QDBusVariant value)
property read bool org.freedesktop.timedate1.CanNTP
property read bool org.freedesktop.timedate1.LocalRTC
property read bool org.freedesktop.timedate1.NTP
property read bool org.freedesktop.timedate1.NTPSynchronized
property read qulonglong org.freedesktop.timedate1.RTCTimeUSec
property read qulonglong org.freedesktop.timedate1.TimeUSec
property read QString org.freedesktop.timedate1.Timezone
method void org.freedesktop.timedate1.SetLocalRTC(bool, bool, bool)
method void org.freedesktop.timedate1.SetNTP(bool, bool)
method void org.freedesktop.timedate1.SetTime(qlonglong, bool, bool)
method void org.freedesktop.timedate1.SetTimezone(QString, bool)
我认为使用PropertiesChanged
D-Bus 信号为每个属性发出somePropertyChanged
信号是很自然的。它刚刚连接到propertiesChanged
调度程序槽。我需要它来发出来自 QML -side singletone的somePropertyChanged
信号(反过来,它只是将 D-Bus 属性名称转换为 QML 属性名称(decapitalizing,从epoch到QDateTime
等从usecs的转换))。
Qt Property System到NOTIFY
信号原型中有要求:
MEMBER变量的NOTIFY信号必须采用零个或一个参数,该参数必须与属性的类型相同。
在somePropertyChanged
信号的 QML - 旁边处理程序中:
Connections {
target: CppSingleton
onSomePropertyChanged: {
if (someProperty) {
//
}
}
}
仅当someProperty
信号被声明为一元而非无效时,才能访问符号somePropertyChanged
。在信号处理程序中访问someProperty
非常方便。
当我发送PropertiesChanged
D-Bus 信号时,我有一个属性的名称。它允许创建信号的原型字符串"somePropertyChanged()"
以获得相应的QObject
方法索引。我也改变了属性的新值。但它被括在QVariant
中。反过来,QMetaMethod::invoke
会接受Q_ARG
个值。因此,我必须只使用nullary *Changed
信号。
引擎盖Q_ARG
只是一个QArgument
:一对typename(const char *
)和类型删除的值(void *
)。显然它是变体概念式的东西。我想将QVariant
转换为QGenericArgument
。一般情况下是否可能?
QVariant
与QGenericArgument
的构造函数类似:QVariant::QVariant(int typeId, const void *copy)
。但我想提取这两个值。我认为这是足够的(借助一些Qt的RTTI,例如QVariant
&#39;} const char *typeToName(int typeId)
)来实现所需。
QVariant
中有未记录的方法:
void *data();
const void *constData() const;
inline const void *data() const { return constData(); }
也许我可以使用它们。但是在这种情况下出现了所有权问题。
似乎QGenericArgument
是非拥有者。与Qt::DirectConnection
就地方法调用相结合,我可以使用QVariant::data
来访问QVaraint
的内部。
QDBus
包含QDBusVariant
/ QVariant
和QDBusArgument
内部转化/代表。似乎无法预测&#34; typeinfo&#34;为每个属性正确提取。 (可以使用未记录的qdbus_cast< T >(const QVariant &)
强制转换来解决。