डी-बस द्वारा क्यूटी में कस्टम प्रकार

छवि एक हब पर डी-बस के बारे में क्यूटी ( समय ) और थोड़ा प्रभावित उपयोगकर्ता प्रकार ( दो ) के लेख थे। यहां हम उपयोगकर्ता प्रकारों के हस्तांतरण, संबंधित सुविधाओं, कार्यदलों के कार्यान्वयन पर विचार करेंगे।

लेख मेमो की तरह दिखेगा, स्निपेट्स के एक छोटे से छप के साथ, खुद के लिए और अपने सहयोगियों के लिए।

नोट: Qt 4.7 के तहत अध्ययन किया गया (इसके लिए धन्यवाद करने के लिए धन्यवाद ...), इसलिए कुछ क्रियाएं उपयोगी नहीं हो सकती हैं।





परिचय



हस्तांतरण के लिए मानक प्रकार जिनके अनावश्यक इशारों की आवश्यकता नहीं है, वे डॉक में हैं । D-Bus के माध्यम से QVariant प्रकार ( QDBusVariant ) संचारित करना भी संभव है। यह आपको उन प्रकारों को स्थानांतरित करने की अनुमति देता है जो QVariant कंस्ट्रक्टर में स्वीकार कर सकते हैं - क्यूआरके से QVariantList और QVariantMap (दो आयामी सरणियों अपेक्षा के अनुरूप काम नहीं करते हैं)। यह आपके प्रकारों को QVariant में परिवर्तित करके लुभाना है। व्यक्तिगत रूप से, मैं इस पद्धति को छोड़ने की सलाह दूंगा, क्योंकि प्राप्त पक्ष कई अलग-अलग प्रकारों के बीच अंतर करने में सक्षम नहीं होगा - वे सभी इसके लिए QVariant होंगे। मेरी राय में, यह संभावित रूप से त्रुटियों को जन्म दे सकता है और समर्थन को जटिल कर सकता है।



अपने प्रकार खाना बनाना



पहले, हम उन प्रकारों का वर्णन करते हैं जो अनुप्रयोगों में उपयोग किए जाएंगे।

पहला प्रकार मनी होगा

[मनी]
struct Money { int summ; QString type; Money() : summ(0) , type() {} };
      
      





पहले आपको इसे टाइप सिस्टम में घोषित करने की आवश्यकता है:

<Q_DECLARE_METATYPE(Money)>





टाइप ट्रांसफर शुरू करने से पहले, आपको टाइप रजिस्टर करने के लिए फंक्शन्स को कॉल करना होगा

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

qDBusRegisterMetaType<Money;>();>






डी-बस प्रकार के माध्यम से इसे प्रसारित करने में सक्षम होने के लिए, आपको इसे मानक प्रकारों में पार्स करने और इकट्ठा करने के लिए तरीकों को जोड़ने की आवश्यकता है (मार्शालिंग और डीमर्शालिंग)।

मार्शलिंग और डेमोर्शलिंग
 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 का उपयोग कर सकते हैं और यदि चर के लिए पहले से ही परिवर्तन हो रहे हों तो उन्हें मार्शलिंग और अनमर्शलिंग की आवश्यकता नहीं होती है।



निर्माण शुरू करें



मान लीजिए कि दो क्यूटी एप्लिकेशन हैं जिन्हें डी-बस पर संचार करने की आवश्यकता है। एक आवेदन एक सेवा के रूप में पंजीकृत किया जाएगा, और दूसरा इस सेवा के साथ बातचीत करेगा।



मैं एक अलग QDBus एडाप्टर बनाने के लिए आलसी और बहुत आलसी हूँ। इसलिए, आंतरिक विधियों और डी-बस इंटरफ़ेस को अलग करने के लिए, मैं 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 टैग इंगित करता है कि डी-बस को विधि से प्रतिक्रिया की प्रतीक्षा नहीं करनी चाहिए।

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")); }
      
      





यह वर्ग परिचित निर्माणों का उपयोग करके डी-बस पर सफलतापूर्वक काम कर सकता है।

इसके इंटरफ़ेस की विधि को कॉल करने के लिए, आप QDBusConnection :: send का उपयोग कर सकते हैं:

[कॉल डी-बस विधि प्रतिक्रिया के बिना]
 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 :: मान विधि टेम्पलेट या qvariant_cast के माध्यम से वापस प्राप्त कर सकते हैं।



यदि आपको एक विधि प्रतिक्रिया की आवश्यकता है, तो आप अन्य QDBusConnection विधियों का उपयोग कर सकते हैं - सिंक्रोनस कॉल के लिए और एसिंक्रोनस कॉलविथकॉलबैक, asyncCall के लिए।

[डी-बस पद्धति का सिंक्रोनस कॉल, प्रतिक्रिया की प्रतीक्षा में]
 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();
      
      





[डी-बस पद्धति का अतुल्यकालिक कॉल]
 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()); }
      
      





आप QDBusAbstractInterface वर्ग के तरीकों का भी उपयोग कर सकते हैं, जिसमें QDBusMessage शामिल नहीं है।

वैसे, संकेतों को भेजने के लिए इंटरफ़ेस को पंजीकृत करने की आवश्यकता नहीं है, उन्हें उसी भेजने की विधि का उपयोग करके भेजा जा सकता है:

[सिग्नल भेजें]
 QDBusMessage msg = QDBusMessage::createSignal(dbus::servicePath(), dbus::serviceName(), "someSignal"); msg << signalArgument; QDBusConnection::sessionBus().send(msg);
      
      





आइए उदाहरण पर वापस जाएं। नीचे एक वर्ग है जो छात्र वर्ग इंटरफ़ेस के साथ सहभागिता करता है।

[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; }
      
      





आप यहां से एक उदाहरण डाउनलोड कर सकते हैं

अगर चीजें इतनी आसानी से नहीं होती हैं



विकास के दौरान, मुझे एक समस्या थी: उपयोगकर्ता के प्रकारों के साथ प्रोग्राम के डी-बस इंटरफ़ेस तक पहुंचने पर, प्रोग्राम क्रैश हो गया। यह 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); …
      
      





तर्कों का प्रकार पैरामीटर उनका हस्ताक्षर है; यह डी-बस विनिर्देश के अनुसार वर्णित है। यदि आपके पास मार्शल टाइपिंग है, तो आप QDBusArgument की अनिर्दिष्ट सुविधाओं का उपयोग करके इसके हस्ताक्षर का पता लगा सकते हैं, अर्थात् इसकी वर्तमान स्थिति () विधि।

[एक प्रकार का हस्ताक्षर प्राप्त करना]
 QDBusArgument arg; arg<<Parcel(); qDebug()<<"Parcel signature: "<<arg.currentSignature();
      
      







उपयोगकर्ता इंटरफ़ेस परीक्षण


संकेत परीक्षण


संकेतों का परीक्षण करने के लिए, आप qdbusviewer का उपयोग कर सकते हैं - यह सिग्नल से जुड़ सकता है और दिखा सकता है कि यह किस प्रकार की संरचना भेजता है। इसके अलावा, डबस-मॉनिटर इसके लिए उपयुक्त हो सकता है - पते को निर्दिष्ट करने के बाद, यह सभी आउटगोइंग इंटरफ़ेस संदेश दिखाएगा।



विधि परीक्षण


क्यूडब्यूव्यूअर कस्टम प्रकारों के साथ तरीकों को नहीं कहता है। इन उद्देश्यों के लिए, आप डी-फीट का उपयोग कर सकते हैं। इस तथ्य के बावजूद कि उसके लिए समझदारीपूर्ण दस्तावेज खोजना मुश्किल है, वह जानता है कि किसी भी जटिलता के प्रकार के साथ तरीकों को कैसे कॉल किया जाए। इसके साथ काम करते समय, आपको कुछ विशेषताओं पर विचार करने की आवश्यकता है:

[डी-फीट के साथ काम करें]
चर को अल्पविराम द्वारा अलग किया जाता है।



मुख्य प्रकार (कोष्ठक में हस्ताक्षर में पदनाम हैं):

int (i) - संख्या (उदाहरण: 42);

बूल (बी) - 1 या 0;

डबल (डी) - एक डॉट के साथ एक संख्या (उदाहरण: 3.1415);

string (s) - उद्धरण चिह्नों में एक स्ट्रिंग (उदाहरण: "string");

संरचनाएं कोष्ठक में ले ली जाती हैं "(" और "), चर को अल्पविराम से अलग किया जाता है, संरचना में एक तत्व होने पर भी अल्पविराम सेट किया जाना चाहिए।

Arrays - वर्ग कोष्ठक "[" और "]", चर अल्पविराम द्वारा अलग किए गए।



टाइप वेरिएंट और डिक्ट ने अध्ययन नहीं किया, क्योंकि यह आवश्यक नहीं था।





आपका ध्यान देने के लिए धन्यवाद।



प्रयुक्त सामग्री:

QtDbus - रहस्य में छाया हुआ अंधेरा। भाग १ , भाग २

क्यूटी डॉक्स

डी-बस विशिष्टता

विशेष रूप से सामान्य और सीमा शुल्क में केडीई डी-बस ट्यूटोरियल



All Articles