Предыдущая часть оставляла мрачные прогнозы, но все оказалось гораздо, гораздо, гораздо лучше.
Вдобавок мое незнание патернов, есть такой патен «Адаптер». Так вот QAbstractAdaptor собственно это он и есть. С учетом вышесказанного и замечания доки, что этот класс должен быть легковесным пример, действительно проясняющий подход либы должен быть таким:
Проект Pong:
Итак у нас есть класс Pong, мы хотим, его сделать доступным для взаимодействия по D-Bus. Что для этого нужно:
И раз уже заговорили о контексте, то нельзя не упомянуть о сердце всего модуля. И сердце это двухкамерное: первая камера — это QDbusMessage т.е. те самые послания которыми обмениваются участники, а вторая камера это их интерпретация в виде QDbusArgument и QDbusReply. Впринципе, послания можно формировать вручную. Например, обращения к property в dbusviwer сделана так:
Но гораздо проще доверить формирование послание QDbusArgument. Для это необходимо всего две вещи: реализовать операторы >> <<, если у вас тип не укладывающийся базовые в стандартные Qt:
И до первого использования вызвать qDBusRegisterMetaType. И после это трудности заканчиваются: с помощью QDbusInterface вы вызываете нужный метод, если это не void-метод, и возвращаемое значение нам важно и нужно, то используем шаблонный класс QDbusReply.
На этом все, действительно все. Знакомство с модулем закончено, впереди только поле где его надо использовать.
Разбор бонус-левела
Итак, спасибо хабражителю KOL93, он обратил внимание на то, что у QDbusAbstractAdaptor должен быть родитель. Для меня это оказалось новостью, т.к. я сильно привык, что родителя может и не быть. Может это убережет еще кого-то от подобного ляпа.Вдобавок мое незнание патернов, есть такой патен «Адаптер». Так вот QAbstractAdaptor собственно это он и есть. С учетом вышесказанного и замечания доки, что этот класс должен быть легковесным пример, действительно проясняющий подход либы должен быть таким:
Проект Pong:
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
Проект Ping:#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;
}
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;
}
Общий файл serviceNameAndProperty.h
#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;
}
И все таки, как это юзать?
Ради целостности представления как пользоваться модулем позволю себе повторение пройденного. Благо повторение — мать учения.Итак у нас есть класс Pong, мы хотим, его сделать доступным для взаимодействия по D-Bus. Что для этого нужно:
- Подсоединится к D-Bus демону. Он может быть где угодно, но для стандартных шин есть уже готовые статические методы. В нашем примере это вызов идет сразу с проверкой
if (!QDBusConnection::sessionBus().isConnected()) {
- Ради удобства и определенности можно закрепить за сервисом имя, аналогичное DNS. Мы делаем это для проекта Pong:
Важно! оно должно содержать хотя бы один разделяющий символ — точку.if (!connection.registerService(SERVICE_NAME)) {
- Создать адаптер, родителем которого будет являться Pong. Этот адаптер будет принимать сигналы извне и перенаправлять их своему родителю, ровно как и в обратном направлении. Еще подчеркну, что по задумке проектировщиков либы адаптер действительно адаптер и с его помощью достаточно просто расширить уже имеющееся приложение. Вот как-то так происходит передача сигнала от проги в D-Bus-мир:
Важно! имя интерфейса адаптера, задаваемого в Q_ClASSINFO должно содержать точку.QObject::connect(this, SIGNAL(callingMe(QString,QString)), m_pongAdapter, SIGNAL(callingMe(QString, QString)));
- Зарегистрировать Pong по некому пути. Выглядит это примерно так:
Важно! Этот путь должен содержать хотя бы один разделяющий символ -/if( ! connection.registerObject("/", &pong)){
И раз уже заговорили о контексте, то нельзя не упомянуть о сердце всего модуля. И сердце это двухкамерное: первая камера — это QDbusMessage т.е. те самые послания которыми обмениваются участники, а вторая камера это их интерпретация в виде QDbusArgument и QDbusReply. Впринципе, послания можно формировать вручную. Например, обращения к property в dbusviwer сделана так:
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());
Но гораздо проще доверить формирование послание QDbusArgument. Для это необходимо всего две вещи: реализовать операторы >> <<, если у вас тип не укладывающийся базовые в стандартные Qt:
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;
}
И до первого использования вызвать qDBusRegisterMetaType. И после это трудности заканчиваются: с помощью QDbusInterface вы вызываете нужный метод, если это не void-метод, и возвращаемое значение нам важно и нужно, то используем шаблонный класс QDbusReply.
На этом все, действительно все. Знакомство с модулем закончено, впереди только поле где его надо использовать.