// 传参数
QDBusMessage msg = QDBusMessage::createMethodCall("com.myabc.service",
"/", "com.myabc.interface", "setName");
msg << QString("Bright");
QDBusMessage response = QDBusConnection::sessionBus().call(msg);
// 获取返回值
QDBusMessage msg = QDBusMessage::createMethodCall("com.myabc.service",
"/", "com.myabc.interface", "name");
QDBusMessage response = QDBusConnection::sessionBus().call(msg);
// 判断 Method 是否被正确返回
if(response.type() == QDBusMessage::ReplyMessage)
{
// QDBusMessage的arguments不仅可以用来存储发送的参数,也用来存储返回值
// 这里取得 checkIn 的返回值
QString name= response.arguments().takeFirst().toString();
}
QDBusInterface interface("com.myabc.service", "/",
"com.myabc.interface",
QDBusConnection::sessionBus());
if(!interface.isValid())
{
qDebug() << qPrintable(QDBusConnection::sessionBus().lastError().message());
exit(1);
}
// 调用远程对象的方法 setName()
interface.call("setName", "Bright");
// 调用 name() 并获取返回值
QDBusReply<QString> reply = interface.call("name");
if(reply.isValid())
{
QString value = reply.value();
qDebug() << "value = " << value ;
}
interface::asyncCall( ) 异步调用
QDBusPendingCall async = interface->asyncCall("setName", "Brion");
// or use this: QDBusPendingReply<QString> reply = interface->asyncCall("RemoteMethod");
async.waitForFinished ();
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(async, this);
QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
this, SLOT(callFinishedSlot(QDBusPendingCallWatcher*)));
void MyClass::callFinishedSlot(QDBusPendingCallWatcher *call)
{
QDBusPendingReply<QString> reply = *call;
if (! reply.isError()) {
QString name= reply.argumentAt<0>();
qDebug() << "name = " << name;
}
call->deleteLater();
}
使用工具qdbuscpp2xml从object.h生成XML文件:
指令: qdbuscpp2xml -M test.h -o com.scorpio.test.xml
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">;;
<node>
<interface name="com.scorpio.test.value">
<method name="maxValue">
<arg type="i" direction="out"/>
</method>
<method name="minValue">
<arg type="i" direction="out"/>
</method>
<method name="value">
<arg type="i" direction="out"/>
</method>
</interface>
</node>
使用工具qdbusxml2cpp从XML文件生成继承自QDBusInterface的类
指令: qdbusxml2cpp com.scorpio.test.xml -p valueInterface
生成两个文件:valueInterface.cpp 和 valueInterface.h & valueInterface.h文件:
/*
* This file was generated by qdbusxml2cpp version 0.7
* Command line was: qdbusxml2cpp com.scorpio.test.xml -p testInterface
*
* qdbusxml2cpp is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
*
* This is an auto-generated file.
* Do not edit! All changes made to it will be lost.
*/
#ifndef TESTINTERFACE_H_1526737677
#define TESTINTERFACE_H_1526737677
#include <QtCore/QObject>
#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
#include <QtDBus/QtDBus>
/*
* Proxy class for interface com.scorpio.test.value
*/
class ComScorpioTestValueInterface: public QDBusAbstractInterface
{
Q_OBJECT
public:
static inline const char *staticInterfaceName()
{ return "com.scorpio.test.value"; }
public:
ComScorpioTestValueInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = 0);
~ComScorpioTestValueInterface();
public Q_SLOTS: // METHODS
inline QDBusPendingReply<int> maxValue()
{
QList<QVariant> argumentList;
return asyncCallWithArgumentList(QLatin1String("maxValue"), argumentList);
}
inline QDBusPendingReply<int> minValue()
{
QList<QVariant> argumentList;
return asyncCallWithArgumentList(QLatin1String("minValue"), argumentList);
}
inline QDBusPendingReply<int> value()
{
QList<QVariant> argumentList;
return asyncCallWithArgumentList(QLatin1String("value"), argumentList);
}
Q_SIGNALS: // SIGNALS
};
namespace com {
namespace scorpio {
namespace test {
typedef ::ComScorpioTestValueInterface value;
}
}
}
#endif
/*
* This file was generated by qdbusxml2cpp version 0.7
* Command line was: qdbusxml2cpp com.scorpio.test.xml -p testInterface
*
* qdbusxml2cpp is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
*
* This is an auto-generated file.
* This file may have been hand-edited. Look for HAND-EDIT comments
* before re-generating it.
*/
#include "testInterface.h"
/*
* Implementation of interface class ComScorpioTestValueInterface
*/
ComScorpioTestValueInterface::ComScorpioTestValueInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent)
: QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent)
{
}
ComScorpioTestValueInterface::~ComScorpioTestValueInterface()
{
}
调用Proxy类访问Service如下:
#include <QCoreApplication>
#include <QDBusMessage>
#include <QDBusConnection>
#include <QDBusReply>
#include <QDBusInterface>
#include <QDebug>
#include "testInterface.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// 初始化自动生成的Proxy类com::scorpio::test::value
com::scorpio::test::value test("com.scorpio.test",
"/test/objects",
QDBusConnection::sessionBus());
// 调用value方法
QDBusPendingReply<int> reply = test.value();
//qdbusxml2cpp生成的Proxy类是采用异步的方式来传递Message,
//所以需要调用waitForFinished来等到Message执行完成
reply.waitForFinished();
if (reply.isValid())
{
int value = reply.value();
qDebug() << QString("value = %1").arg(value);
}
else
{
qDebug() << "value method called failed!";
}
return a.exec();
}
QDBusConnection::sessionBus().connect("com.brion.service", "/",
"com.brion.interface",
"ageChanged", this,
SLOT(onAgeChanged(int)));
QDBusInterface *interface = new QDBusInterface("com.brion.service", "/",
"com.brion.interface",
DBusConnection::sessionBus());
QObject::connect(&interface, SIGNAL(ageChanged(int)),
object, SLOT(onAgeChanged(int)));
class Person : public QObject
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "com.brion.interface")
public:
explicit Person(QObject *parent = 0);
signals:
void nameChanged(QString);
void ageChanged(int);
public slots:
QString name() const { return m_name; }
// can‘t be reference
void setName(QString name) {
m_name = name;
}
int age() const { return m_age; }
void setAge(int age) {
m_age = age;
}
private:
QString m_name;
int m_age;
};
// main.cpp
#include <QtDBus/QDBusConnection>
#include <person.h>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QDBusConnection sessionBus = QDBusConnection::sessionBus();
if (sessionBus.registerService("com.brion.service")) {
sessionBus.registerObject("/", new Person(),
QDBusConnection::ExportAllContents);
}
return a.exec();
}
示例2:PingPong Game
Publisher,直接继承自 QDBusAbstractAdaptor:
class Pong: public QDBusAbstractAdaptor
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.example.QtDBus.ComplexPong.Pong")
Q_PROPERTY(QString value READ value WRITE setValue)
public:
QString m_value;
QString value() const;
void setValue(const QString &newValue);
Pong(QObject *obj) : QDBusAbstractAdaptor(obj)
{ }
signals:
void aboutToQuit();
public slots:
QDBusVariant query(const QString &query);
Q_NOREPLY void quit();
};
// 启动服务
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
QObject obj;
Pong *pong = new Pong(&obj);
QObject::connect(&app, &QCoreApplication::aboutToQuit, pong, &Pong::aboutToQuit);
pong->setProperty("value", "initial value");
QDBusConnection::sessionBus().registerObject("/", &obj);
if (!QDBusConnection::sessionBus().registerService(SERVICE_NAME)) {
fprintf(stderr, "%s\n",
qPrintable(QDBusConnection::sessionBus().lastError().message()));
exit(1);
}
app.exec();
return 0;
}
Subscriber端,监听Publisher的信号:
class Ping: public QObject
{
Q_OBJECT
public slots:
void start(const QString &);
public:
QFile qstdin;
QDBusInterface *iface;
};
void Ping::start(const QString &name)
{
if (name != SERVICE_NAME)
return;
// open stdin for reading
qstdin.open(stdin, QIODevice::ReadOnly);
// find our remote
iface = new QDBusInterface(SERVICE_NAME, "/", "org.example.QtDBus.ComplexPong.Pong",
QDBusConnection::sessionBus(), this);
if (!iface->isValid()) {
fprintf(stderr, "%s\n",
qPrintable(QDBusConnection::sessionBus().lastError().message()));
QCoreApplication::instance()->quit();
}
connect(iface, SIGNAL(aboutToQuit()), QCoreApplication::instance(), SLOT(quit()));
while (true) {
printf("Ask your question: ");
QString line = QString::fromLocal8Bit(qstdin.readLine()).trimmed();
if (line.isEmpty()) {
iface->call("quit");
return;
} else if (line == "value") {
QVariant reply = iface->property("value");
if (!reply.isNull())
printf("value = %s\n", qPrintable(reply.toString()));
} else if (line.startsWith("value=")) {
iface->setProperty("value", line.mid(6));
} else {
QDBusReply<QDBusVariant> reply = iface->call("query", line);
if (reply.isValid())
printf("Reply was: %s\n", qPrintable(reply.value().variant().toString()));
}
if (iface->lastError().isValid())
fprintf(stderr, "Call failed: %s\n", qPrintable(iface->lastError().message()));
}
}
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
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;
}
QDBusServiceWatcher serviceWatcher(SERVICE_NAME, QDBusConnection::sessionBus(),
QDBusServiceWatcher::WatchForRegistration);
Ping ping;
QObject::connect(&serviceWatcher, &QDBusServiceWatcher::serviceRegistered,
&ping, &Ping::start);
QProcess pong;
pong.start("./complexpong");
app.exec();
}
方法4: DBusAdapter
生成Adapter类的流程如下:
使用工具 qdbuscpp2xml从test.h生成XML文件
qdbuscpp2xml -M test.h -o com.scorpio.test.xml
编辑com.scorpio.test.xml,选择需要发布的method,不需要发布的删除。
使用工具qdbusxml2cpp从XML文件生成继承自QDBusInterface的类
qdbusxml2cpp com.scorpio.test.xml -i test.h -a valueAdaptor
生成两个文件:valueAdaptor.cpp和valueAdaptor.h & valueAdaptor.h文件:
/*
+ This file was generated by qdbusxml2cpp version 0.7
+ Command line was: qdbusxml2cpp com.scorpio.test.xml -i test.h -a valueAdaptor
*
+ qdbusxml2cpp is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
*
+ This is an auto-generated file.
+ This file may have been hand-edited. Look for HAND-EDIT comments
+ before re-generating it.
*/
#ifndef VALUEADAPTOR_H_1526742670
#define VALUEADAPTOR_H_1526742670
#include <QtCore/QObject>
#include <QtDBus/QtDBus>
#include "test.h"
class QByteArray;
template<class T> class QList;
template<class Key, class Value> class QMap;
class QString;
class QStringList;
class QVariant;
/*
+ Adaptor class for interface com.scorpio.test.value
*/
class ValueAdaptor: public QDBusAbstractAdaptor
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "com.scorpio.test.value")
Q_CLASSINFO("D-Bus Introspection", ""
" <interface name=\"com.scorpio.test.value\">\n"
" <method name=\"maxValue\">\n"
" <arg direction=\"out\" type=\"i\"/>\n"
" </method>\n"
" <method name=\"minValue\">\n"
" <arg direction=\"out\" type=\"i\"/>\n"
" </method>\n"
" </interface>\n"
"")
public:
ValueAdaptor(QObject *parent);
virtual ~ValueAdaptor();
public: // PROPERTIES
public Q_SLOTS: // METHODS
int maxValue();
int minValue();
Q_SIGNALS: // SIGNALS
};
#endif
/*
+ This file was generated by qdbusxml2cpp version 0.7
+ Command line was: qdbusxml2cpp com.scorpio.test.xml -i test.h -a valueAdaptor
*
+ qdbusxml2cpp is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
*
+ This is an auto-generated file.
+ Do not edit! All changes made to it will be lost.
*/
#include "valueAdaptor.h"
#include <QtCore/QMetaObject>
#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
/*
+ Implementation of adaptor class ValueAdaptor
*/
ValueAdaptor::ValueAdaptor(QObject *parent)
: QDBusAbstractAdaptor(parent)
{
// constructor
setAutoRelaySignals(true);
}
ValueAdaptor::~ValueAdaptor()
{
// destructor
}
int ValueAdaptor::maxValue()
{
// handle method call com.scorpio.test.value.maxValue
int out0;
QMetaObject::invokeMethod(parent(), "maxValue", Q_RETURN_ARG(int, out0));
return out0;
}
int ValueAdaptor::minValue()
{
// handle method call com.scorpio.test.value.minValue
int out0;
QMetaObject::invokeMethod(parent(), "minValue", Q_RETURN_ARG(int, out0));
return out0;
}
调用Adaptor类注册Object对象如下:
#include <QCoreApplication>
#include <QDBusConnection>
#include <QDebug>
#include <QDBusError>
#include "test.h"
#include "valueAdaptor.h"
int main(int argc, char *argv[]){
QCoreApplication a(argc, argv);
QDBusConnection connection = QDBusConnection::sessionBus();
test object(60);
//ValueAdaptor是qdbusxml2cpp生成的Adaptor类
ValueAdaptor valueAdaptor(&object);
if (!connection.registerService("com.scorpio.test"))
{
qDebug() << connection.lastError().message();
exit(1);
}
connection.registerObject("/test/objects", &object);
return a.exec();
}
D-Bus系统提供了一种机制可以在访问某个service时,自动把应用程序运行起来。
需要在/usr/share/dbus-1/services下面建立com.scorpio.test.service文件,文件的内容如下:
**[D-****BUS Service]**
Name=com.scorpio.test
Exec=/path/to/scorpio/test
在访问test的method前,不必手动运行应用程序。
常用类即其方法,请参考脑图。
这里提供一个实例代码(PingPong在PyQt5中的实现):
import sys
from PyQt5.QtCore import QCoreApplication, pyqtSlot, QObject
from PyQt5.QtDBus import QDBusConnection, QDBusInterface, QDBusReply, QDBusAbstractAdaptor
class Pong(QObject):
@pyqtSlot(str, result=str)
def pong_method(self, args):
print("Get a proxy-method call... the args is: ", args)
return ‘ping("%s") got called.‘ % args
@pyqtSlot()
def pong_method_without_params(self):
print("Get a proxy-method call...")
if __name__ == "__main__":
app = QCoreApplication(sys.argv)
bus = QDBusConnection.sessionBus() # sessionBus()
if not bus.isConnected():
raise Exception("Fail to connect to Session-Bus.")
# register the service
if not bus.registerService(‘org.example.QtDBus.PingPong‘): # 注册 BusName
raise Exception("Fail to register the service.")
pong = Pong() # 创建对象 -> dbus server object
print("-->> ", isinstance(pong, QDBusAbstractAdaptor))
bus.registerObject("/", # this is the object-path, must be start from ‘/‘
"pong.anything.namespace", # this is the interface [choose to name it]
pong, # this is the server obj, but you must prepare it before
# QDBusConnection.ExportAllSlots) # 注册所有slot作为proxy-method
QDBusConnection.ExportAllContents)
sys.exit(app.exec_())
class Car(QObject):
def turn_left(self, degree: int):
print("The car turn left [{}] degree.".format(degree))
def turn_right(self, degree: int):
print("The car turn right [{}] degree.".format(degree))
def turn_back(self):
print("The car is turnning back.")
class CarInterface(QDBusAbstractAdaptor):
Q_CLASSINFO("D-Bus Interface", ‘org.HallGarden.Examples.Interface‘)
Q_CLASSINFO("D-Bus Introspection", ‘‘
‘ <interface name="org.HallGarden.Examples.Interface">\n‘
‘ <method name="turn_left">\n‘
‘ <arg direction="in" type="i"/>\n‘
‘ </method>\n‘
‘ <method name="turn_right">\n‘
‘ <arg name="degree" direction="in" type="i"/>\n‘
‘ <arg name="any" direction="out" type="i"/>\n‘
‘ </method>\n‘
‘ <method name="turn_back"/>\n‘
‘ </interface>\n‘
‘‘)
def __init__(self, parent):
super().__init__(parent)
self.setAutoRelaySignals(True)
@pyqtSlot(int)
def turn_left(self, degree):
self.parent().turn_left(degree)
@pyqtSlot(int)
def turn_right(self, degree):
self.parent().turn_right(degree)
return 30
@pyqtSlot()
def turn_back(self):
self.parent().turn_back()
if __name__ == "__main__":
app = QCoreApplication(sys.argv)
car = Car()
CarInterface(car) # 装饰car对象,而新生成的对象并没实际应用
connection = QDBusConnection.sessionBus()
connection.registerService(‘org.example.CarExample‘)
connection.registerObject(‘/Car‘, car)
sys.exit(app.exec_())
在 freedesktop 体系中,将interface至于Object下层。于是对于binding的设计,一般的(比如dbus-python)proxy可以获得object的代理,而interface依旧是对象的namespace,故而在proxy下层。但Qt的命名似乎不太一样——它所谓的 Interface 对象与代理很相似,而Qt概念中的代理,一般是通过 XML 转换生成的。
一般的binding做到Proxy就可以了,而且一般Proxy确实够用了;而Qt又设计了Adapter类,用于将DBus信号绑定QObject,其意义何在?
关于dbus消息传递时的载体是二进制流——那么它解码后的内容是中间格式的XML吗?亦或是给底层 lib 库的C语言数据结构?
关于XML是为了实现跨平台、跨语言。但问题是,流程不应该是将代码的结构,例如根据 QDBusConnection.ExportAllContents 的性质,将 @pyqtSlot 槽函数抽象为中间代码,也就是XML,再将XML发送给daemon读取形成总线服务或信号。但实际上这个过程却颠倒过来了——如果你使用QDBusAdapter,你需要将xml文本写入,或者通过 qdbusxml2cpp 再生成C++代码。除了逻辑上的颠倒,更麻烦的是,没有任何工具提供了静态编译验证XML语法或语义的 debug 调试工具。一旦你不小心在
原文:https://www.cnblogs.com/brt2/p/13586094.html