D-BusによるQtのカスタムタイプ

画像 ハブには、QtのD-Busに関する記事( time )と、わずかに影響するユーザータイプ( 2 )がありました。 ここでは、ユーザータイプの転送、関連機能、回避策の実装を検討します。

この記事はメモのように見えますが、自分自身と同僚の両方のためにスニペットの小さなスプラッシュがあります。

注:Qt 4.7(これはSqueezeに感謝します)の下で研究されているため、一部の手順は役に立たない可能性があります。





はじめに



不要なジェスチャを必要としない転送の標準タイプは、 ドックにあります。 D-Busを介してQVariantタイプ( QDBusVariant )を送信することもできます。 これにより、QVariantがコンストラクターで受け入れることができる型をQRectからQVariantListおよびQVariantMapに転送できます(2次元配列は期待どおりに機能しません)。 タイプをQVariantに変換することでタイプを渡すのは魅力的です。 個人的には、受信側はいくつかの異なるタイプを区別できないため、この方法を放棄することをお勧めします-それらはすべてQVariantです。 私の意見では、これは潜在的にエラーを引き起こし、サポートを複雑にする可能性があります。



タイプを調理する



最初に、アプリケーションで使用されるタイプについて説明します。

最初のタイプはMoneyです

[お金]
struct Money { int summ; QString type; Money() : summ(0) , type() {} };
      
      





まず、型システムで宣言する必要があります。

<Q_DECLARE_METATYPE(Money)>





型の転送を開始する前に、関数を呼び出して型を登録する必要があります

<qRegisterMetaType<Money;>("Money");

qDBusRegisterMetaType<Money;>();>






D-Busタイプを介して送信できるようにするには、解析して標準タイプに収集するためのメソッドを追加する必要があります (マーシャリングとデマーシャリング)。

マーシャリングとデマーシャリング
 friend QDBusArgument& operator <<(QDBusArgument& argument, const Money& arg) { argument.beginStructure(); argument << arg.summ; argument << arg.type; argument.endStructure(); return argument; } friend const QDBusArgument& operator >>(const QDBusArgument& argument, Money& arg) { argument.beginStructure(); argument >> arg.summ; argument >> arg.type; argument.endStructure(); return argument; }
      
      





そんなに退屈しないように、いくつかのタイプを追加します。 完全にタイプしたファイルは次のようになります。

[types.h]
 #include <QString> #include <QDateTime> #include <QMap> #include <QMetaType> #include <QtDBus> //   D-Bus     namespace dbus { static QString serviceName() { return "org.student.interface"; } static QString servicePath() { return "/org/student/interface"; } } struct Money { int summ; QString type; Money() : summ(0) , type() {} friend QDBusArgument &operator<<(QDBusArgument &argument, const Money &arg); friend const QDBusArgument &operator>>(const QDBusArgument &argument, Money &arg); }; Q_DECLARE_METATYPE(Money) struct Letter { Money summ; QString text; QDateTime letterDate; Letter() : summ() , text() , letterDate() {} friend QDBusArgument &operator<<(QDBusArgument &argument, const Letter &arg); friend const QDBusArgument &operator>>(const QDBusArgument &argument, Letter &arg); }; Q_DECLARE_METATYPE(Letter) //      typedef QList<QVariant> Stuff; Q_DECLARE_METATYPE(Stuff) struct Parcel { Stuff someFood; Letter letter; Parcel() : someFood() , letter() {} friend QDBusArgument &operator<<(QDBusArgument &argument, const Parcel &arg); friend const QDBusArgument &operator>>(const QDBusArgument &argument, Parcel &arg); }; Q_DECLARE_METATYPE(Parcel)
      
      





[types.cpp]
 #include "types.h" #include <QMetaType> #include <QtDBus> //    static struct RegisterTypes { RegisterTypes() { qRegisterMetaType<Money>("Money"); qDBusRegisterMetaType<Money>(); qRegisterMetaType<Letter>("Letter"); qDBusRegisterMetaType<Letter>(); qRegisterMetaType<Stuff>("Stuff"); qDBusRegisterMetaType<Stuff>(); qRegisterMetaType<Parcel>("Parcel"); qDBusRegisterMetaType<Parcel>(); } } RegisterTypes; //------------------------ QDBusArgument& operator <<(QDBusArgument& argument, const Money& arg) { argument.beginStructure(); argument << arg.summ; argument << arg.type; argument.endStructure(); return argument; } const QDBusArgument& operator >>(const QDBusArgument& argument, Money& arg) { argument.beginStructure(); argument >> arg.summ; argument >> arg.type; argument.endStructure(); return argument; } //------------------------ QDBusArgument& operator <<(QDBusArgument& argument, const Letter& arg) { argument.beginStructure(); argument << arg.summ; argument << arg.text; argument << arg.letterDate; argument.endStructure(); return argument; } const QDBusArgument& operator >>(const QDBusArgument& argument, Letter& arg) { argument.beginStructure(); argument >> arg.summ; argument >> arg.text; argument >> arg.letterDate; argument.endStructure(); return argument; } //------------------------ QDBusArgument& operator <<(QDBusArgument& argument, const Parcel& arg) { argument.beginStructure(); argument << arg.someFood; argument << arg.letter; argument.endStructure(); return argument; } const QDBusArgument& operator >>(const QDBusArgument& argument, Parcel& arg) { argument.beginStructure(); argument >> arg.someFood; argument >> arg.letter; argument.endStructure(); return argument; }
      
      





配列を使用するにはQListを使用でき、変数の変換が既に存在する場合はマーシャリングとアンマーシャリングを必要としません。



構築を開始



D-Busで通信する必要がある2つのQtアプリケーションがあるとします。 1つのアプリケーションはサービスとして登録され、2番目のアプリケーションはこのサービスと対話します。



私は怠け者で、あまりにも怠け者で、別のQDBusアダプタを作成できません。 したがって、内部メソッドとD-Busインターフェイスを分離するために、インターフェイスメソッドをQ_SCRIPTABLEマクロでマークします。

[student.h]
 #include <QObject> #include "../lib/types.h" class Student : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.student.interface") public: Student(QObject *parent = 0); ~Student(); signals: Q_SCRIPTABLE Q_NOREPLY void needHelp(Letter reason); void parcelRecived(QString parcelDescription); public slots: Q_SCRIPTABLE void reciveParcel(Parcel parcelFromParents); void sendLetterToParents(QString letterText); private: void registerService(); };
      
      





Q_NOREPLYタグは、D-Busがメソッドからの応答を待つべきではないことを示します。

Q_SCRIPTABLEとマークされたメソッドでサービスを登録するには、次のコードが使用されます。

[サービス登録]
 void Student::registerService() { QDBusConnection connection = QDBusConnection::connectToBus(QDBusConnection::SessionBus, dbus::serviceName()); if (!connection.isConnected()) qDebug()<<(QString("DBus connect false")); else qDebug()<<(QString("DBus connect is successfully")); if (!connection.registerObject(dbus::servicePath(), this, QDBusConnection::ExportScriptableContents)) { qDebug()<<(QString("DBus register object false. Error: %1").arg(connection.lastError().message())); } else qDebug()<<(QString("DBus register object successfully")); if (!connection.registerService(dbus::serviceName())) { qDebug()<<(QString("DBus register service false. Error: %1").arg(connection.lastError().message())); } else qDebug()<<(QString("DBus register service successfully")); }
      
      





完全なcppファイルは次のようになります。

[student.cpp]
 #include "student.h" #include <QDBusConnection> #include <QDebug> #include <QDBusError> Student::Student(QObject *parent) : QObject(parent) { registerService(); } Student::~Student() { } void Student::reciveParcel(Parcel parcelFromParents) { QString letterText = parcelFromParents.letter.text; letterText.append(QString("\n Money: %1 %2").arg(parcelFromParents.letter.summ.summ).arg(parcelFromParents.letter.summ.type)); Stuff sendedStuff = parcelFromParents.someFood; QString stuffText; foreach(QVariant food, sendedStuff) { stuffText.append(QString("Stuff: %1\n").arg(food.toString())); } QString parcelDescription; parcelDescription.append(letterText); parcelDescription.append("\n"); parcelDescription.append(stuffText); emit parcelRecived(parcelDescription); } void Student::sendLetterToParents(QString letterText) { Letter letterToParents; letterToParents.text = letterText; letterToParents.letterDate = QDateTime::currentDateTime(); emit needHelp(letterToParents); } void Student::registerService() { QDBusConnection connection = QDBusConnection::connectToBus(QDBusConnection::SessionBus, dbus::serviceName()); if (!connection.isConnected()) qDebug()<<(QString("DBus connect false")); else qDebug()<<(QString("DBus connect is successfully")); if (!connection.registerObject(dbus::servicePath(), this, QDBusConnection::ExportScriptableContents)) { qDebug()<<(QString("DBus register object false. Error: %1").arg(connection.lastError().message())); } else qDebug()<<(QString("DBus register object successfully")); if (!connection.registerService(dbus::serviceName())) { qDebug()<<(QString("DBus register service false. Error: %1").arg(connection.lastError().message())); } else qDebug()<<(QString("DBus register service successfully")); }
      
      





このクラスは、使い慣れた構造を使用してD-Busで正常に機能します。

インターフェイスのメソッドを呼び出すには、QDBusConnection :: send:を使用できます。

[応答なしでD-Busメソッドを呼び出す]
 const QString studentMethod = "reciveParcel"; QDBusMessage sendParcel = QDBusMessage::createMethodCall(dbus::serviceName(), dbus::servicePath(), "", studentMethod); QList<QVariant> arg; arg.append(qVariantFromValue(parentsParcel)); sendParcel.setArguments(arg); if ( !QDBusConnection::sessionBus().send(sendParcel) ) { qDebug()<<QString("D-bus %1 calling error: %2").arg(studentMethod).arg(QDBusConnection::sessionBus().lastError().message()); }
      
      





ブラックマジック、ボイドポインター、および型を登録したという事実を使用するqVariantFromValueメソッドは、QVariantに変換します。 QVariant :: valueメソッドテンプレートまたはqvariant_castから取得できます。



メソッド応答が必要な場合は、他のQDBusConnectionメソッドを使用できます-同期呼び出しと非同期callWithCallback、asyncCallに。

[D-Busメソッドの同期呼び出し、応答を待機]
 const QString studentMethod = "reciveParcel"; QDBusMessage sendParcel = QDBusMessage::createMethodCall(dbus::serviceName(), dbus::servicePath(), "", studentMethod); QList<QVariant> arg; arg.append(qVariantFromValue(parentsParcel)); sendParcel.setArguments(arg); int timeout = 25; //   ,    - 25  QDBusReply<int> reply = QDBusConnection::sessionBus().call(sendParcel, QDBus::Block, timeout); //QDBus::Block   (event loop)    if (!reply.isValid()) { qDebug()<<QString("D-bus %1 calling error: %2").arg(studentMethod).arg(QDBusConnection::sessionBus().lastError().message()); } int returnedValue = reply.value();
      
      





[D-Busメソッドの非同期呼び出し]
 const QString studentMethod = "reciveParcel"; QDBusMessage sendParcel = QDBusMessage::createMethodCall(dbus::serviceName(), dbus::servicePath(), "", studentMethod); QList<QVariant> arg; arg.append(qVariantFromValue(parentsParcel)); sendParcel.setArguments(arg); int timeout = 25; //   ,    - 25  bool isCalled = QDBusConnection::sessionBus().callWithCallback(sendParcel, this, SLOT(standartSlot(int)), SLOT(errorHandlerSlot(const QDBusMessage&)), timeout) if (!isCalled) { qDebug()<<QString("D-bus %1 calling error: %2").arg(studentMethod).arg(QDBusConnection::sessionBus().lastError().message()); }
      
      





QDBusMessageが関与しないQDBusAbstractInterfaceクラスのメソッドを使用することもできます。

ちなみに、信号を送信するには、インターフェイスを登録する必要はありません。同じ送信方法を使用して送信できます。

[信号送信]
 QDBusMessage msg = QDBusMessage::createSignal(dbus::servicePath(), dbus::serviceName(), "someSignal"); msg << signalArgument; QDBusConnection::sessionBus().send(msg);
      
      





例に戻りましょう。 以下は、Studentクラスインターフェイスと対話するクラスです。

[parents.h]
 #include <QObject> #include "../lib/types.h" class Parents : public QObject { Q_OBJECT public: Parents(QObject *parent = 0); ~Parents(); private slots: void reciveLetter(const Letter letterFromStudent); private: void connectToDBusSignal(); void sendHelpToChild(const Letter letterFromStudent) const; void sendParcel(const Parcel parentsParcel) const; Letter writeLetter(const Letter letterFromStudent) const; Stuff poskrestiPoSusekam() const; };
      
      





[parents.cpp]
 #include "parents.h" #include <QDBusConnection> #include <QDebug> Parents::Parents(QObject *parent) : QObject(parent) { connectToDBusSignal(); } Parents::~Parents() { } void Parents::reciveLetter(const Letter letterFromStudent) { qDebug()<<"Letter recived: "; qDebug()<<"Letter text: "<<letterFromStudent.text; qDebug()<<"Letter date: "<<letterFromStudent.letterDate; sendHelpToChild(letterFromStudent); } void Parents::connectToDBusSignal() { bool isConnected = QDBusConnection::sessionBus().connect( "", dbus::servicePath(), dbus::serviceName(), "needHelp", this, SLOT(reciveLetter(Letter))); if(!isConnected) qDebug()<<"Can't connect to needHelp signal"; else qDebug()<<"connect to needHelp signal"; } void Parents::sendHelpToChild(const Letter letterFromStudent) const { Parcel preparingParcel; preparingParcel.letter = writeLetter(letterFromStudent); preparingParcel.someFood = poskrestiPoSusekam(); sendParcel(preparingParcel); } void Parents::sendParcel(const Parcel parentsParcel) const { const QString studentMethod = "reciveParcel"; QDBusMessage sendParcel = QDBusMessage::createMethodCall(dbus::serviceName(), dbus::servicePath(), "", studentMethod); QList<QVariant> arg; arg.append(qVariantFromValue(parentsParcel)); sendParcel.setArguments(arg); if ( !QDBusConnection::sessionBus().send( sendParcel) ) { qDebug()<<QString("D-bus %1 calling error: %2").arg(studentMethod).arg(QDBusConnection::sessionBus().lastError().message()); } } Letter Parents::writeLetter(const Letter letterFromStudent) const { QString text = "We read about you problem so send some help"; Letter parentLetter; parentLetter.text = text; Money summ; summ.summ = letterFromStudent.text.count(",")*100; summ.summ += letterFromStudent.text.count(".")*50; summ.summ += letterFromStudent.text.count(" ")*5; summ.type = "USD"; parentLetter.summ = summ; parentLetter.letterDate = QDateTime::currentDateTime(); return parentLetter; } Stuff Parents::poskrestiPoSusekam() const { Stuff food; food<<"Russian donuts"; food<<"Meat dumplings"; return food; }
      
      





こちらからサンプルをダウンロードできます。

物事がそれほどスムーズに行かない場合



開発中に問題が発生しました。ユーザータイプでプログラムのD-Busインターフェイスにアクセスすると、プログラムがクラッシュしました。 これはすべて、Q_CLASSINFOマクロを使用してクラスのインターフェイスのxml記述を追加することで解決しました。 上記の例では、次のようになります。

[student.h]
 class Student : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.student.interface") Q_CLASSINFO("D-Bus Introspection", "" "<interface name=\"org.student.interface\">\n" " <signal name=\"needHelp\">\n" " <arg name=\"reason\" type=\"((is)s((iii)(iiii)i))\" direction=\"out\"/>\n" " <annotation name=\"com.chameleon.QtDBus.QtTypeName.Out0\" value=\"Letter\"/>\n" " </signal>\n" " <method name=\"reciveParcel\">\n" " <arg name=\"parcelFromParents\" type=\"(av((is)s((iii)(iiii)i)))\" direction=\"in\"/>\n" " <annotation name=\"org.qtproject.QtDBus.QtTypeName.In0\" value=\"Parcel\"/>\n" " <annotation name=\"org.freedesktop.DBus.Method.NoReply\" value=\"true\"/>\n" " </method>\n" ) public: Student(QObject *parent = 0); …
      
      





引数の型パラメーターはその署名であり、D-Bus仕様に従って記述さます。 型のマーシャリングがある場合は、QDBusArgumentのドキュメント化されていない機能、つまりcurrentSignature()メソッドを使用して署名を見つけることができます。

[タイプシグネチャの取得]
 QDBusArgument arg; arg<<Parcel(); qDebug()<<"Parcel signature: "<<arg.currentSignature();
      
      







ユーザーインターフェイスのテスト


信号テスト


シグナルをテストするには、qdbusviewerを使用できます。シグナルに接続して、送信する構造の種類を表示できます。 また、dbus-monitorはこれに適している場合があります。アドレスを指定すると、すべての発信インターフェイスメッセージが表示されます。



メソッドテスト


qdbusviewerは、カスタムタイプのメソッドを呼び出しません。 これらの目的のために、dフィートを使用できます。 彼がわかりやすいドキュメントを見つけるのは難しいという事実にもかかわらず、彼はどんな複雑なタイプのメソッドを呼び出すかを知っています。 それを使用する場合、いくつかの機能を考慮する必要があります。

[Dフィートの使用]
変数はコンマで区切られます。



主なタイプ(括弧内は署名の指定です):

int(i)-数値(例:42);

bool(b)-1または0;

double(d)-ドット付きの数値(例:3.1415);

string(s)-引用符で囲まれた文字列(例:「string」);

構造は括弧「(」と「)」で囲まれ、変数はコンマで区切られます。構造に要素が1つある場合でもコンマを設定する必要があります。

配列-角括弧「[」および「]」、コンマで区切られた変数。



Variant型とDict型は必要なかったため、勉強しませんでした。





ご清聴ありがとうございました。



使用材料:

QtDbus-謎に包まれた闇。 パート1パート2

Qtドキュメント

D-Bus仕様

一般的なKDE D-Busチュートリアル 、特にCustomTypes



All Articles