Qt DBus : A Simple Example

Two sample applications , in which one will call the function of other through dbus.
Qt Dbus wrapper makes it very easy.

Okay, So, How is this possible ?
In depth explanation can be found in d-bus manuals. Here, the layman's point of view :

Application1 has Proxy Object instance of Application2.
Call the function in proxy-App2 and the function in "real" App2 will also be executing. [ Timing of execution and result depends on whether the slots / functions synchronous/Asynchronous . Ref: http://qt-project.org/doc/qt-5.0/qtdbus/qdbusdeclaringslots.html ]

Where in App2 has adapters to sync with proxy in App1. And the communication is over dbus.

The adapter and proxy to be considered as the wrapper for qbus communication.

Steps :
1) Create your application.
2) Generate / write xml file.
3) Generate / Write Adaptors
4) Generate/Write the Interfaces/Proxy Classes
5) Register the service on dbus channel.
6) Use proxy/interface classes in the client application when needed.


Here is the sample.

In our application, we will be exposing the following function :

1)
in mainwindow.h
public slots:

    int test(QString str="");

in mainwindow.cpp
int MainWindow::test(QString str)
{
    if(str == QString("via ctor"))
    {
        QLabel *a = new QLabel(this);
        a->setText("Via ctor : Hola");
        a->show();
        return 101;
    }else if(str == QString("via setParent"))
    {
        QLabel *a = new QLabel(0);
        a->setParent(this);
        a->setText("via setParent : Hola");
        a->show();
        return 102;
    }
    return 100;

}


2)Generate xml for dbus.
Issue the following command on terminal [ This tool comes with Qt packages]
qdbuscpp2xml mainwindow.h -o mainwindow.xml

mainwindow.xml might look like this :
 node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
   name="local.MainWindow">
     name="test">
       type="i" direction="out"/>
       name="str" type="s" direction="in"/>
    
     name="test">
       type="i" direction="out"/>
    
  

The xml file is meaningful when compared with dbus xml manual. [and its very simple, when autogenerated ;)]

3)Generate Adapters
Issue the following command 
qdbusxml2cpp -a test_adap mainwindow.xml
You will get test_adap.cpp and its header.

4)Generate proxy / Interface 
Issue the following command 
qdbusxml2cpp -p test_interface mainwindow.xml
You will get test_interface.cpp and its header.

5) Register the service on dbus channel.

The following registers object name and service name on session bus, which later used to identify by the client apps. [More on session bus or system bus can be found at freedesktop.org documentation. ]

test_adap.h
  >
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;

    new MainWindowAdaptor(&w);
    QDBusConnection connection = QDBusConnection::sessionBus();
    connection.registerObject("/testObject", &w);
    connection.registerService("dbustester.test");
    w.show();
    
    return a.exec();
}

6)Use proxy/interface classes in the client application when needed.

The following creates a proxy object and calls the function.

test_interface.h >
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    local::MainWindow *m_window = new local::MainWindow("dbustester.test", "/testObject", QDBusConnection::sessionBus(), 0);
    qDebug() << "Result from 1# " << m_window->test("via setParent");
 
    return a.exec();
}

########################################################################



A Special Case : QtDBus & Pass By Reference

The above methods can be used for pass by value methods with or with out a return value. Pass by value in terms of two different process makes no sense. Because of the different memory boundaries of two process.
However this can be emulated with no(very less) effort in Qt DBus. Following example explains how to :-
Note : A function with return type "void" , is not working with the auto generator tool. (Qt.5.0, qdbuscpp2xml).
in mainwindow.h, lets have a function with ref param.
int referencefunction(int &val); 
and detailed in mainwindow.cpp
int MainWindow::referencefunction(int &val)
{
    val = 200;
    return 1;
}

Follow the above 6 methods to generate xml,adaptors and proxy.
Your xml should contain something like this :

Note that arg direction is out.


We need to edit the auto generated adapter little bit : 
int MainWindowAdaptor::referencefunction(int &val)
{
    // handle method call local.MainWindow.referencefunction
    //return static_cast *>(parent())->referencefunction(val);
}


Change this to 
int MainWindowAdaptor::referencefunction(int &val)
{
// handle method call local.MainWindow.referencefunction
return static_cast<MainWindow *>(parent())->referencefunction(val);
}
and #include "mainwindow.h" in the adapter as well


Check this in the client application :
int ab = 30;
clientObj->referencefunction(ab);
qDebug() << ab; //  the output will be 200.

If you want to know how this is implemented [ simulated ] , have a look at proxy file.
inline QDBusPendingReply<int, int> referencefunction()
{
QList<QVariant> argumentList;
return asyncCallWithArgumentList(QLatin1String("referencefunction"), argumentList);
}And
inline QDBusReply<int> referencefunction(int &val)
{
QList<QVariant> argumentList;
QDBusMessage reply = callWithArgumentList(QDBus::Block, QLatin1String("referencefunction"), argumentList);
if (reply.type() == QDBusMessage::ReplyMessage && reply.arguments().count() == 2) {val = qdbus_cast<int>(reply.arguments().at(1));}
return reply;
}

1 comment: