Qt Meta-Object System and namespaces

2009/04/26 by Kalvy

In my recent work in KTutorial (implementing scripted tutorials using Kross) I stumbled upon some problems with Qt Meta-Object system and namespaces that I think are worth mentioning.

Note that these comments apply to Qt 4.5.1. In newer versions the described behaviour may be different (and, in the case of the bug, hopefully it will be already fixed it is fixed in Qt 4.5.3.).

The first is more something to take into account than a problem. When a signal or slot is connected (using QObject::connect directly, or indirectly, for example, through QSignalSpy) the types of the arguments should be qualified using the same namespace used in the signal or slot declaration. You may declare it with the class name alone, without namespace, but only if you always use connect with the class name alone. That is, you always use the namespace, or you never use the namespace, but you can’t mix it.

The second, a strange behaviour if you don’t know what’s going on, involves namespaces in signal signatures, QSignalSpy and QVariant. A perfect mix. QSignalSpy is a useful class from QtTest module that can be used to record the emissions for a signal of an object. The arguments are stored as QVariant objects, that act as general containers for data types.

By default, QVariant supports most of Qt data types, but it can also store other types once they are registered. After using Q_DECLARE_METATYPE macro, a type can be stored in a QVariant (provided it has a public default constructor, public copy constructor and public destructor, which holds true for pointers to any type). Without it, the code won’t even compile.

However, when the type is used in the argument of a signal spied by a QSignalSpy, the type must be also registered using qRegisterMetaType. If not done, it complaints in debug output at runtime with “Don’t know how to handle ’scripting::ScriptedTutorial*’, use qRegisterMetaType to register it.”.

The strange behaviour comes into play when namespaces are added to the party. If a signal has an argument that is a type (or a pointer to a type) in a namespace, as said above you can declare the argument with the class name alone. If the type is registered with Q_DECLARE_METATYPE and qRegisterMetaType (with the class name alone, without namespace), QSignalSpy will record the emitted signals, and when the QVariant that stored the argument is queried for the userType(), it will even return the same id as qRegisterMetaType returned when the argument type was registered. Apparently, it works.

However, there is no way to get the value of the argument. When a qvariant_cast is used on the QVariant that stores the argument, a default constructed value is returned. That doesn’t mean that the argument can’t be sent in a signal. If it was connected to a slot, it would have received the true argument value. But qvariant_cast doesn’t know how to manage the argument type.

Why? Because, for qvariant_cast, the class name, and the class name qualified with the namespace are different types. When the type is declared using Q_DECLARE_METATYPE, internally the full qualified name is used. If the name registered in qRegisterMetaType doesn’t includes the namespace, it won’t be recognized by qvariant_cast. So the full qualified name must be used in qRegisterMetaType. And if this is done, the full qualified name must be used also in QSignalSpy, and therefore in the declaration of the signal. So, to sum up: use the full qualified name of classes in signals and slots, you’ll live longer and happier :P

See testSetup() method for an example of all this in KTutorial commit #48.

The last is a true problem, as it is an already reported bug (246221 – QMetaObject::newInstance() returns 0 for class in namespace). The QMetaObject::newInstance method, introduced in Qt 4.5, doesn’t work when the class is in a namespace. The problem seems to be that when the constructor signature is prepared in newInstance code it includes the namespace. However, indexOfConstructor method doesn’t use the namespace, so when the constructor is looked for it isn’t found and no new instance is created. The bug is fixed in Qt 4.5.3.

Note to myself

2009/03/31 by Kalvy

RPATH is great. But remember, when the library is installed in a path included in LD_LIBRARY_PATH, the installed library takes priority over the one pointed by RPATH.

Next time that QTest executables run directly or through Valgrind don’t change their behavior after modifying the library code, but when executed through shell files they do, that’s what is going on.

Yes, pretty obvious. But I’m sure that it is not the first time that you get stuck for an hour in something like that ;) (and that then, after realizing what’s happening, you feel like a moron :P ).

Unit testing a KDE 4 library

2009/03/18 by Kalvy

Here it is, my first English post!

I have still some things to finish (unrelated to KTutorial) before I can get a steady development pace, but until I finish them I’m going to make some sporadic work, mostly fixing problems (not necessarily bugs) to have a better base. For example, improving the unit tests.

I’m an advocate of testing before coding, but I can’t understand how or why in this project I had only 1 class and 4 methods tested. That had to change! So I entered in a test frenzy state and covered most of the code (KDevelop4 coverage plugin reports a healthy 91% coverage) with tests, which also helped to unveil some lurking bugs.

However, I’m not going to talk about the tests themselves, but about the infrastructure for them.

Executing tests without installing the library

When checking the first and (then) only test case I found that it passed when run using the shell wrapper, but not when run calling the executable directly.

Shell wrapper? What are you taking about? Ok, ok, let me explain. When you develop a library, you’re likely developing a dynamic library. That is, the applications using it will link to it when executed, not when compiled, and the library will remain as a separate file in the system.

In GNU/Linux the linker usually looks for the libraries in system directories. So if your test executable is linked against your dynamic library, the linker won’t find it until you install it. Apart from using system wide configuration, you can tell the linker other paths to look for the libraries with LD_LIBRARY_PATH environment variable.

And here is where the shell wrapper appears. The KDE 4 macros for CMake add, for each executable, a script which sets the needed paths in LD_LIBRARY_PATH for the executable to run and then calls it. This way, you can run your executable linked with dynamic libraries that are in the build directory but not installed yet.

But I needed the test executable to work even without the shell wrapper so I can run all the tests through CTest (more on this later). Unlike dynamic libraries, static libraries are linked at compiling time and added to the executable, so they aren’t needed separately.

So I thought “well, let’s create a ktutorial-static library to be used only by test executables and not even to be installed”. However, I wasn’t satisfied with that because the size of each test executable is much larger, specially in full debug mode, as each of them now contain the library. And I also felt that it was just a dirty workaround and there had to be a better solution.

And yes, there was. Reading the KDE 4 Macros documentation I found that KDE4_ADD_UNIT_TEST “adds a unit test [...] (that) will be built with RPATH pointing to the build dir”. Mmm, what was that RPATH thing? CMake wiki gave me the answer: it was just what I was looking for. It’s like hardcoding LD_LIBRARY_PATH in the executable.

But, if the macro sets it, why didn’t it work? After some diving in KDE 4 Macros I concluded that it was a bug and opened RPATH isn’t set for tests added with KDE4_ADD_UNIT_TEST report. The bug is now fixed, so all the things explained here are superfluous, as now KDE4_ADD_UNIT_TEST works out of the box with dynamic libraries. Yes, you have wasted your time reading all this, sorry :P

Running the tests

But, why do I need to execute the tests not using the shell wrapper? In KDE 4, the preferred unit testing library is QtTestLib. In QtTestLib each test case is an executable on its own, unlike other libraries where you can execute a test suite that groups several test cases. So you need a way to run all the tests without needing to run each test executable alone.

CMake provides its own test system, CTest. It has several fancy features, like sending the results of the tests to testing dashboards. But right now I just want to execute all the tests and see whether they pass or not. Fortunately, with CMake and CTest that’s a piece of cake.

CTest uses a CTestTestfile.cmake (or DartTestfile.txt, for legacy purposes) where it is specified the tests for its directory and subdirectories to look for other CTestTestfile.cmake. A CTestTestfile.cmake is created and filled by CMake in each build directory if ENABLE_TESTING command was run, which it is when including KDE4Defaults. CMake macro ADD_TEST adds a new test in the CTestTestfile.cmake of its directory. This macro is called from KDE4_ADD_UNIT_TEST, so using it effectively prepares CTest to run all the tests, even those in subdirectories of the base test directory. However, the tests are executed directly, not through their shell wrapper.

Well, I thought that. But… in fact, the generated CTestTestfile.cmake do use the shell wrapper. Since January 2008… But I was sooo smart to forget to update my KDE environment since then. Don’t ask… (Note, however, that this was written some months ago, although I didn’t publish it until now ;) )

So now CTest works out of the box with KDE 4 Macros. We only have to build the tests (calling CMake with KDE4_BUILD_TESTS=ON variable they will be built with the rest of the project) and run CTest in our build/test directory. It will execute all the tests in that directory and, recursively, in all the needed subdirectories. If we need a more detailed output than the default summary, run ctest with -V or even -VV parameters to see the full test output.

Running QTest test from KDevelop4

A very interesting new feature of KDevelop4 is the support for unit test frameworks (although, as far as I know, only QTest works at this moment). With QTest plugin, you can enable and disable QTest tests to execute and see their results.

With some build systems, you must configure the plugin specifying the executables to be taken into account. However, if your project uses CMake, they are automatically discovered using CTestTestfile.cmake files (there is no problem even if they specify non-QTest tests, as these would be ignored).

There is only a little requeriment: there must be a CTestTestfile.cmake file in the root build directory. So just by using ENABLE_TESTING command in the root CMakeLists.txt file the CTestTestfile.cmake file is created and filled to recursively search in its subdirectories.

The only drawback with this approach is that CTestTestfile.cmake files will be created even in build directories where they aren’t needed (for example, src). However, although it is not the best solution, it is good enough ;)

Macro for adding several tests at once in CMakeLists.txt

In order to add a test, you need two or three CMake macros: KDE4_ADD_UNIT_TEST, TARGET_LINK_LIBRARIES and, optionally, SET (to set a variable with the list of files of the tests). As unit tests usually need just one file, you can avoid creating a variable and set the file in the KDE4_ADD_UNIT_TEST macro without losing legibility.

Anyway, the two calls are almost identical for every test, just changing the name of the test. It will be better to tell CMake just the name of the tests, instead of copy-pasting the calls for each test.

Of course, this can be easily done. As I have no idea about CMake programming, I took a look at KDECORE_UNIT_TESTS macro in CMakeLists.txt for kdecore and tweaked it for my own needs, as seen in KTutorial commit #24.

Preparando la vuelta al trabajo

2008/10/16 by Kalvy

Al trabajo en KTutorial, me refiero ;)

Desgraciadamente estos meses desde mi último mensaje no tuve apenas tiempo libre y no pude continuar con el desarrollo de KTutorial desde entonces. Pero afortunadamente eso cambiará pronto.

Mi intención es continuar el desarrollo de KTutorial como mi proyecto de fin de carrera, por lo que, a no ser que las cosas se tuerzan mucho, para junio confío en haber implementado una serie de nuevas características, acompañadas cómo no de una buena documentación para desarrolladores y usuarios.

Dichas características son, principalmente, las ya mencionadas en su día de tutoriales interpretados e integración con GHNS, junto a una notable simplificación de la edición de tutoriales, puede que mediante un editor gráfico. Confío en conseguir un nivel de desarrollo suficiente como para que pueda interesar a los desarrolladores de KDE y, quién sabe, quizás poder verlo en KDE 4.3 ó 4.4… aunque eso sólo el tiempo lo dirá :)

Aún no me pondré con ello, no obstante, ya que antes necesito terminar algunas tareas que tengo pendientes. Confío en haberlas despachado en unas semanas.

Respecto al Concurso Universitario de Software Libre, este año volveré a presentarme, pero no con KTutorial. Supongo que, dado que voy a implementar nuevas características, podría hacerlo, pero prefiero presentar algo nuevo. No sé, manías mías. Ya veremos qué tal sale, aunque mi intuición me dice que voy a estar muy enredado con KTutorial y apenas voy a poder dedicarle tiempo al otro proyecto. Pero bueno, lo importante es participar :)

Y para acabar, de ahora en adelante seguramente escribiré las entradas en inglés (bueno, “inglés” :P ), ya que considero que es más apropiado dada la naturaleza del blog. Supongo que no era necesario indicarlo y que quien leyese las próximas entradas sería capaz de deducirlo sin ayuda… pero nunca se sabe… :P

¡Hasta dentro de unas semanas!

Versión 0.1 disponible

2008/04/04 by Kalvy

Realmente, ya hace una semana que la publiqué. Pero fue una semana de locura con cosas de clase y no encontré un triste momento para escribirlo aquí en el blog :(  Pero bueno, menos lamentar y más informar ;)

La versión 0.1, así como las instrucciones de compilación e instalación, puede encontrarse en la sección de descargas de la web del proyecto.

Aproveché también para incluir la documentación de la API, un tutorial de uso de la biblioteca (para desarrolladores de aplicaciones, no para usuarios finales) y un documento de diseño. El documento de diseño, no obstante, aún es más un simple borrador que un documento serio.

¿Y ahora? Ahora, lógicamente, a por la versión 0.2. Inicialmente era donde me proponía llegar antes de terminar el concurso,  con algo de tiempo para empezar a revelar la existencia del proyecto a los desarrolladores de KDE y con suerte recibir algo de feedback (la versión española de la palabra no acaba de gustarme ;) ). Pero con todo el lío que tuve estas últimas semanas con cosas de la universidad, eso no va a ser posible. ¡Una pena!

La idea para esta versión 0.2 es poder crear tutoriales interpretados mediante scripts (gracias a Kross), en lugar de compilados en C++, y utilizar Get Hot New Stuff para poder descargar nuevos tutoriales con facilidad.

Así que nada, ahora, a intentar integrar Kross y GHNS en la medida de lo posible con el tiempo del que dispongo. Aún no investigué GHNS, pero Kross parece que no va a ser tan sencillo como pensaba. Mejor dicho, la integración en sí sí es sencilla. Conseguir lo que creía que podría conseguir directamente con ella, no ;) Pero eso es otra historia, y como tal ya habrá tiempo de contarla.

¡Está vivo! Â¡Viiivooo!

2008/02/07 by Kalvy

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 by Kalvy

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 :P ).

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 by Kalvy

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 :P

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 by Kalvy

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.

¡Ya era hora!

2007/12/06 by Kalvy

Por fin tengo algo de tiempo para poder dedicarle al II concurso universitario de software libre. Hace casi un mes que tendría que haber hecho todo lo que hice hoy, pero bueno, más vale tarde que nunca :)

Por fin hay una versión preliminar de la página web de KTutorial. Está en inglés, ya que preferí utilizar la lengua franca del mundo del software libre para poder llegar a un mayor número de personas (siendo optimistas, y creyendo que alguien más aparte de mí puede encontrarle utilidad ;) ).

Lógicamente lo ideal sería una web y documentación localizada en español e inglés (al menos), pero no tengo tiempo material para ello, así que confío en que no haya problema por tener la web y la documentación en inglés.

¿Y ahora? Bueno, lo siguiente será hacer una planificación inicial para ver cómo organizarme.

Lo primero de todo será acotar el alcance de lo que voy a hacer para el concurso de modo que sea algo realista para el tiempo del que dispongo. Una vez que ya sepa qué hacer, haré un diseño base general.

No obstante, tampoco voy a estar mucho tiempo realizando el diseño, ya que la experiencia me demostró que cuando se desarrolla una biblioteca o framework, lo mejor es hacer un diseño inicial, y luego comenzar a implementar y probar poco a poco viendo las necesidades reales y adaptando el diseño, en lugar de intentar prever desde un principio todo.

Respecto al blog, nunca fui demasiado amigo de escribir uno, principalmente porque cuando me pongo a escribir tardo muuucho tiempo en terminar de hacerlo. Pero bueno, también pensé más de una vez que podría serme útil para ver cómo va evolucionando un proyecto. Y como a fin de cuentas en este caso es obligatorio, se intentará hacerlo lo mejor posible. Ya veremos dentro de un tiempo qué tal fue ;)

Y habrá que ir cortando ya el artículo, que va siendo una hora respetable para caer en los brazos de Morfeo :)