SDK Multiplataforma en C logo

SDK Multiplataforma en C

Nuevo proyecto

❮ Anterior
Siguiente ❯

Me considero una persona técnica que eligió un gran proyecto y una excelente manera para llevarlo a cabo. Linus Torvalds.


En capítulos anteriores hemos descargado el software, ejecutado los ejemplos y aprendido algo sobre la estructura del SDK. Es el momento de crear una nueva aplicación desde cero.


1. La Aplicación Die

En este capítulo y el siguiente vamos a trabajar sobre una aplicación real (Figura 1), incluida en el directorio src/demo/die del SDK. Es un sencillo programa que permite dibujar la silueta de un dado, manipulando parámetros a través de varios de controles Slider. Aquí solo veremos como crear la solución de Visual Studio.

  • Consulta Die para detalles sobre la aplicación.
  • Consulta Recursos para más detalles sobre el uso de textos e iconos.
  • Consulta Portabilidad para detalles sobre la compilación en macOS y Linux.
  • Captura de la aplicación Die.
    Figura 1: Aplicación Die. Inspirado en DieView (Cocoa Programming for OSX, Hillegass et al.).

2. Soluciones y proyectos

En Visual Studio (y Xcode o GNU-Make) existe el concepto de solución que es, en esencia, un contenedor de proyectos que guardan algún tipo de relación. Por su parte, un proyecto es un programa ejecutable o una librería. Estas soluciones implementan los guiones de compilación que permiten generar, enlazar, ejecutar y depurar las aplicaciones. Al abrirla, Visual Studio cargará automáticamente todos los proyectos que contenga. Como ya vimos en Inicio rápido, CMake crea la solución NAppGUI totalmente configurada y lista para compilar, con todas las librerías y demos del SDK. Para ello se basa en el archivo /src/CMakeLists.txt. Si queremos crear una nueva aplicación, tan solo deberemos añadir la siguiente línea al final del archivo:

Listado 1: src/CMakeLists.txt
1
desktopApp(Die2 die "" NRC_EMBEDDED NO)
Utilizamos Die2 porque ya existe la aplicación Die como parte de las demos. VisualStudio no permite tener dos proyectos con el mismo nombre.
  • Una vez listo, abre CMake.
  • Where is the source code: C:\nappgui\src.
  • Where to build the binaries: C:\nappgui_build.
  • Pulsa [Configure]: Si es la primera vez que generas el proyecto, CMake pedirá el compilador (utilizamos Visual Studio 16 2019). Si previamente realizaste los puntos del apartado Compilar los ejemplos tan solo actualizará la solución actual.
  • Pulsa [Generate] y luego [Open Project].
  • Verás que CMake ha creado un nuevo proyecto llamado Die2 dentro de la solución (Figura 2).
  • Captura del Explorador de Solución de Visual Studio donde aparece el nuevo proyecto.
    Figura 2: Proyecto Die2 recién añadido a la solución.

Si compilas y ejecutas Die2, te darás cuenta de que no es otra cosa que el Hello, World!, ya que esta es la plantilla predeterminada para cada nueva aplicación de escritorio. También se ha asignado un Icono de aplicación por defecto, pero lo podremos cambiar posteriormente. Si profundizamos en la sintaxis del comando desktopApp que acabamos de añadir al CMakeLists.txt, tenemos:

1
2
3
desktopApp(Die2 die "" NRC_EMBEDDED NO)

desktopApp(appName path depends nrcMode installer)
  • appName: El nombre de la aplicación.
  • path: Ruta relativa dentro de la solución donde se ubicará el proyecto (en este caso C:\nappgui\die). Se admite cualquier profundidad de ruta. Por ejemplo, "games/die" creará el proyecto en C:\nappgui\games\die y "demo/games/die" en C:\nappgui\demo\games\die.
  • depends: Todas las aplicaciones enlazan de forma predeterminada con las librerías del SDK de NAppGUI. Si la aplicación necesitara dependencias adicionales, como librerías creadas por el propio usuario, las escribiremos en este campo separadas por punto y coma (pe. "physics;render"). Como en este proyecto no tenemos ninguna pasamos una cadena vacía ("").
  • nrcMode: Cómo se administrarán los recursos. Por el momento, NRC_EMBEDDED. Profundizaremos en ellos en el siguiente capítulo Recursos.
  • installer: Admite dos valores YES o NO que indican si hay que crear un instalador para la aplicación. Más información en Instaladores.
Puedes crear tantas nuevas aplicaciones como quieras. Solo debes que repetir el proceso, añadiendo nuevos desktopApp() al script CMakeLists.txt.

3. Añadir archivos

Volviendo al proyecto Die, vemos que solo tiene un archivo de código fuente (die.c). Para este caso podría ser suficiente, pero vamos a dividir el código en varios archivos, ya que será lo habitual en, prácticamente, cualquier nueva aplicación que abordemos. Para agregar nuevos archivos fuente, simplemente los creamos a través del explorador de archivos:

  • Ir a C:\nappgui\src\die utilizando el Explorador de Windows.
  • Clic derecho->Nuevo->Documento de texto. Renombra a die.hxx. En este archivo incluiremos las definiciones de tipos compartidos.
  • Repite y crea dgui.h y dgui.c. Aquí implementaremos el GUI (Figura 3).
  • Captura del Explorador de Windows, donde se están creando nuevos archivos.
    Figura 3: Añadiendo nuevos archivos al proyecto, directamente desde el explorador de archivos.
  • Crea una nueva carpeta dentro de die. Cámbiale el nombre a draw.
  • Abre draw y añade dos archivos: ddraw.c y ddraw.h. Aquí vamos a implementar las funciones de dibujo.

Pulsa [Generate] en CMake para actualizar la solución. Visual Studio nos informa que ha habido cambios en el proyecto Die (Figura 4). Simplemente presiona [Reload Solution] y se agregarán los nuevos archivos (Figura 5).

Advertencia que muestra Visual Studio, al detectar que se han añadido nuevos archivos.
Figura 4: Visual Studio advierte sobre nuevos archivos. Pulsa [Reload Solution].
Los nuevos archivos, dentro del árbol del proyecto de Visual Studio.
Figura 5: CMake clonará el sistema de archivos del proyecto dentro de la solución.
El contenido de cada uno de estos archivos lo tienes en /src/demo/die y /src/demo/casino. Próximamente, en Librerías veremos como compartir código entre aplicaciones.

Tras hacer clic en [Generate], CMake analiza los archivos y subcarpetas del proyecto y actualiza la solución. Establece el INCLUDE_PATH, agrupa los fuentes en carpetas y revisa los recursos. Es necesario presionar [Generate] al agregar o eliminar archivos del proyecto. Si solo editamos el código no es necesario, ya que la estructura del proyecto no ha cambiado. Con volver a compilar será suficiente.


4. Aplicaciones de línea de comandos

Hace unos cuantos años, cuando no existían los entornos gráficos o estaban en fase experimental, los ordenadores se manejaban a través de una terminal de texto. El usuario tecleaba comandos y la máquina respondía imprimiendo mensajes o datos en la pantalla. Hoy en día nuestro diálogo con la aplicación se lleva a cabo a través de clics de ratón o directamente con el propio dedo. No obstante, este tipo de aplicaciones "de consola" siguen estando muy vivas a día de hoy, sobre todo en lo relativo a la automatización de tareas.

La automatización consiste en conseguir que el ordenador haga cosas sin que un operador humano esté constantemente presente. Un ejemplo lo tenemos en (Figura 6), donde hemos improvisado una sencilla aplicación para cambiar el tamaño de imágenes. La operativa es sencilla: Abrir la imagen, seleccionar la nueva medida y pulsar el botón [Resize].

Captura de una hipotética aplicación de escritorio para redimensionar imágenes.
Figura 6: Sencilla aplicación para redimensionar imágenes.

El problema aquí es que somos nosotros los que, constantemente, tenemos que guiar al programa paso a paso: Abre, edita, guarda, abre, edita, guarda, ..., etc. Si tuviésemos que redimensionar 100 imágenes la tarea se haría monótona, repetitiva y muy muy cara. Una versión de la misma aplicación por línea de comandos sería algo así.

1
imgresize C:\imgs\nature.jpg -w320 -h160 C:\imgs2\nature.jpg

Podemos pensar que no hemos ganado mucho. Hemos pasado de realizar unos cuantos clics a tener que escribir una parrafada para realizar la tarea. Nada más lejos de la realidad. La primera ventaja es la facilidad con la que podemos procesar lotes de imágenes:

Listado 4: Script en Python para procesar múltiples imágenes.
1
2
3
4
import os
for file in os.listdir("C:\imgs"):
    if file.endswith(".jpg"):
        subprocess.run(["imgsize", file, "-w360", "-h160", "C:\imgs2\" + file])

Si en el directorio tuviésemos 1000 imágenes, este programa tardará unos pocos segundos (en el peor de los casos) y nosotros todo un día (en el mejor). También podríamos mejorar el script haciendo que suba las nuevas imágenes a un servidor remoto, por ejemplo.

Como podrás imaginar, para crear una aplicación de consola, volveremos al CMakeLists.txt de nuestra solución y añadiremos una nueva línea al final del mismo:

Listado 5: src/CMakeLists.txt
1
2
3
commandApp("utils/imgresize" "" NRC_NONE)

commandApp(appPath depends nrcMode)

Los parámetros son muy simulares a los de las aplicaciones de escritorio:

  • appPath: Directorio de la aplicación dentro de la solución.
  • depends: Dependencias. Una aplicación de línea de comandos añade siempre una dependencia con Core, ya que esta librería proporciona un montón de utilidades que enriquecen mucho el lenguaje C. Librerías como INet, Draw2D y Geom2D tampoco requieren de interfaz gráfica y podrían añadirse como dependencias si fuera necesario:
  • 1
    
    commandApp("utils/imgresize" "draw2d;inet" NRC_NONE)
    
  • nrcMode: NRC_NONE, NRC_EMBEDDED o NRC_PACKED. Distribución de recursos.

Tras el [Generate] de CMake, en Visual Studio se ha creado el nuevo proyecto. Ni que decir tiene que el añadir nuevos archivos se hace de forma similar a lo visto en aplicaciones de escritorio, no lo repetiremos. Si navegamos hasta el código fuente, CMake ha creado una sencilla plantilla:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
/* NAppGUI Console Application */
    
#include "coreall.h"

int main(int argc, char *argv[])
{
    unref(argc);
    unref(argv);
    core_start();
    bstd_printf("Hello world!\n");
    core_finish();
    return 0;
}

Que es la típica plantilla de un programa en C, a la que se le ha incluido el soporte de la librería core. A partir de aquí, ya podemos modificar el código y compilar. CMake ya lo configuró todo por nosotros.

❮ Anterior
Siguiente ❯