通过QtDBus,可以将Qt的信号槽机制上升到不同进程之间通信。
使用系统:银河麒麟
Qt版本:4.6.1
网上教程说,qmake编译需要将QT+= qDbus添加到pro工程里
添加后提示找不到该模块,在包含头文件后使用函数时报了找不到定义的编译错误。
经过一下午的排查尝试,最后发现在pro工程里添加
QT += dbus
便能正确的将模块加载进来了。
示例程序:
本程序中服务端运行后弹出窗口,此时运行发送端将关闭服务端弹出的窗口。
调用端:
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QtDBus/QDBusConnection>
#include <QtDBus/QDBusError>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
main.cpp
//#include "widget.h"
#include <QCoreApplication>
#include <QtDBus/QDBusConnection>
#include <QtDBus/QDBusError>
#include <QtDBus/QDBusMessage>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// Widget w;
// w.show();
//构造一个消息
QDBusMessage message = QDBusMessage::createMethodCall("com.scorpio.test",
"/home/kylin",
"com.scorpio.test.close",
"closeWidget");
//发送消息
QDBusMessage response = QDBusConnection::sessionBus().call(message);
if(response.type()==QDBusMessage::ReplyMessage)
{
int value = response.arguments().takeFirst().toInt();
qDebug()<<QString("value==%1").arg(value);
}
else
{
qDebug()<<"value method called failed:"<<response.type();
}
// return 0;
return a.exec();
}
受控服务端:
main.cpp
#include <QApplication>
#include <QDebug>
#include <QtDBus/QDBusConnection>
#include <QtDBus/QDBusError>
#include "widget.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//建立到session bus的连接
QDBusConnection connection = QDBusConnection::sessionBus();
//在session bus上注册名为com.scorpio.test的服务
if(!connection.registerService("com.scorpio.test"))
{
qDebug()<<connection.lastError().message();
exit(-1);
}
Widget w;
connection.registerObject("/home/kylin",&w,QDBusConnection::ExportAllSlots);
w.show();
return a.exec();
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QObject>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface","com.scorpio.test.close")
public:
explicit Widget(QWidget *parent = 0);
~Widget();
public slots:
int closeWidget();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
int Widget::closeWidget()
{
this->close();
return 1;
}
参数解释:
对象路径
像网络主机,应用程序通过导出对象为其它应用程序提供特定服务。这些对象按层次组织,非常类似类的父子关系,派生自 QObject 占有。不管怎样,一种区别是存在根对象概念 (所有对象具有最终父级)。
若继续与 Web 服务进行类比,对象路径等同于 URL 的路径部分
像它们,D-Bus 对象路径的形成类似文件系统路径名:它们是以斜杠分隔的标签,各标签的组成由字母、数字及下划线字符 ( _ )。它们必须始终以斜杠开头,且不能以斜杠结尾。
服务名称
当通过总线通信时,应用程序获得所谓的服务名称:这就是该应用程序选择被同一总线中,其它应用程序知道的方式。服务名称由 D-Bus 总线代理程序守护,用于将消息从一个应用程序路由到另一个应用程序。与服务名称类似的概念是 IP 地址和主机名:根据计算机提供给网络的服务,一台计算机通常具有一个 IP 地址,且可能具有一个或多个与其关联的主机名。
另一方面,若不使用总线,也不会使用服务名称。若再将其与计算机网络进行比较,这相当于点对点网络:由于对等方是已知的,因此不需要使用主机名来查找它 (或其 IP 地址)。
D-Bus 服务名称的格式实际上非常类似于主机名:它是由点分隔的字母和数字的序列。常见实践甚至是根据定义该服务的组织的域名,来命名一服务名称。
例如,D-Bus 服务的定义通过 freedesktop.org 且可以在总线中找到以下服务名称:
org.freedesktop.DBus
接口
接口类似 C++ 抽象类和 Java 的 interface 关键词和声明在调用者和被调用者之间建立的契约。也就是说,它们建立的方法、信号及特性的名称是可用的,及行为是双方期望的当建立通信时。
Qt 使用非常类似的机制在其 插件系统 :C++ 中的基类关联唯一标识符,通过方式 Q_DECLARE_INTERFACE () 宏。
实际上,D-Bus 接口名称的命名方式与 Qt 插件系统的建议类似:标识符的构造通常来自定义该接口的实体的域名