lunes, 14 de abril de 2014

Aplicaciones visuales con ZinjaI

Después de algunos años ya de utilizar ZinjaI en combinación con wxFormBuilder, creo que la integración de ambas herramientas ha quedado bastante aceitada. Para quien no lo conozca, wxFormBuilder es una programa que permite "dibujar" interfaces (digamos ventanas para nuestros programas) de forma visual (como si fuera Visual Basic, o  Borland Builder), y luego generar automáticamente el código fuente C++ (o Python) que produce esas interfaces para usarlas en nuestros proyectos. Por un lado nos automatiza un poco una tarea a veces tediosa y repetitiva, y por otro nos ahorra escribir mucho código y consecuentemente también investigar la forma de utilizar la biblioteca de componentes gráficos.

Desde un proyecto de ZinjaI, podemos aprovechar esto, y ZinjaI está particularmente preparado para simplificar mucho la interacción entre ambas partes, y estas características de ZinjaI han mejorado bastante en las últimas versiones. Por eso en este post voy a resumir qué se puede hacer, en líneas generales cómo funciona a nivel del código (y aquí va otro uso interesante del polimorfismo) y los pasos básicos para empezar a aprovecharlo. Espero con esto mostrar lo simple y rápido que es hacer GUIs con esta combinación de herramientas.

 El diagrama muestra qué hace cada parte en un proyecto con wxFormBuilder y wxWidgets.
Más abajo otro diagrama mostrará los pasos para llegar a esto, y cuáles automatiza ZinjaI.

Para empezar, definamos quien es quien en esta cadena. wxFormBuilder (desde ahora wxFB) es el que nos deja "dibujar" las ventanas y obtener esa parte del código fuente. Ese código lo incluimos en un proyecto C++ en ZinaI, para que ZinjaI lo compile. Lo que nosotros tenemos que programar es el comportamiento de esas ventanas, pero ya no el aspecto. Todo ese código hará uso de la biblioteca wxWidgets, por lo que será portable a distintos sistemas operativos. Entonces, los pasos serían, 1) dibujar las ventanas en wxFB y pedirle el código fuente, 2) crear un proyecto configurado para wxWidgets en ZinjaI y agregar ese código generado, 3) completar el código programando el comportamiento. Pero como un programa no se hace de forma tan lineal, sino que se va iterando, es probable que pasemos varias veces por cada uno de estos pasos hasta converger en algo funcional. Ahí es donde ZinjaI aceita un poco la ida y vuelta.

Así se ve wxFormBuilder, a la izquierda tenemos la estructura de las ventanas, 
en el medio vemos el resultado, arriba está la paleta de componentes para agregar, 
y a la derecha cambiamos las propiedades de cada componente.

En ZinjaI tenemos una plantilla para crear un proyecto wxFB (si instalamos el complemento para wxWidgets, y el wxFB por separado). Ese proyecto ZinjaI ya crea a la vez un proyecto wxFB y asocia sus archivos (tanto el de proyecto wxFB como los fuentes que éste genera). Entonces, desde ZinjaI, buscamos el proyecto wxFB en el árbol de archivos y lanzamos desde allí el diseñador para dibujar nuestras ventanas. Si en el diseñador guardamos el resultado, al volver a ZinjaI éste detecta al cambio y se encarga de forma transparente actualizar el código y algunas cosas más. Entonces vemos los cambios reflejados en los fuentes en ZinjaI. Así editamos la apariencia del programa con poco esfuerzo. El código generado tendrá una clase por ventana, cada clase tendrá un atributo por objeto dentro de la ventana (serán punteros a los botones, cuadritos de texto, imágenes, etc), y el constructor de la clase se encargará de inicializar todo y acomodarlo como queremos.

Falta decir entonces cómo se comporta esa ventana. Y para esto utilizamos eventos (eventos son clicks en botones, ingresos de texto en cuadritos, selección de opciones en listas, cierre de ventantas, etc). Marcamos en wxFB qué eventos nos interesan, dándole un "nombre" a cada uno, y wxFB pondrá en la clase de la ventana un método con ese nombre, al que llamará automáticamente (en realidad lo hará wxWidgets) cuando el evento ocurra. Por ejemplo, si quiero que mi aplicación haga algo al hacer click en un botón, en wxFB selecciono el botón, voy a la lista de eventos y le pongo un nombre al OnClick; luego tendré un método con ese nombre en el código generado para programarle adentro lo que quiero que haga. Así de simple... o casi.

Ejemplo del código en ZinjaI. En el árbol de proyecto vemos el proyecto wxFB.
El código de la izquierda (no editable) es el generado automáticamente (clase
VentanaPersona), el de la derecha es el que hay que escribir (clase heredada, que
solo agrega el comportamiento de los eventos de interés).

Aquí entra en acción otra vez el polimorfismo. El problema es el siguiente: si programamos la respuesta a los eventos en los métodos de las ventanas que pone wxFB, cuando cambiemos algo en el diseñador y este deba regenerar el código de las ventanas, puede pisar esos cambios que hicimos. Para evitar ese problema, el diseñador pone los métodos como virtuales, de forma que podamos generar una clase heredada, en un archivo aparte, y hacer los cambios allí. Al regenerar, cambia la clase base, pero permanece intacto lo que implementamos en la herencia. De paso, separa el aspecto de la ventana de su comportamiento, y así además de lograr un código más modularizado, podemos hacer que dos ventanas se vean iguales (dibujarlas una vez) y actúen diferente (heredarlas dos veces). Por ejemplo, una ventana que muestra datos puede servir para solo mostrarlos en un contexto, para editarlos en otro, o para cargar nuevos en un tercero.

Resumiendo, los pasos para crear una aplicación "visual" con estas herramientas son: 1) crear el proyecto en ZinjaI (la plantilla nos deja todo configurado), 2) dibujar las ventanas en wxFB, 3) elegir qué eventos nos interesan en wxFB, 4) generar el código y volver a ZinjaI, 5) hacer las herencias e implementar los eventos en estas herencias. El paso 4 y la primera parte del 5 se hace automáticamente en ZinjaI. Al guardar en wxFB y volver a ZinjaI, detecta los cambios, manda a regenerar el código, y averigua qué ventanas se agregaron para generar las herencias, qué ventantas cambiaron para agregar los nuevos eventos en las herencias que ya estaban generadas, y qué ventanas se eliminaron para eliminar las herencias que ya no se usen. Además, para evitar problemas ZinjaI no nos dejará editar las clases que genere wxFB, solo verlas. Así, podemos iterar entre los pasos 2 y 5 tantas veces como necesitemos, dejando todo el trabajo repetitivo a ZinjaI.

El esquema muestra los pasos, y qué o quién hace cada uno. La parte en rojo es 
la que tenemos que programar, lo demás lo gestionan las herramientas y bibliotecas.

En el sitio de ZinjaI, en la sección Documentos, pueden encontrar un par de tutoriales para empezar a probar esto. El primero (en español) desarrolla una agenda completa, empezando por el modelo de objetos que gestiona los datos, y luego agregándole una interfaz. El segundo (en inglés) apunta directamente a la parte de interfaz, desarrollando un editor de texto muy simple que no necesita un modelo de objetos detrás para su lógica. Ambos fueron escritos para versiones ya desactualizadas de ZinjaI, por lo que verán que algunos pasos son más simples o directos de lo que dicen, pero en esencia el mecanismo es el mismo. Espero que esta información les sea útil y empiecen a desarrollar interfaces para sus programas. Después de renegar aprendiendo con un par de proyectos verán que son capaces de generar toda una interfaz funcional en una tarde.

2 comentarios:

  1. Gracias por el post! La verdad si no lo hubieras publicado probablemente no hubiera echo esto:

    Captura

    La idea es usarlo para controlar una fresadora. Creo que hay un par de controladores disponibles pero estuvo bueno como primer proyecto con wxFB (y wxW en general).

    La verdad que wxFB agiliza mucho el trabajo y usar wxWidgets no es dificil como pensaba.

    ResponderEliminar
  2. No sabía que se podía hacer esto. Muchas gracias por la explicación.

    ResponderEliminar