Archive for February, 2008

¡Está vivo! ¡Viiivooo!

2008/02/07

Y por fin llegó el día de mostrar a la bestiecilla. Lo cierto es que me gustaría explayarme más, pero tengo examen mañana y no me sobra el tiempo, así que seré breve 😉

Enlazo tres vídeos de KTutorial en funcionamiento a través de una aplicación de prueba:

  • ktutorial-limpiar.ogg: muestra un tutorial muy sencillo y cómo puede moverse el Widget de los tutoriales por la pantalla.
  • ktutorial-mover.ogg: un tutorial ya más complejo (el código presenta filtrado de eventos para poder interceptar pulsaciones del ratón, por ejemplo) que muestra el uso de opciones en los pasos.
  • ktutorial-l10n.ogg: simplemente muestra cómo KTutorial está internacionalizado y puede localizarse como cualquier otra aplicación de KDE (en el vídeo se muestra la localización al español).

Los vídeos fueron realizados utilizando recordMyDesktop, por tanto utiliza sólo formatos libres. En concreto, el contenedor ogg y el formato de vídeo Theora. No hay audio en estos tres vídeos.

Los vídeos deberían estar alojados en la web del proyecto… pero hoy no soy capaz de conectarme mediante SCP a la forja de RedIRIS (me devuelve un hermoso “Lost connection”), así que temporalmente están alojados en otro servidor. ¡Gracias Luis!

Actualización:  los vídeos ya están alojados en la web del proyecto, pues se resolvió el problema de SCP (algo debía haber en el lado del servidor, ya que yo no hice nada más que probar lo mismo días más tarde).

En los próximos días intentaré subir el código fuente del framework y la aplicación de prueba a la forja, así como la documentación de la API en la página web (cuando consiga transferir ficheros a ella, claro).

Oh, huelga decir que los vídeos están bajo licencia Creative Commons Attribution-Share Alike 😉

Bibliotecas dinámicas en KDE 4

2008/02/06

Versión corta: si desarrollas una biblioteca dinámica para KDE, debes marcar tus clases con la macro KNOMBREBIBLIOTECA_EXPORT (de forma que su definición quede como “class KNOMBREBIBLIOTECA_EXPORT nombreClase: public claseBase {…“), y definir esa macro en un archivo de cabecera knombrebiblioteca_export.h (los nombres no tienen por qué ser necesariamente así, pero por seguir la práctica común) con un contenido adaptado a la biblioteca en cuestión. Véase como ejemplo cualquiera de los archivos include/kloquesea_export.h de KDE 4, así como los archivos de cabecera de las clases que los usan.

Para quien quiera aburrirse con la versión larga y detallada de mis aventuras y desventuras hasta llegar a tan sencilla conclusión, a continuación viene la versión larga.

“El miedo es el camino hacia el Lado Oscuro. El miedo lleva a la ira, la ira lleva al odio, el odio lleva al sufrimiento. Veo mucho miedo en ti.” Con estas palabras el maestro Yoda expresaba su preocupación ante el entrenamiento en los caminos de la Fuerza del joven Anakin Skywalker. Pues bien, si hoy el maestro Yoda me hubiese examinado a mí, miedo no habría encontrado. Ahora que de ira, odio y sufrimiento iba servido.

Este cúmulo de sensaciones tan poco agradables eran debidas al cansancio y desesperación acumuladas durante las ¿5? horas que había pasado intentando averiguar por qué cuando convertí la biblioteca KTutorial de estática a dinámica el enlazado desde una aplicación de prueba fallaba (una larga retahíla de “undefined reference to `vtable for NombreClase::metodo()‘” en los lugares de la aplicación de prueba que se usaba la biblioteca).

Pero, ¿por qué? Inicialmente pensé que tal vez habría que hacer algo más que añadir la opción SHARED en el objetivo para crear la biblioteca (“kde4_add_library(ktutorial SHARED ${ktutorial_LIB_SRCS})“) y por ese motivo fallaba. Sin embargo, el amigo Google no me devolvía nada concluyente a este respecto. Según parecía, con añadir esa opción ya era suficiente para crear la biblioteca.

Así que busqué en las kdelibs algún archivo de CMake que generase una biblioteca dinámica, para ver si había algo que se me escapase. La mayoría eran archivos “grandes”, aunque buscando, llegué al archivo de CMake para kdelibs/kde3support/kunittest. Generaba una biblioteca y un ejecutable que enlazaba con ésta, era pequeño y fácil de entender. ¡Excelente!

O no tanto. Porque resulta que el archivo de CMake tenía lo mismo que los que utilizaba yo. Tras varias pruebas, entre ellas aislar esa biblioteca y generarla de 0 (por si acaso CMake utilizaba objetivos heredados de los directorios padre) y ver que funcionaba, supuse que el problema no estaría en los objetivos de CMake. Pero, ¿dónde entonces?

Pensé que el problema podía residir en que la biblioteca dinámica enlazaba con una biblioteca estática temporal. Esta biblioteca estática simplemente contiene las clases de la vista, que están separadas del modelo en un subdirectorio. Me parecía extraño que fuese el problema, ya que anteriormente había trabajado con un esquema similar, aunque en una biblioteca que no era de KDE, y no presentaba problemas. Pero como no sabía dónde buscar, intenté por ese lado. Y como era de esperar… seguía sin funcionar.

Puestos a dar palos de ciego, decidí enlazar la biblioteca no con una aplicación de verdad, sino con la mínima expresión de un programa de C++, a ver qué pasaba. Así que hice simplemente un método main que crease un objeto de una clase de la biblioteca. Enlacé y… ¡funcionó! Por probar, cree un objeto de otra clase, y enlacé de nuevo. Y esta vez, fallaba al enlazar con el nuevo objeto. Ni que decir tiene que miraba incrédulo a la pantalla, esperando que mi querido ordenador sacase un diálogo llamándome inocente y diciéndome que ya paraba con la broma.

Pero en vista de que eso no ocurría, decidí mirar dónde estaba la diferencia entre ambas clases, para que una enlazase y la otra no. Y la diferencia era que la que enlazaba era una clase normal, mientras que la que no, era derivada de QObject. Me encanta Qt, adoro todas sus características, me emociono sólo de pensar en señales y ranuras. Pero en ese instante quise que David el gnomo acabase con todos los trolls (por si alguien no sabe a qué me refiero, Qt es obra de Trolltech, a cuyos empleados se les conoce como “los trolls”. Sí, es el peor intento de chiste que leíste en mucho tiempo, lo sé. Pero qué quieres, estoy cansado 😛 ).

Con todo esto, decidí volver sobre el código de kunittest. El sistema de construcción era igual al que yo usaba, por lo que la diferencia tenía que estar en el código de las clases. Lo primero que advertí era que dichas clases incluían en los cpp los archivos moc generados por la herramienta de Qt para hacer su magia.

No me constaba que en KDE 4 fuese necesario (aunque, hasta donde sé, con KDE 3 sí), pero dado que a esas alturas si alguien me decía que mi problema se solucionaría dando tres vueltas sobre mi mismo y diciendo “Oh todopoderoso enlazador yo te invoco” en arameo antes de construir la biblioteca yo lo intentaría, pues probé incluyendo los archivos moc.

Jamás adivinaríais qué pasó. Exacto, siguió sin funcionar. A punto de subirme a la silla gritando “Je suis Napoléon!”, vi algo que me llamó la antención en un archivo .h de kunittest: “class KUNITTEST_EXPORT Runner : public QObject“. Tenía que ser eso. Miré qué valor tenía la macro, y cuando se construía la biblioteca éste era “KDE_EXPORT“. “Eso suena a exportar símbolos de la biblioteca”, pensé.

Así que hice una prueba rápida y añadí dicha macro en la clase que utilizaba en mi main simplificado y que no enlazaba. Y un coro celestial bajó a arroparme entre cánticos de júbilo y felicidad. ¡¡¡AL FIN!!!

Tras esto, hice los cambios necesarios en la biblioteca para que todas las clases derivadas directa o indirectamente de QObject utilizasen dicha macro, y todo funcionó como era de esperar 🙂

Realmente no estoy seguro de la explicación técnica de por qué ocurre esto. Mis conocimientos sobre enlazado de C++ en Linux no llegan a tanto, y tampoco tengo tiempo de ponerme a investigarlo. Pero sí se la regla de oro: si haces una biblioteca dinámica en KDE, tienes que marcar tus clases públicas para que se pueda enlazar con ellas. Para ver cómo deben marcarse, a la versión corta del artículo me remito 😉

Una vez arreglado esto, a seguir con los objetivos de instalación de CMake para poder usar la internacionalización.

Y tras este artículo es cuando el anterior pasa a ser claramente anacrónico, ya que no pude dedicarme la tarde entera a investigar lo narrado en él, cuando en éste se ve cómo estuve haciendo otra cosa distinta 😉

Añadiendo la entrada “Tutoriales” al menú ayuda de KDE

2008/02/06

El siguiente artículo es un anacronismo. Debería haberlo publicado ya hace varios días, que fue cuando lo escribí, pero no sé por qué no lo hice aún. Así que aquí está, para regocijo y disfrute de pequeños y mayores 😛

Tras una tarde de investigación, lectura de código (ventajas del software libre 🙂 ) y depuración no encontré la manera de que una biblioteca añada automáticamente una entrada en un punto concreto del menú de ayuda de KDE de una aplicación que utiliza XMLGUI (que deberían ser todas).

¿Y qué quiere decir todo eso? Bueno, expliquémoslo un poco más. En KDE 4 se utiliza (por el momento al menos) una tecnología denominada XMLGUI para crear las interfaces gráficas de usuario de las aplicaciones. En un fichero XML se indican cómo estarán organizados el menú y las barras de tareas y qué acciones corresponderán a cada elemento, y luego esas acciones se definen en el propio código de la aplicación.

Los archivos XML que describen la interfaz son jerárquicos, pudiendo mezclarse con otros. De esta forma, puede tenerse un archivo que describe la interfaz común de las aplicaciones de KDE, y luego cada una de éstas puede tener su propio archivo en el que se añaden nuevos elementos. La unión (virtual, no física) de ambos archivos da como resultado la interfaz final.

La mezcla, en el caso de los menús que es lo que nos ocupa, se realiza mediante los elementos Merge, MergeLocal y DefineGroup. Reconozco no tener muy claro cómo funciona el elemento Merge, principalmente porque por más que busqué, no encontré documentación al respecto de los elementos, por lo que lo que sé de ellos es fruto del mero análisis del código fuente y la experimentación.

En cuanto a MergeLocal, se establece en el documento padre de forma que los elementos que en el hijo pertenezcan al mismo menú en el que está se incluyan en lugar del elemento MergeLocal. En lo que respecta a DefineGroup, define la posición en la que se añadirán todos los elementos pertenecientes a un determinado grupo. En el documento hijo simplemente se añade en los elementos deseados el atributo group con el nombre del grupo en cuestión.

Si no existe ninguno de estos elementos de mezcla, los nuevos elementos del documento hijo se añaden al final del menú al que pertenezcan.

El problema se presenta cuando aparece un tercer documento XML. O, mejor dicho, cuando aparece un tercer documento que deba mezclarse… en el MergeLocal del primer documento. Por ejemplo, el que indica dónde debe incluirse en el menú de ayuda una acción correspondiente a una biblioteca utilizada por una aplicación (que, obviamente, era lo que intentaba 😉 ).

El menú de ayuda viene definido, como era de esperar, en el documento de XMLGUI general de KDE: kdelibs/kdeui/xmlgui/ui_standards.rc. A este documento se le añade el documento específico de cada aplicación. Y a estos dos debe añadirse el documento de KTutorial, de forma que se añada una entrada en la interfaz final en el menú de ayuda, y que ésta lógicamente esté ubicada en el espacio habilitado para la mezcla, no al final del menú.

El problema está en que MergeLocal sólo sirve para mezclar el documento padre con el documento hijo inmediatamente inferior. Es decir, el documento general de KDE con el documento de la aplicación. En KXMLGUIClientPrivate::mergeXML(…), en el archivo kxmlguiclient.cpp puede verse cómo, una vez mezclado un documento padre con el hijo, el elemento se elimina.

Podría pensarse en mezclar el documento de la aplicación con el de la biblioteca, y luego todo ello con el general de KDE. Pero tampoco serviría. Dejando a un lado que los métodos que permitirían hacer eso son protegidos (lo que quizás podría salvarse con un sucio apaño en el preprocesador definiendo la cadena “protected” como “public” e incluyendo los archivos de cabecera), el principal problema reside en el código del método KXmlGuiWindow::setupGUI(), el encargado de crear la interfaz gráfica a partir de los documentos de XMLGUI.

En éste se carga primero el documento general de KDE, y luego éste se mezcla con el específico de la aplicación. Y una vez hecho eso, ya se analizan los demás documentos a utilizar. Te pongas como te pongas, eso es algo contra lo que no puedes luchar 🙂

Y entonces, todo esto, ¿en qué acaba? La conclusión es que no se puede habilitar KTutorial en una aplicación únicamente llamando a KTutorial::setup(), como sería deseable, sino que es necesario además modificar el documento XMLGUI que utiliza, definiendo en el menú de ayuda el grupo ktutorial. De este modo, cuando se cargue el documento de KTutorial, los elementos definidos en éste se añadirán al grupo, que a su vez ocupará la posición indicada en el documento general de KDE para la mezcla en el menú de ayuda.

Sí, ciertamente es una investigación muy larga para un problema tan pequeño, pero bueno, sería mejor que funcionase sin tener que añadir nada en el documento de XMLGUI, ¿no? 🙂

Sí, de vez en cuando está bien actualizar

2008/02/04

Ya sabía yo que el blog no iba a ser lo mío 🙂

El caso es que todo este tiempo de silencio no fue tiempo de inactividad (aunque una parte sí, para qué negarlo). De hecho, hace un mes que tengo un borrador de un artículo pendiente de terminar de escribir y publicar… A ver si estos días lo hago, junto con algún otro texto que escribí para mí mismo y que no sé por qué no subí aún al blog.

Pero, ¿en qué estado está el proyecto? Porque en la forja tampoco es que haya mucho (por no decir ningún) movimiento. En contra de lo que pudiese pensarse, el proyecto marcha 🙂

Mis planes iniciales eran hacer unos requisitos y un análisis básicos y a partir de ahí, empezar a diseñar y a implementar poco a poco. Pero la verdad es que el análisis y yo no nos llevamos muy bien, por decirlo suavemente. Así que sí, hice unos requisitos básicos… y luego se me fue la cabeza, y empecé a pensar en el lenguaje de script.

Estuve unos días dándole vueltas al tema del lenguaje, pero no avanzaba nada de nada. Lo único que podría decirse que descubrí es que XML no es nada apropiado para lo que quería hacer en un principio, así que tendré que replantearme esa parte. Pero salvo esa conclusión, la cosa no marchaba.

Hasta que, un día, volviendo de clase en el autobús, adormilado, tuve una revelación. Alcancé el Nirvana de KTutorial, y vi claramente que el enfoque que estaba siguiendo era erróneo. No tengo pruebas gráficas, pero creo que en aquel momento tuve un aura dorada visible alrededor de mí. Vale, sí, quizás esté exagerando un poco…

El caso es que, en lugar de darle vueltas al lenguaje de script, me di cuenta de que lo mejor era olvidarse de él temporalmente, y centrarme en crear el framework en sí que utilizaría. Y eso es lo que hice.

(Aquí irían una serie de acontecimientos ciertamente irrelevantes y que no harían más que alargar un ya de por sí insulso artículo, por lo que elegantemente me lo salto).

A día de hoy, el framework está próximo a una versión inicial digna. En esta versión inicial los tutoriales tienen que estar hechos en C++ y compilados con la propia aplicación a la que pertenezcan en lugar de ser scripts, pero ya habrá tiempo de dar soporte también a scripts en próximas versiones.

Quedan acometer una serie de puntos, como son verificar el funcionamiento de la internacionalización, escribir los objetivos de instalación para CMake (el sistema de construcción de KDE 4) y habilitar pruebas unitarias, que haré en los próximos días (ya, las pruebas unitarias hubiesen estado mejor creadas de antemano 🙂 ).

Cuando eso esté listo, subiré una versión inicial a la forja y comenzaré a trabajar sobre su SVN (ahora mismo estoy con un SVN local). Si no lo hago aún es porque prefiero que la primera versión disponible sea algo digno. Meras manías 🙂

Y así están las cosas. Una vez termine el parón por los exámenes de febrero (que ya están ahí, y habrá que dedicarles algo de tiempo 😉 ) me meteré con alguna de las tareas de mi TODO… Pero sobre eso ya habrá tiempo de escribir.