📜 ⬆️ ⬇️

QtDbus Part 2. The Victory Lights of Pure Mind

The previous part left gloomy predictions, but everything turned out to be much, much, much better.

Parsing Bonus Level
So, thanks to KOL93, he noticed that QDbusAbstractAdaptor must have a parent. For me it turned out to be news, because I am very used to the fact that there may not be a parent. Maybe it will save someone else from such a blooper.

In addition, my ignorance of patterns, there is such a patent, “Adapter”. So QAbstractAdaptor itself is what it is. In view of the above, and the observation of the docks that this class should be a lightweight example, the truly clarifying approach should be:
Pong Project:
main.cpp
#include <QCoreApplication> #include <QDBusConnection> #include <QDBusError> #include <QDebug> #include "Pong.h" #include "../serviceNameAndProperty.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QDBusConnection connection = QDBusConnection::sessionBus(); Pong pong; if( ! connection.registerObject("/", &pong)){ fprintf(stderr, "%s\n", qPrintable("Can't register object")); exit(1); } qDebug()<<"Pong connected to D-bus"; if (!connection.registerService(SERVICE_NAME)) { fprintf(stderr, "%s\n", qPrintable(QDBusConnection::sessionBus().lastError().message())); exit(1); } qDebug()<<"Test service start"; return a.exec(); } 
Pong.h
 #ifndef PONG_H #define PONG_H #include <QDBusAbstractAdaptor> #include <QDBusVariant> #include <QDBusArgument> #include <QDBusContext> #include "../serviceNameAndProperty.h" class Pong; class PongAdapter : public QDBusAbstractAdaptor { Q_OBJECT Q_CLASSINFO("D-Bus Interface", BUFFER_NAME) Q_PROPERTY(QString IMAGE_DATA_SHARED_ID READ imageDataSharedId) public: explicit PongAdapter(Pong *parent); QString imageDataSharedId(); public slots: TestStructure structureField(); signals: void callingMe(QString, QString); private: Pong * m_parentPong; }; class Pong : public QObject, public QDBusContext { Q_OBJECT public: Pong() { m_pongAdapter = new PongAdapter(this); QObject::connect(this, SIGNAL(callingMe(QString,QString)), m_pongAdapter, SIGNAL(callingMe(QString, QString))); m_imageDataSharedId = "testImageBufferId"; } public: QString imageDataSharedId(); TestStructure& structureField(); signals: void callingMe(QString, QString); private: PongAdapter *m_pongAdapter; QString m_imageDataSharedId; TestStructure test; }; #endif // PONG_H> 
Pong.cpp
 #include "Pong.h" #include <QDebug> #include <QDBusMetaType> #include <QDBusConnection> #include <QDBusMessage> PongAdapter::PongAdapter(Pong *parent) : QDBusAbstractAdaptor(parent) { m_parentPong = parent; qRegisterMetaType<TestStructure>("TestStructure"); qDBusRegisterMetaType<TestStructure>(); } QString PongAdapter::imageDataSharedId() { return m_parentPong->imageDataSharedId(); } TestStructure PongAdapter::structureField() { return m_parentPong->structureField(); } QString Pong::imageDataSharedId() { return m_imageDataSharedId; } TestStructure &Pong::structureField() { qDebug()<<"Me calld"<<QDBusConnection::sessionBus().baseService()<<message().service(); emit callingMe(QString("Panic"), QString("Super panic")); test.str = QString("ku"); test.id =2; return test; } 
Ping Project:
main.cpp
 #include <stdio.h> #include <QObject> #include <QCoreApplication> #include <QDBusConnection> #include <QDBusConnectionInterface> #include <QDBusServiceWatcher> #include <QDebug> #include "Ping.h" #include "../serviceNameAndProperty.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); Ping ping; if (!QDBusConnection::sessionBus().isConnected()) { fprintf(stderr, "Cannot connect to the D-Bus session bus.\n" "To start it, run:\n" "\teval `dbus-launch --auto-syntax`\n"); return 1; } qDebug()<<"Ping connected to D-bus"; QDBusConnectionInterface *iface = QDBusConnection::sessionBus().interface(); QDBusServiceWatcher watcher; watcher.setConnection(QDBusConnection::sessionBus());; watcher.addWatchedService(ping.m_aviableServiceName); // QObject::connect(&watcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)),&ping, SLOT(manageConnection(QString,QString,QString))); QObject::connect(&watcher, SIGNAL(serviceRegistered(QString)), &ping, SLOT(connectToService(QString))); QStringList registedServices = iface->registeredServiceNames(); if(registedServices.contains(ping.m_aviableServiceName)) ping.connectToService(ping.m_aviableServiceName); return a.exec(); 

}
Ping.h
 #ifndef PING_H #define PING_H #include <QObject> #include <QDBusAbstractInterface> #include <qdbusinterface.h> class Ping : public QObject { Q_OBJECT public: explicit Ping(QObject *parent = 0); public slots: void manageConnection(const QString &name, const QString &oldVAlue, const QString &newValue); void connectToService(const QString &name); void disconnect(const QString &name); void reacoOnMeCalling(QString message, QString message2); public: QString m_aviableServiceName; private: QDBusInterface *m_interface; QString m_interfaceName; static const QString _propertyName; }; #endif // PING_H 

Ping.cpp
 #include "Ping.h" #include "../serviceNameAndProperty.h" #include <QDBusConnectionInterface> #include <QDebug> #include <QDBusMetaType> const QString Ping::_propertyName(QUIOTING(IMAGE_DATA_SHARED_ID)); Ping::Ping(QObject *parent) : QObject(parent) { m_interface = NULL; m_interfaceName = QString(BUFFER_NAME); m_aviableServiceName = QString(SERVICE_NAME); qRegisterMetaType<TestStructure>("TestStructure"); qDBusRegisterMetaType<TestStructure>(); } void Ping::manageConnection(const QString& name, const QString &oldVAlue, const QString &newValue) { Q_UNUSED(oldVAlue) if(name != m_aviableServiceName) return; if(newValue.isEmpty()) disconnect(name); else connectToService(name); } void Ping::connectToService(const QString &name) { if(name != m_aviableServiceName) return; qDebug()<<"Connceting"; m_interface = new QDBusInterface(name, "/", m_interfaceName, QDBusConnection::sessionBus(), this); QObject::connect(m_interface, SIGNAL(callingMe(QString, QString)), this, SLOT(reacoOnMeCalling(QString, QString))); if(!m_interface->isValid()){ qDebug()<<"Invalid interface"<<m_interface->lastError(); delete m_interface; m_interface = NULL; return; } qDebug()<<m_interface->interface(); QVariant var("sss"); var = m_interface->property("imageDataSharedId"); qDebug()<<var; QDBusReply<TestStructure> reply= m_interface->call("structureField"); if(reply.isValid()) { TestStructure testStructure = reply.value(); qDebug()<<testStructure.id<<testStructure.str; } } void Ping::disconnect(const QString &name) { if(name != m_aviableServiceName) return; if(name != m_interface->service()) return; delete m_interface; m_interface = NULL; qDebug()<<"Disconnect"; } void Ping::reacoOnMeCalling(QString message, QString message2) { qDebug()<<message<<message2; } 

General serviceNameAndProperty.h file
 #pragma once #include<QMetaType> #include <QString> #include <QDBusArgument> #define SERVICE_NAME "ru.sonarh.dbus.pong" #define BUFFER_NAME "ru.buffer" #define IMAGE_DATA_SHARED_ID imageDataSharedId #define QUIOTING(text) #text struct TestStructure{ int id; QString str; }; Q_DECLARE_METATYPE(TestStructure) static QDBusArgument& operator <<(QDBusArgument &argument, const TestStructure & arg) { argument.beginStructure(); argument<<arg.id<<arg.str; argument.endStructure(); return argument; } static const QDBusArgument& operator >>(const QDBusArgument &argument, TestStructure & arg) { argument.beginStructure(); argument>>arg.id>>arg.str; argument.endStructure(); return argument; } 

And yet, how to use it?
For the sake of the integrity of the submission, how to use the module let me repeat the passage. The blessing of repetition is the mother of learning.
So, we have a Pong class, we want to make it available for D-Bus interaction. What you need for this:
  1. Connect to the D-Bus daemon. It can be anywhere, but for standard tires there are already ready static methods. In our example, this call goes immediately with a check.
     if (!QDBusConnection::sessionBus().isConnected()) { 
  2. For the sake of convenience and certainty, you can assign a name to the service, similar to DNS. We do this for the Pong project:
     if (!connection.registerService(SERVICE_NAME)) { 
    Important! it must contain at least one separating character - a point.
  3. Create an adapter whose parent will be Pong. This adapter will receive signals from the outside and redirect them to its parent, exactly like in the opposite direction. I’ll also emphasize that according to the ideas of designers, the adapter is really an adapter and with its help it is enough to simply expand an existing application. This is how the signal from the program goes to the D-Bus world:
     QObject::connect(this, SIGNAL(callingMe(QString,QString)), m_pongAdapter, SIGNAL(callingMe(QString, QString))); 
    Important! The interface name of the adapter specified in Q_ClASSINFO must contain a period.
  4. Register Pong by no one. It looks like this:
      if( ! connection.registerObject("/", &pong)){ 
    Important! This path must contain at least one separating character - /
An interesting twist in this story is that if we want to know the context of the message that activated the slot, then we need to inherit the real object (Pong) from QDbusContext , but at the same time, the context methods, ala message, need to be pulled in the adapter, if we We still want to get rid of D-Bus and not get a headache when testing. Quite a strange turn, but so far with the inconvenience caused by it, I have not encountered.
')
And since we have already started talking about the context, it is impossible not to mention the heart of the whole module. And the heart is two-chamber: the first camera is QDbusMessage the same messages that participants exchange, and the second camera is their interpretation in the form of QDbusArgument and QDbusReply. In principle, messages can be formed manually. For example, calls to property in dbusviwer are made like this:
 QDBusMessage message = QDBusMessage::createMethodCall(sig.mService, sig.mPath, QLatin1String("org.freedesktop.DBus.Properties"), QLatin1String("Get")); QList<QVariant> arguments; arguments << sig.mInterface << sig.mName; message.setArguments(arguments); c.callWithCallback(message, this, SLOT(dumpMessage(QDBusMessage))); QDBusInterface iface(sig.mService, sig.mPath, sig.mInterface,c); if( !iface.isValid()) qDebug()<<(QDBusError(iface.lastError()).message()); else qDebug()<<iface.property(sig.mName.toLatin1().data()); 

But it is much easier to entrust the formation of the QDbusArgument message. For this, you need only two things: implement the operators >> <<, if you have a type that does not fit the basic Qt standard:
 struct TestStructure{ int id; QString str; }; Q_DECLARE_METATYPE(TestStructure) static QDBusArgument& operator <<(QDBusArgument &argument, const TestStructure & arg) { argument.beginStructure(); argument<<arg.id<<arg.str; argument.endStructure(); return argument; } 

And before the first use, call qDBusRegisterMetaType. And after this the difficulties end: with the help of QDbusInterface you call the desired method, if it is not a void method, and the return value is important and necessary for us, then use the template class QDbusReply .
That's all, really everything. Acquaintance with the module is complete, in front of only the field where it should be used.

Source: https://habr.com/ru/post/185950/


All Articles