Gráficas e interfaces gráficas

Figura que muestra el aspecto de los programas que desarrollaremos en la entrada.
Aspecto de los programas que desarrollaremos en la entrada.

¿Requieres graficar los resultados de tus programas de métodos numéricos o procesamiento de señales? ¿Sabes cómo programar determinada funcionalidad pero NO cómo hacer la interfaz gráfica que ahora todos esperan? Cairo y GTK+ son bibliotecas gratuitas que te servirán. Ambas herramientas son muy poderosas y, por lo tanto, complejas. En esta entrada sólo te daré unos ejemplos para que empieces a usarlas en Ubuntu y después tendrás que consultar:

Compilaremos con gcc, que está incluido en Ubuntu. Todo lo que se requiere adicionalmente se obtiene con los comandos siguientes:

sudo apt update
sudo apt install libgtk-3-dev

El tutorial Getting Started with GTK+ inicia diciendo “GTK+ is a widget toolkit“, lo que podríamos traducir inicialmente como: GTK+ es un estuche de widgets. Lo que nos lleva a la preguntas: ¿qué son los widgets?, ¿cómo se pronuncia esa palabra?, ¿sigue siendo una regla básica del español pronunciar como se escribe? Al parecer en España se está usando artilugio como traducción, pero creo que esta palabra no se usa mucho en México, así que le diremos chunche a un widget. En las figuras 1 y 2 están las definiciones de estas palabras según la RAE. La definición en inglés de widget está en la figura 3.

Pantalla definición artilugio.
Figura 1. Captura de pantalla al consultar el Diccionario de la lengua española para la entrada artilugio.
Chunche: Objeto cuyo nombre se desconoce o no se quiere mencionar.
Figura 2. Captura de pantalla al consultar el Diccionario de la lengua española para la entrada chunche – objeto cuyo nombre se desconoce o no se quiere mencionar.
Definición en inglés de widget.
Figura 3. Definición en inglés y “traducción” de widget que se obtuvieron al usar Google Translate.

Entonces: GTK+ es un conjunto de programas que sirven para desarrollar interfaces gráficas; o dicho de otra forma, GTK+ es un estuche de chunches (GTK+ is a widget toolkit). Cada interfaz creada con GTK+ está hecha de chunches.

Los chunches en los programas de ejemplo serán:

  • Una ventana en gtk0.c
  • Una ventana, una rejilla y 17 botones en gtk1.c
  • Una ventana, una rejilla y 17 botones en gtk2.c
  • Una ventana y una superficie para dibujar en grafica.c

Observa que en los cuatro programas de ejemplo hay una ventana, este chunche puede contener a otros como: menús, botones, etiquetas, imágenes, entradas de texto, cajas, adornos, barras de desplazamiento… y entre todos darán forma a la aplicación.

GTK+ se maneja a través de eventos, éstos indican que secciones de código se deben ejecutar en determinado momento. Pero en lugar de seguir con palabras, pasemos a los ejemplos.

gtk0.c

Figura de la ventana que se obtiene al ejecutar gtk0.
Figura 4. Ventana que se obtiene al ejecutar gtk0. Cuando compiles y ejecutes el programa en tu PC, el aspecto de la ventana puede cambiar dependiendo de cómo tengas configurado tu escritorio; en este caso, estoy usando el tema Ambiance (el predeterminado en Ubuntu 16.04). Se ejecuta desde la terminal con el comando: ./gtk0

Este primer ejemplo sólo produce una ventana, como la de cualquier aplicación que se ejecuta en un ambiente gráfico, y permite que ésta se cierre al dar clic sobre x (botón anaranjado en la parte superior izquierda de la figura 4), o al oprimir Alt+F4. El código de gtk0.c es:

#include <gtk/gtk.h>

static void salir(GtkWidget *chunche, gpointer datos)
{
 gtk_widget_destroy(GTK_WIDGET(datos));
 gtk_main_quit();
}

void main (int numero_args, char *argumentos[])
{
 GtkWidget *ventana;

 gtk_init(&numero_args,&argumentos);
 ventana=gtk_window_new(GTK_WINDOW_TOPLEVEL);
 gtk_window_set_title(GTK_WINDOW(ventana),"Ventana vacía");
 gtk_window_set_default_size(GTK_WINDOW(ventana),300,300);
 g_signal_connect(ventana,"destroy",G_CALLBACK(salir),ventana);
 gtk_widget_show_all(ventana);
 gtk_main();
}

Observa que:

  • Sólo se requiere de una directiva include para incorporar a los programas la funcionalidad de GTK, línea 15.
  • En este caso, el programa consta de sólo dos funciones: main y salir.
  • En la función principal (main) se crea el aspecto de la aplicación y se establece a que función llamarán los eventos que ocurran al momento de ejecutar la aplicación.

En main:

  • Se declaran la variables, en este caso sólo un apuntador a la ventana, línea 25.
  • gtk_init es la primera función GTK que se debe usar, línea 27. Ésta se encarga de preparar lo necesario para desarrollar interfaces gráficas usando GTK+.
  • Se crea la ventana de aplicación y se asocia a un apuntador para referirnos a ella, línea 28.
  • Se establecen algunas propiedades de la ventana (título y tamaño), líneas 29 y 30.
  • Se asocia la orden de cerrar la aplicación (destroy) con la ejecución de la función salir, línea 31.
  • Se ordena desplegar la aplicación, línea 32.
  • En la línea 33, se llama a la función que hace que la PC busque eventos en la aplicación. Es decir: se determina si el usuario oprime botones, abre menús, llena entradas, desplaza elementos, o da la orden de cerrar la aplicación. Dicho de otra forma, gtk_main ordena la ejecución de las otras funciones del programa de acuerdo a lo establecido previamente mediante las llamadas a g_signal_connect.

En salir:

  • Se libera memoria al destruir la ventana y los chunches que contiene, línea 19.
  • Se da la orden para que la PC deje de buscar eventos en la aplicación, línea 20.

gtk1.c

Figura que muestra la ventana que se obtiene al ejecutar gtk1.
Figura 5. Ventana que se obtiene al ejecutar gtk1. Observa que los botones son de tamaños diferentes, podrás cambiar esto usando las funciones: gtk_grid_set_column_homogeneous y gtk_widget_set_vexpand. Se ejecuta desde la terminal con el comando: ./gtk1

gtk1.c crea una ventana con un conjunto de botones parecido al del teclado numérico de la PC (figura 5), imprime en la terminal un mensaje cuando se da clic sobre la tecla Hola, y tiene varias formas de salida: como cualquier programa y activando la tecla Adiós. Si optas por usar la tecla Adiós, además se imprimirá un mensaje en la terminal. El código de gtk1.c es:

#include <gtk/gtk.h>

static void salir(GtkWidget *chunche, gpointer datos)
{
 gtk_widget_destroy(GTK_WIDGET(datos));
 gtk_main_quit();
}

static void imprime_hola (GtkWidget *chunche, gpointer datos)
{
 g_print("¡Hola mundo!\n");
}

static void imprime_adios (GtkWidget *chunche, gpointer datos)
{
 g_print("Adiós mundo cruel :)\n");
}

void main (int numero_args, char *argumentos[])
{
 GtkWidget *ventana, *rejilla;
 GtkWidget *b_hola, *b_div, *b_mult, *b_resta, *b_suma, *b_punto, *b_adios;
 GtkWidget *b0, *b1, *b2, *b3, *b4, *b5, *b6, *b7, *b8, *b9;  

 gtk_init(&numero_args,&argumentos);
 ventana=gtk_window_new(GTK_WINDOW_TOPLEVEL);
 gtk_window_set_title (GTK_WINDOW(ventana),"Prueba de botones y señales");
 gtk_window_set_default_size(GTK_WINDOW(ventana),300,300);
 gtk_container_set_border_width(GTK_CONTAINER(ventana),10);
 
 rejilla=gtk_grid_new(); 
 b_hola=gtk_button_new_with_label("Hola");
 b_div=gtk_button_new_with_label("/");
 b_mult=gtk_button_new_with_label("*");
 b_resta=gtk_button_new_with_label("-");
 b_suma=gtk_button_new_with_label("+");
 b_punto=gtk_button_new_with_label(".");
 b_adios=gtk_button_new_with_label("Aadiós");
 b0=gtk_button_new_with_label("0");
 b1=gtk_button_new_with_label("1");
 b2=gtk_button_new_with_label("2");
 b3=gtk_button_new_with_label("3");
 b4=gtk_button_new_with_label("4");
 b5=gtk_button_new_with_label("5");
 b6=gtk_button_new_with_label("6");
 b7=gtk_button_new_with_label("7");
 b8=gtk_button_new_with_label("8");
 b9=gtk_button_new_with_label("9");
 
 gtk_container_add(GTK_CONTAINER(ventana),rejilla);
 gtk_grid_attach(GTK_GRID(rejilla),b_hola,0,0,1,1);
 gtk_grid_attach(GTK_GRID(rejilla),b_div,1,0,1,1);
 gtk_grid_attach(GTK_GRID(rejilla),b_mult,2,0,1,1);
 gtk_grid_attach(GTK_GRID(rejilla),b_resta,3,0,1,1);
 
 gtk_grid_attach(GTK_GRID(rejilla),b7,0,1,1,1);
 gtk_grid_attach(GTK_GRID(rejilla),b8,1,1,1,1);
 gtk_grid_attach(GTK_GRID(rejilla),b9,2,1,1,1);
 gtk_grid_attach(GTK_GRID(rejilla),b_suma,3,1,1,2);
 
 gtk_grid_attach(GTK_GRID(rejilla),b4,0,2,1,1);
 gtk_grid_attach(GTK_GRID(rejilla),b5,1,2,1,1);
 gtk_grid_attach(GTK_GRID(rejilla),b6,2,2,1,1);
 
 gtk_grid_attach(GTK_GRID(rejilla),b1,0,3,1,1);
 gtk_grid_attach(GTK_GRID(rejilla),b2,1,3,1,1);
 gtk_grid_attach(GTK_GRID(rejilla),b3,2,3,1,1);
 gtk_grid_attach(GTK_GRID(rejilla),b_adios,3,3,1,2);
 
 gtk_grid_attach(GTK_GRID(rejilla),b0,0,4,2,1);
 gtk_grid_attach(GTK_GRID(rejilla),b_punto,2,4,1,1);
 
 g_signal_connect(ventana,"destroy",G_CALLBACK(salir),ventana);
 g_signal_connect(b_hola,"clicked",G_CALLBACK(imprime_hola),NULL);
 g_signal_connect(b_adios,"clicked",G_CALLBACK(imprime_adios),NULL);
 g_signal_connect(b_adios,"clicked",G_CALLBACK(salir),ventana);
 
 gtk_widget_show_all(ventana);
 gtk_main();
}

Observa que:

  • El programa consta de 4 funciones.
  • imprime_hola e imprime_adios sólo envían textos a la terminal usando la función g_print. Estas funciones están asociadas a los botones b_hola y b_adios respectivamente.
  • salir es igual que en gtk0. main también es “igual” respecto a que sus funciones son establecer la apariencia de la aplicación, establecer la asociación entre chunches y funciones, y dar la orden de iniciar la búsqueda de eventos en la aplicación.
  • Los botones son de tamaños diferentes, podrás cambiar esto usando las funciones: gtk_grid_set_column_homogeneous y gtk_widget_set_vexpand.

En main:

  • Se declaran 19 variables del tipo GtkWidget, una por cada chunche que constituye la aplicación, líneas 35 a 37.
  • Ventana contiene a una rejilla que contiene los 17 botones.
  • La función que coloca la rejilla dentro de la ventana es gtk_container_add, línea 64.
  • La función que incorpora y localiza los botones en la rejilla es gtk_grid_attach, líneas 65 a 85.
  • Los argumentos 3 y 4 de gtk_grid_attach indican las posiciones (x,y) de los elementos en la rejilla a partir de (0,0). Los argumentos 5 y 6 indican las dimensiones horizontal y vertical del elemento insertado.
  • Un clic en el botón b_adios se liga a dos llamadas a función: la primera imprimirá un texto en la terminal, la segunda dará fin a la aplicación. Líneas 89 y 90.

gtk2.c

Figura que muestra la ventana que se obtiene al ejecutar gtk2.
Figura 6. Ventana que se obtiene al ejecutar gtk2. El cambio en la dimensión de los botones es independiente del método para obtenerlos.

gtk2.c hace lo mismo que gtk1.c, sólo te muestro otra forma de programar en la que se separa la apariencia y el funcionamiento de la aplicación. La apariencia se escribe en un archivo XML con extensión UI. Aquí puedes obtener el archivo XML para gtk2.c, no olvides renombrar el archivo para cambiar la extensión (quedaría gtk2.ui). El contenido de gtk2.c y gtk2.ui es:

#include <gtk/gtk.h>

static void imprime_hola (GtkWidget *chunche, gpointer datos)
{
 g_print("¡Hola mundo!\n");
}

static void imprime_adios (GtkWidget *chunche, gpointer datos)
{
 g_print("Adiós mundo cruel :)\n");
 gtk_main_quit();
}

void main (int numero_args, char *argumentos[])
{
 GtkBuilder *constructor;
 GObject *ventana, *rejilla;
 GObject *b_hola, *b_div, *b_mult, *b_resta, *b_suma, *b_punto, *b_adios;
 GObject *b0, *b1, *b2, *b3, *b4, *b5, *b6, *b7, *b8, *b9;  

 gtk_init(&numero_args,&argumentos);
 constructor=gtk_builder_new();
 gtk_builder_add_from_file(constructor,"gtk2.ui",NULL);
 ventana=gtk_builder_get_object(constructor,"ventana");
 g_signal_connect(ventana,"destroy",G_CALLBACK(gtk_main_quit),NULL);
 b_hola=gtk_builder_get_object(constructor,"b_hola");
 g_signal_connect(b_hola,"clicked",G_CALLBACK(imprime_hola),NULL);
 b_hola=gtk_builder_get_object(constructor,"b_div");
 b_hola=gtk_builder_get_object(constructor,"b_mult");
 b_hola=gtk_builder_get_object(constructor,"b_resta");
 b_hola=gtk_builder_get_object(constructor,"b_suma");
 b_hola=gtk_builder_get_object(constructor,"b_punto");
 b_adios=gtk_builder_get_object(constructor,"b_adios");
 g_signal_connect(b_adios,"clicked",G_CALLBACK(imprime_adios),NULL);
 b_hola=gtk_builder_get_object(constructor,"b0");
 b_hola=gtk_builder_get_object(constructor,"b1");
 b_hola=gtk_builder_get_object(constructor,"b2");
 b_hola=gtk_builder_get_object(constructor,"b3");
 b_hola=gtk_builder_get_object(constructor,"b4");
 b_hola=gtk_builder_get_object(constructor,"b5");
 b_hola=gtk_builder_get_object(constructor,"b6");
 b_hola=gtk_builder_get_object(constructor,"b7");
 b_hola=gtk_builder_get_object(constructor,"b8");
 b_hola=gtk_builder_get_object(constructor,"b9");
 
 gtk_main();
}

Observa que:

  • Se usa un constructor para obtener la apariencia de la aplicación.
  • La declaración de la variable tipo GtkBuilder asociada con el constructor está en la línea 30.
  • En la línea 37 está la instrucción que ordena que se utilice el archivo XML para obtener la apariencia de la aplicación.
  • En este código se sigue estableciendo la relación entre los eventos que ocurren al momento de ejecutar la aplicación y las funciones que conforman el programa. Llamadas a g_signal_connect.
  • Cuando un chunche está asociado a una función, la asociación se escribe inmediatamente después de crear el chunche. Llamadas g_signal_connect inmediatamente después de gtk_builder_get_object.
<interface>
 <object id="ventana" class="GtkWindow">
  <property name="visible">TRUE</property>
  <property name="default-height">300</property>
  <property name="default-width">300</property>
  <property name="border-width">10</property>
  <child>
   <object id="rejilla" class="GtkGrid">
    <property name="visible">TRUE</property>
    <property name="column-homogeneous">TRUE</property>

    <child>
     <object id="b_hola" class="GtkButton">
      <property name="visible">TRUE</property>
      <property name="label">Hola</property>
      <property name="vexpand">TRUE</property>
     </object>
     <packing>
      <property name="left-attach">0</property>
      <property name="top-attach">0</property>
     </packing>
    </child>

    <child>
     <object id="b_entre" class="GtkButton">
      <property name="visible">TRUE</property>
      <property name="label">/</property>
     </object>
     <packing>
      <property name="left-attach">1</property>
      <property name="top-attach">0</property>
     </packing>
    </child>

    <child>
     <object id="b_mult" class="GtkButton">
      <property name="visible">TRUE</property>
      <property name="label">*</property>
     </object>
     <packing>
      <property name="left-attach">2</property>
      <property name="top-attach">0</property>
     </packing>
    </child>

    <child>
     <object id="b_resta" class="GtkButton">
      <property name="visible">TRUE</property>
      <property name="label">-</property>
     </object>
     <packing>
      <property name="left-attach">3</property>
      <property name="top-attach">0</property>
     </packing>
    </child>

    <child>
     <object id="b7" class="GtkButton">
      <property name="visible">TRUE</property>
      <property name="label">7</property>
      <property name="vexpand">True</property>
     </object>
     <packing>
      <property name="left-attach">0</property>
      <property name="top-attach">1</property>
     </packing>
    </child>

    <child>
     <object id="b8" class="GtkButton">
      <property name="visible">TRUE</property>
      <property name="label">8</property>
     </object>
     <packing>
      <property name="left-attach">1</property>
      <property name="top-attach">1</property>
     </packing>
    </child>

    <child>
     <object id="b9" class="GtkButton">
      <property name="visible">TRUE</property>
      <property name="label">9</property>
     </object>
     <packing>
      <property name="left-attach">2</property>
      <property name="top-attach">1</property>
     </packing>
    </child>

    <child>
     <object id="b_suma" class="GtkButton">
      <property name="visible">TRUE</property>
      <property name="label">+</property>
     </object>
     <packing>
      <property name="left-attach">3</property>
      <property name="top-attach">1</property>
      <property name="height">2</property>
     </packing>
    </child>

    <child>
     <object id="b4" class="GtkButton">
      <property name="visible">TRUE</property>
      <property name="label">4</property>
      <property name="vexpand">True</property>
     </object>
     <packing>
      <property name="left-attach">0</property>
      <property name="top-attach">2</property>
     </packing>
    </child>

    <child>
     <object id="b5" class="GtkButton">
      <property name="visible">TRUE</property>
      <property name="label">5</property>
     </object>
     <packing>
      <property name="left-attach">1</property>
      <property name="top-attach">2</property>
     </packing>
    </child>

    <child>
     <object id="b6" class="GtkButton">
      <property name="visible">TRUE</property>
      <property name="label">6</property>
     </object>
     <packing>
      <property name="left-attach">2</property>
      <property name="top-attach">2</property>
     </packing>
    </child>

    <child>
     <object id="b1" class="GtkButton">
      <property name="visible">TRUE</property>
      <property name="label">1</property>
      <property name="vexpand">True</property>
     </object>
     <packing>
      <property name="left-attach">0</property>
      <property name="top-attach">3</property>
     </packing>
    </child>

    <child>
     <object id="b2" class="GtkButton">
      <property name="visible">TRUE</property>
      <property name="label">2</property>
     </object>
     <packing>
      <property name="left-attach">1</property>
      <property name="top-attach">3</property>
     </packing>
    </child>

    <child>
     <object id="b3" class="GtkButton">
      <property name="visible">TRUE</property>
      <property name="label">3</property>
     </object>
     <packing>
      <property name="left-attach">2</property>
      <property name="top-attach">3</property>
     </packing>
    </child>

    <child>
     <object id="b_adios" class="GtkButton">
      <property name="visible">TRUE</property>
      <property name="label">Adiós</property>
     </object>
     <packing>
      <property name="left-attach">3</property>
      <property name="top-attach">3</property>
      <property name="height">2</property>
     </packing>
    </child>

    <child>
     <object id="b0" class="GtkButton">
      <property name="visible">TRUE</property>
      <property name="label">0</property>
      <property name="vexpand">True</property>
     </object>
     <packing>
      <property name="left-attach">0</property>
      <property name="top-attach">4</property>
      <property name="width">2</property>
     </packing>
    </child>

    <child>
     <object id="b_punto" class="GtkButton">
      <property name="visible">TRUE</property>
      <property name="label">.</property>
     </object>
     <packing>
      <property name="left-attach">2</property>
      <property name="top-attach">4</property>
     </packing>
    </child>

   </object>
  </child>
 </object>
</interface>

Observa que:

  • Cada chunche tiene un identificador, propiedades y directivas de empaquetado.
  • Se utilizan pares <child></child> para indicar que rejilla esta dentro de ventana, y que todos los botones están dentro de la rejilla.
  • <property></property> se utiliza tanto para indicar propiedades de los chunches, como para establecer formas de empacado.
  • Cada propiedad tiene un nombre, que se escribe entre comillas, y un valor.

grafica.c

Figura que muestra la ventana que se obtiene al ejecutar grafica.
Figura 7. Ventana que se obtiene al ejecutar grafica.

El último ejemplo de la entrada, usa algunas funciones de Cairo y, muestra cómo graficar la función seno en una superficie de dibujo GTK. También deja ver otra forma de codificar que se encamina al desarrollo de aplicaciones; es decir, programas que se ejecutan desde el lanzador, tienen un icono que los representa, cuentan con una sección de ayuda, etcétera. El código de grafica.c es:

#include <gtk/gtk.h>
#include <math.h>

gboolean grafica(GtkWidget *superficie, cairo_t *contexto, gpointer datos)
{
 guint ancho, alto, n;
 GdkRGBA color;

 ancho=gtk_widget_get_allocated_width(superficie);
 alto=gtk_widget_get_allocated_height(superficie);
 cairo_set_source_rgb(contexto,0,0,0);
 cairo_set_line_width (contexto,1);
 cairo_move_to(contexto,0,alto/2);
 for (n=0; n<ancho; n++)
  cairo_line_to(contexto,n,(alto/2)-sin(2.0*G_PI*n/ancho)*(alto/2.5));
 cairo_stroke(contexto);
 cairo_set_source_rgb(contexto,255,0,0);
 cairo_set_font_size(contexto,20);
 cairo_move_to(contexto,160,40);
 cairo_show_text(contexto,"sin(2*pi*t)");
 cairo_stroke(contexto);
 return FALSE;
}

static void activar(GtkApplication *aplicacion, gpointer datos)
{
 GtkWidget *ventana;
 GtkWidget *dibujo;

 ventana=gtk_application_window_new(aplicacion);
 gtk_window_set_title (GTK_WINDOW (ventana),"Gráfica X,Y");
 gtk_window_set_default_size(GTK_WINDOW(ventana),640,480);
 dibujo=gtk_drawing_area_new();
 gtk_container_add(GTK_CONTAINER(ventana),dibujo);
 g_signal_connect(G_OBJECT(dibujo),"draw",G_CALLBACK(grafica),NULL);
 gtk_widget_show_all(ventana);
}

int main(int numero_args, char **argumentos)
{
 GtkApplication *aplicacion;
 int estado;

 aplicacion=gtk_application_new("olinki.gtk.ejemplo",G_APPLICATION_FLAGS_NONE);
 g_signal_connect(aplicacion,"activate",G_CALLBACK(activar),NULL);
 estado=g_application_run(G_APPLICATION(aplicacion),numero_args,argumentos);
 g_object_unref(aplicacion);
 return estado;
}

Observa que:

  • La función principal (main) se limita a arrancar la aplicación, en este caso llamando a la función activar.
  • En la función activar están las tareas que en los ejemplos previos realiza main.
  • En la función grafica se hace el dibujo y para esto se hace uso de un “contexto” – en el contexto se almacena el tamaño del elemento de dibujo, su color y factor de transparencia, entre muchas otras cosas.
  • Las coordenadas en la superficie de dibujo corresponden al cuarto cuadrante del plano cartesiano.
  • En la línea 25 se ordena pintar con negro indicando 0 contenido de rojo, 0 de verde y 0 de azul. En la línea 31 se ordena cambiar a rojo (255 o todo el rojo posible, 0 de verde y 0 de azul).
  • En la línea 26 se ordena trazar lineas con un pixel de ancho.
  • En las líneas 27 y 33 se ordena colocar el punto de partida para otras ordenes en una coordenada específica.
  • La instrucción de la línea 29 ordena trazar una línea desde la última localización asignada hasta una nueva coordenada.
  • Para escribir en el dibujo se usa la función cairo_show_text.
  • La función cairo_stroke (líneas 30 y 35) ordena pasar lo que se ha almacenado en el contexto a la superficie de dibujo.

En fin, antes de despedirme:

  • Te recuerdo que los objetivos de la entrada son: presentarte las herramientas gratuitas Cairo y GTK+, y facilitarte iniciar su estudio.
  • Te comento que puedes ver la documentación de GTK+ sin estar conectado a Internet mediante el programa DevHelp (también gratuito). Aunque, al parecer, no siempre se observan los códigos de ejemplo (figura 8).
  • Si quieres dibujar directamente en archivos (png, pdf…) te sugiero que además consultes: http://zetcode.com/gfx/cairo/cairobackends/
Figura que muestra captura de pantalla al usar DevHelp.
Figura 8. Captura de pantalla al usar DevHelp. Observa que la PC NO está conectada a Internet, y que NO se ve el código del ejemplo example-0.c.

¡Hasta la próxima!

4 comentarios en “Gráficas e interfaces gráficas”

    1. Las instrucciones para compilar están en los comentarios de cada código (en las versiones que se descargan).
      Observa que grafica.c se compila un poco diferente.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *