Cuando los usuarios tienen la razón
Hace unos días recibí un mail de un usuario de
Kdropbox
diciendo que el comportamiento de la aplicación al hacer clic sobre el
icono de la bandeja de sistema no era el habitual en KDE. Según me
contaba al hacer clic se abría la ventana por tanto al volver a hacerlo
debería cerrarse.
Es curioso pero tantos años usando este
escritorio y no me había fijado. Lo comprobé y es cierto que no todos
los programas lo cumplen, pero aquellos que se distribuyen con KDE sí.
Kdropbox abría (y la versión estable la abre aún en el momento de
escribir esto) una ventana de un navegador de archivos (dolphin o
konqueror) cada vez que se hacía clic en el icono, por lo que era una
buena manera de lanzar rápidamente 377 navegadores de archivos,
dependiendo de la velocidad de nuestra mano :D
En todo caso
terminaba siendo una cuestión de usabilidad y el usuario tenía razón así
que había que cambiarlo. Sin embargo había un problema bastante
importante en todo esto: hacer un control de ese tipo en una ventana
propia de la aplicación hubiera sido relativamente sencillo, pero ¿que
pasa si lo que hemos lanzado es un proceso independiente? Bueno, una
solución podría ser la primera que se me ocurrió: lanzar el proceso,
guardarme el pid y matarlo con el segundo clic. Error, Dolphin no se
lanza como instancia nueva si ya está corriendo, o sea que si ya hay una
ventana de Dolphin abierta cuando hacemos clic en el icono no tendremos
un pid independiente y lo que es peor, matar ese proceso terminaría con
todas las ventanas del gestor de archivos que tuviera abiertas el
usuario.
DBus y qdbusviewer
La solución para estos casos es hacer uso de
DBus que
es el estándar de Linux para la comunicación entre aplicaciones. Si no
tenemos experiencia previa lo mejor es trastear un poco con qdbusviewer
el cual nos permitira escudriñar por todas las aplicaciones que
utilizan este protocolo e incluso, llamar a sus métodos.
Por
ejemplo podemos abrir una ventana nueva de Dolphin indicando la URL que
queremos mostrar.La condición previa necesaria es que Dolphin esté en
ejecución ya que de otro modo no aparecerá en el listado de servicios de
qdbusviewer (panel izquierdo)
En
cuanto le demos a OK en la ventana en la que se solicitan los
parámetros veremos que en el panel inferior se muestra la respuesta al
método o los posibles errores que ocurran.
Escribiendo el código para hacer llamadas
Pero
¿cómo hacemos esto desde nuestra aplicación? Hay bastantes ejemplos en
la web para contestar a esta pregunta, un ejemplo rápido usando QT para
el caso anterior sería este... bueno, no tan rápido. Un poquito de
teoría antes.
Siempre necesitaremos saber lo siguiente:
- El nombre del servicio con el que el proceso se identifica en DBus, en la práctica se asemeja a una URL
- La ruta hasta el objeto al que queremos llamar
- El interfaz declarado por la aplicación para el método que buscamos
- El método al que queremos llamar y sus parámetros
Salta a la vista que todo esto lo obtenemos fácilmente con qdbusviewer. En nuestro caso:
- Nombre del servicio: org.kde.dolphin
- Ruta: /MainApplication
- Interfaz: org.kde.dolphin.Aplication
- Método: openWindow
Teniendo
todo lo necesario QT lo pone muy fácil gracias a la clase
QDBusInterface. Sólo tenemos que incluir las clases que necesitamos y
unas cuantas líneas de código para hacer la llamada.
En este ejemplo se necesitan las siguientes clases relativas a DBus:
#include <qdbusinterface>
#include <qdbusreply>
El
código necesario para abrir nuestra ventana de Dolphin es el siguiente,
teniendo en cuenta que path debería contener alguna ruta válida:
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
());
}
Hemos
intentando la conexión con la aplicación en la ruta e interfaz deseado,
después hemos comprobado que realmente hemos contactado con ella y así
hemos podido llamar al método openWindow con la tura deseada. Por último
nos hemos guardado la respuesta que nos devuelve el método que no es
otra cosa que el identificador de la nueva ventana.
Un último
ejemplo, vamos a ver como cerrar esa misma ventana ya que la forma de
proceder varía ligeramente. Para entender esto hay que observar como se
transforma el árbol de Dolphin en DBus, en la siguiente captura se pude
ver como ha aparecido un nuevo nodo identificando la nueva ventana, de
forma que si el identificador que nos ha devuelto el método anterior es
el 3 tendremos la ruta
/dolphin/MainWindow3
Esto es importante a la hora de cerrar la ventana ya que es la ruta que utilizaremos:
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");
Hemos concatenado la cadena
/dolphin/MainWindow con
el identificador de la ventana que nos habíamos guarda. En este caso el
método no tiene parámetros y no devuelve tampoco ningún valor.
Esto no ha sido nada
Esto
solo han sido un par de ejemplos básicos, como se puede suponer cada
aplicación será un mundo y habrá que investigar un poco los métodos que
contiene para ver lo que podemos hacer con ella. Por supuesto que no
todas las aplicaciones están preparadas para ser utilizadas mediante
DBus. En fin, todo esto no es más que la punta del iceberg ya que por
ejemplo se pueden usar señales y slots entre aplicaciones distintas de
forma muy parecida a como lo hace QT. En mi caso no me ha hecho falta de
momento nada más que esto pero al que le interese le vale la pena
profundizar más.