从QVariant

时间:2017-08-31 06:55:48

标签: c++ qt signals-slots dbus qvariant

我为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 SystemNOTIFY信号原型中有要求:

  

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。一般情况下是否可能?

QVariantQGenericArgument的构造函数类似: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(); }

也许我可以使用它们。但是在这种情况下出现了所有权问题。

附加2:

似乎QGenericArgument是非拥有者。与Qt::DirectConnection就地方法调用相结合,我可以使用QVariant::data来访问QVaraint的内部。

附加3:

QDBus包含QDBusVariant / QVariantQDBusArgument内部转化/代表。似乎无法预测&#34; typeinfo&#34;为每个属性正确提取。 (可以使用未记录的qdbus_cast< T >(const QVariant &)强制转换来解决。

0 个答案:

没有答案