Playing with DBus in qt
When users are right
A few days ago I received an email from a user of Kdropbox saying the behaviour of the application when clicking on the the system tray icon was not the usual in KDE. As he said, if a window is opened when you click on the icon a second click should close it. It's funny but so many years using this desktop environment and I had not noticed. I checked and, it is true that not all programs fulfill it, but those that ship with KDE do it.
Kdropbox opened (and even the stable version opens at the time of writing this) a window of a file browser (konqueror or dolphin) every time you click the icon, so it was a good way to quickly launch 377 file browsers, depending on the speed of our hand :D
In any case, finally this is usability and the user was right so I had to change it. However, there was a very important problem in all this: to do such control in a window of the application itself is relatively simple, but what if what we have launched is a separate process? Well, one solution might be the first that occurred to me: start the process, save the pid and kill it with the second click. Error, Dolphin doesn't launch a new instance if it is already running, so if there is already a Dolphin window opened, when you click on the icon, it will not have a separate pid and what is worse, kill the process would terminate with all file manager windows opened by the user.
DBus and qdbusviewer
A good solution for these cases is to use DBus which is the Linux standard for communication between applications. If we have no previous experience it is best to play a bit with qdbusviewer which will allow us to seek through all the applications that use this protocol and even call their methods.
For example we can open a new window in Dolphin showing a URL. But first it is necessary to have Dolphin already running otherwise it will not appear in the list of services in qdbusviewer (left panel)

As we give OK on the window in which the parameters are requested we will see the lower panel shows the response to the method or any errors that might occur.
Typing the code to make calls
But how do we do this from our application? There are many examples on the web to answer this question, a quick example using QT for the previous case would be this ... well, not so fast. A little bit of theory before.We always need to know:
1. The service name of the process in DBus, in practice seems a URL
2. The path to the object you want to call
3. The interface declared by the application for the method that we want to use
4. The method and its parameters
It is obvious that we can obtain all this easily using qdbusviewer. In our case:
1. Service Name: org.kde.dolphin
2. Path: /MainApplication
3. Interface: org.kde.dolphin.Aplication
4. Method: openWindow
Taking all the necessary, QT makes it very easy with QDBusInterface class. We just have to include the classes we need and a few lines of code to make the call.
In this example you need the following classes for DBus:
#include <QDBusInterface>
#include <QDBusReply>
QDBusInterface remoteApp("org.kde.dolphin","/MainApplication","org.kde.dolphin.Application",QDBusConnection::sessionBus());
if (remoteApp.isValid()){
QDBusReply<int> reply =remoteApp.call("openWindow",path);
int winid=(reply.value());
}
A final example, let's see how to close that same window as the way forward is slightly different. To understand this we must see how the Dolphin tree is transformed into DBus, the next screen shot shows a new node which appeared identifying the new window, so if the identifier given to us in the above method call is 3 the path we need is
/dolphin/MainWindow3

This is important when closing the window because it is the path to use:
QDBusInterface remoteApp("org.kde.dolphin","/dolphin/MainWindow"+QString::number(getWindowId()), "org.kde.dolphin.MainWindow", QDBusConnection::sessionBus());
if (remoteApp.connection().isConnected())
QDBusReply<int> reply =remoteApp.call("quit");

Comments
Post new comment