Qt Framework은 프로세스간 통신(IPC)을 위한 몇 가지 방법을 제공하는데 이 글에서는 Qt D-Bus에 대해 설명한다.
D-Bus는 리눅스용으로 개발된 메세지 버스 시스템으로 RPC(Remote Procedure Call) 및 프로세스간의 통신(IPC) 방법으로 사용된다. 리눅스계열에서 주로 사용되며 꽤 오래 전에 윈도우에서도 사용할 수 있도록 포팅이 진행되었다.
이 글에서는 윈도우즈 기반에서 Qt D-Bus를 사용, 설명할 것이므로 먼저 윈도우즈에서 D-Bus를 사용하기 위한 방법을 알아본다. 윈도우즈 기반에서 설명하긴하지만 Qt D-Bus는 리눅스건 윈도우즈건 플랫폼에 무관하게 사용할 수 있다. (D-Bus기본 개념은 이 글을 참조)
최신 소스코드(git://anongit.freedesktop.org/git/dbus/dbus)를 직접 컴파일 후 사용할 수 도 있고, 비공식이긴하지만 패키징된 설치파일을 다운로드받아 사용할 수 있는 방법이 있다.( 여기서는 오래된 버전이긴 하지만 간단히 비공식 설치파일을 다운로드 받아 설치한다.)
아래 주소를 방문하면 윈도우즈용 설치파일과 간단한 예제 응용프로그램도 다운로드 받을 수 있다. 윈도우즈용 설치파일을 다운로드 받아 설치한다.
https://code.google.com/archive/p/dbus-windows-installer/downloads
설치가 완료된 이후 C:\Program Files (x86)\D-Bus\bin 에는 dbus-daemon.exe을 포함한 몇가지 유틸리티도 설치되어 있다.
D-Bus를 사용하려면 dbus-daemon이 실행되어 있어야 한다.
윈도우즈 명령 프롬프트(cmd)를 열어서 먼저 dbus-env.bat과 dbus-daemon --session 을 차례로 명령한다.
이제 Qt D-Bus를 사용하기 위해 두개의 간단한 프로젝트를 생성한다. 프로젝트 폴더 구조는 다음과 같다.
하나는 on, off 상태에 따라 동작되는 light이고, 다른 하나는 이조명을 제어하는 switch 이다.
이 간단한 프로젝트들은 Qt D-Bus을 이용하여 light system으로 통합된다.
MyLight Class
MyLight객체는 상태를 나타내는 프로퍼티를 가지며 setter와 getter를 제공한다. 이 헤더파일은 D-Bus의 인터페이스로 사용될 것이다.
light.h
#include <QObject>
class MyLight : public QObject
{
Q_OBJECT
Q_PROPERTY(bool isOn READ getIsOn WRITE setIsOn NOTIFY lightChanged)
public:
explicit MyLight(QObject *parent = nullptr);
bool getIsOn() const;
void setIsOn(bool isOn);
signals:
void lightChanged();
public slots:
void turnOn();
void turnOff();
private:
bool mIsOn;
};
Qt용 명령 프롬프트를 실행하고 아래와 같은 명령으로 interface xml파일을 생성할 수 있다.
출력된 내용을 복사해서 프로젝트의 inferface디렉토리에 light.xml 파일로 생성한다. 이 light.xml은 light에서 ADAPTOR로 사용되고 switch에서는 INTERFACE로 사용된다.
light프로젝트 파일(.pro)에 아래 내용을 추가한다.
MyLight 생성자에서 MyLightAdaptor를 생성한다.(컴파일이 실행시에 light_adaptor.h파일이 만들어진다.)
light.cpp
#include "light.h"
#include "light_adaptor.h"
MyLight::MyLight(QObject *parent)
: QObject(parent),
mIsOn(false)
{
// Create interface adaptor
new MyLightAdaptor(this);
}
bool MyLight::getIsOn() const
{
return mIsOn;
}
void MyLight::setIsOn(bool isOn)
{
if(mIsOn != isOn){
mIsOn = isOn;
emit lightChanged();
}
}
void MyLight::turnOn()
{
setIsOn(true);
}
void MyLight::turnOff()
{
setIsOn(false);
}
main.cpp에서는 D-Bus의 세션버스에 MyLight 객체 및 서비스를 등록한다.
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtDBus>
#include <QtQml>
#include "light.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
// Create new instance
MyLight myLight;
engine.rootContext()->setContextProperty("myLight", &myLight);
// Connect to session bus
QDBusConnection connection = QDBusConnection::sessionBus();
connection.registerObject("/light", &myLight);
connection.registerService("cong.service.light");
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
이제 light를 컴파일 및 실행하고 서비스가 정상적으로 등록되어 있는지 확인하기 위해서 QDBusViewer를 실행 해 본다. (Qt가 설치된 곳에 있다.)
Qt D-Bus는 기본 D-Bus 컨셉을 유지하면서도 D-Bus의 저수준 API를 캡슐화하여 Qt 개발자에게 익숙한 시그널, 슬롯, 프로퍼티 개념을 제공하고 있다. Method를 각각 더블클릭해서 light가 on, off됨을 확인 할 수 있다.
switch프로젝트 파일(.pro)에는 아래 내용을 추가한다.
LightController Class
controller객체는 조명을 제어하기위한 on, off 인터페이스만 제공하고 private멤버인 D-Bus의 interface 객체를 가진다.
lightcontroller.h
#include <QObject>
#include "light_interface.h"
class LightController : public QObject
{
Q_OBJECT
public:
explicit LightController(QObject *parent = nullptr);
private:
local::MyLight *theLight;
public slots:
void turnOn();
void turnOff();
};
생성자에서 세션버스에 등록된 서비스 및 오브젝트 path의 interface를 생성한다.
lightcontroller.cpp
#include "lightcontroller.h"
LightController::LightController(QObject *parent) : QObject(parent)
{
theLight = new local::MyLight("cong.service.light", "/light",
QDBusConnection::sessionBus(), this);
}
void LightController::turnOn()
{
theLight->turnOn();
}
void LightController::turnOff()
{
theLight->turnOff();
}
LightController객체를 생성하고 qml에서 사용할 수 있도록 한다.
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "lightcontroller.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
LightController controller;
engine.rootContext()->setContextProperty("controller", &controller);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
switch를 컴파일 및 실행하고 on, off 버튼을 클릭해보자!