Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha...

25
Programación Mª Dolores Molina Programación Gráfica en C++ Introducción Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemos adquirido los conocimientos básicos, aprendiendo a desarrollar programas que incluyen una interfaz gráfica para comunicarse con el usuario. En nuestro caso vamos a desarrollar programas para el sistema operativo Linux utilizando el conjunto de bibliotecas multiplataforma GTK+. Para utilizar GTK+ con el lenguaje de programación C++ usaremos la interfaz gtkmm, que por defecto ya tenemos instalada en el sistema operativo Linux para la mayoría de sus versiones. En este manual se van a incluir algunos conceptos previos que no han sido tratados con anterioridad pero que son necesarios para poder utilizar gtkmm. Instalación de las librerías Antes de poder utilizar la biblioteca gtkmm necesitaremos instalar los siguientes paquetes: pkg-config: se instala con la instrucción sudo apt install pkg-config. Este programa gestiona las opciones de compilación cuando utilizamos librerías como la gtkmm, nos ahorra tener que indicar la ubicación de todos los ficheros de cabecera y de las librerías que vamos a utilizar en nuestra compilación libgtkmm-3.0-dev: se instala con la instrucción sudo apt install libgtkmm-3.0-dev. Este paquete contiene los ficheros de desarrollo necesarios para ser utilizados con la librería gtk. Para entender como funciona la gtkmm necesitaremos antes aprender algunos conceptos de programación avanzada que no hemos incluido en manuales anteriores. Conceptos previos: Punteros Antes de empezar a utilizar gtkmm necesitaremos aprender el concepto de puntero ya que de lo contrario no podríamos comprender la definición de la mayoría de funciones que vamos a emplear. Un puntero es una variable cuyo valor almacena la dirección de memoria en la que se encuentra almacenada otra variable, es decir, su contenido es la posición de memoria que ocupa otra variable.

Transcript of Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha...

Page 1: Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemos adquirido

Programación Mª Dolores Molina

Programación Gráfica en C++

Introducción

Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemosadquirido los conocimientos básicos, aprendiendo a desarrollar programas que incluyen una interfazgráfica para comunicarse con el usuario. En nuestro caso vamos a desarrollar programas para elsistema operativo Linux utilizando el conjunto de bibliotecas multiplataforma GTK+. Para utilizarGTK+ con el lenguaje de programación C++ usaremos la interfaz gtkmm, que por defecto yatenemos instalada en el sistema operativo Linux para la mayoría de sus versiones. En este manual sevan a incluir algunos conceptos previos que no han sido tratados con anterioridad pero que sonnecesarios para poder utilizar gtkmm.

Instalación de las libreríasAntes de poder utilizar la biblioteca gtkmm necesitaremos instalar los siguientes paquetes:

• pkg-config: se instala con la instrucción sudo apt install pkg-config. Este programa gestionalas opciones de compilación cuando utilizamos librerías como la gtkmm, nos ahorra tenerque indicar la ubicación de todos los ficheros de cabecera y de las librerías que vamos autilizar en nuestra compilación

• libgtkmm-3.0-dev: se instala con la instrucción sudo apt install libgtkmm-3.0-dev. Estepaquete contiene los ficheros de desarrollo necesarios para ser utilizados con la librería gtk.

Para entender como funciona la gtkmm necesitaremos antes aprender algunos conceptos deprogramación avanzada que no hemos incluido en manuales anteriores.

Conceptos previos: Punteros

Antes de empezar a utilizar gtkmm necesitaremos aprender el concepto de puntero ya que de locontrario no podríamos comprender la definición de la mayoría de funciones que vamos a emplear.

Un puntero es una variable cuyo valor almacena la dirección de memoria en la que se encuentraalmacenada otra variable, es decir, su contenido es la posición de memoria que ocupa otra variable.

Page 2: Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemos adquirido

Programación Mª Dolores Molina

Para definir y utilizar punteros en C++ se utilizan los operadores dirección & (que ya hemosutilizando cuando aprendimos a definir parámetros por referencia en las funciones) e indirección *.

El operador & nos da la dirección de memoria que ocupa una variable. El operador * aplicado sobreuna variable de tipo puntero da acceso al contenido que almacena la variable a la que el punteroapunta. Veamos un ejemplo:

int v=20; /*declaración de una variable de tipo entero con valor 20 */int *p; /*declaración de un puntero que apunta a una variable de tipo entero */

p=&v; /*la variable puntero almacena la dirección que ocupa la variable v */ /* se dice que puntero apunta a v */

cout >> *p; /* mostraremos por pantalla un 20 que es el contenido que almacena */ /* la variable a la que apunta puntero */

El puntero solo puede hacer referencia a variables del mismo tipo al que ha sido declarado, es decir,si definimos un puntero de tipo entero solo puede apuntar a variables de tipo entero.

Los punteros se utilizan como ya hemos visto para pasar parámetros por referencia en las funcionesy muchas de las funciones que vamos a utilizar para la creación de nuestro entorno gráfico utilizanpunteros.

Parámetros de la función mainEn todos los ejemplos que hemos utilizado en los manuales anteriores la función main no recibeningún parámetro recordemos que la forma de declarar la función principal era con las instrucción:

int main(void)

Indicando con la palabra reservada void que la función no recibía ningún parámetro desde línea decomandos.Pero es posible pasar parámetros desde línea de comandos a la función principal para elloutilizaremos la siguiente sintaxis:

int main(int argc, char *argv[])

Los parámetros de la función main en C++ están estandarizados, es decir, vienen predefinidos pordefecto en el lenguaje de programación. El primero es un entero que indica el número deparámetros que le vamos a pasar a la función y el segundo es un puntero a cadena de caracteres quealmacena los argumentos que le hemos pasado a la función. La variable argc toma valores positvosy como mínimo valdrá 1, ya que el nombre del programa se toma como primer argumento,almacenado con argv[0], que es el primer elemento de la matriz. Cada elemento del vector apunta aun argumento de la línea de órdenes separados por espacios. Todos los argumentos de la línea deórdenes se tratarán como cadenas de caracteres.

Veamos un ejemplo en el que llamamos a la función main pasando parámetros desde la línea decomandos:

Page 3: Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemos adquirido

Programación Mª Dolores Molina

#include <iostream>using namespace std;

main(int argc, char * argv[]) /* declaración de la función con sus parámetros */{

if(argc!=2) /* comprobamos que en la llamada se hayan puesto dos cadenas */{ cout << "Ha olvidado su nombre" << endl; /* si no la hemos puesto avisamos al usuario */ exit(1); /* salimos del programa con un código de error */}cout << "Hola" << argv[1] << endl; /* si todo es correcto imprimiremos el saludo */

return (0);}

Para ejecutar este programa escribiremos en la terminal el nombre del programa y el nombre delusuario que llama al programa separado por un espacio.

El tipo autoLa palabra reservada auto nos sirve para que el compilador decida automáticamente el tipo másapropiado para una variable. Como puedes imaginar esto soluciona todos muchos errores de sintaxisya que nos ahorra tener que estar pensando en qué tipo concreto estamos usando, también nosahorra escribir código cuando la definición del tipo de dato es demasiado larga y hace que escribirun código en teoría simple se convierta en algo difícil de leer y mantener por parte del programador.

Veamos varios ejemplos: auto x = 1.0; // deduce por el valor que le hemos asignado que x es de tipo doubleauto b = x; // b es una copia de xauto& c = x; // c es un puntero a x ya que detrás del tipo auto le hemos puesto el operador dirección

El operador de resolución de ámbitoEl operador :: de acceso a ámbito o resolución de ámbito, permite acceder a un tipo, objeto,función o enumerador incluso si su identificador está oculto. Por ejemplo, anteponiéndolo a unidentificador global, permite acceder al mismo incluso cuando es ocultado por una redeclaracióncon el mismo nombre de una variable de tipo local. Veamos un ejemplo:

int x = 2; // Variable global...void main() { int x = 3; // Variable localstd::cout << "X = " << x << std::endl; // x local muestra el valor 3

std::cout << "X = " << ::x << std::endl; // x global muestra el valor 2}

Page 4: Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemos adquirido

Programación Mª Dolores Molina

En el ejemplo anterior, la variable local x oculta momentáneamente a la global del mismo nombre.Sin embargo, como puede verse, el operador :: antepuesto a la variable señala que no se trata de laversión local, sino la perteneciente al ámbito global.

Como hemos explicado este operador también se utiliza para realizar la declaración método cuandoesta se realiza fuera de la clase. Cuando utilizamos este operador en la declaración de un método ledice al compilador a que clase pertenece dicho método, es decir, :: declara que una función seencuentra en el ámbito de una determinada clase. Varias clases diferentes pueden usar los mismosnombres de función (sobrecarga). El compilador sabe qué función pertenece a qué clase y esto esposible por el operador de resolución de ámbito unido a el nombre de la clase. El siguiente códigomuestra un ejemplo de cómo utilizar el operador resolución de ámbito en la declaración del métodofuera de la definición de su clase.

#include <iostream>#include <cstring>using std::cout;using std::endl;

// Esto define la clase ClaseCadenaclass ClaseCadena {public:    char buffer[255];  //declaración del atributo buffer    void m_rellena(const char *cadena);  //declaración del método rellena};// implementación del método  m_rellena() para la clase ClaseCadena// como vemos lo hemos definido fuera de la definición de la clase y por eso necesitamos el// operador :: que va a asociar la definición de la función con su clasevoid ClaseCadena::m_rellena(const char *cadena){    strcpy(buffer, cadena);  //copia la cadena que le pasemos como parámetro al    return; // atributo de la clase buffer}

int main (int argc, char **argv){    //llamada a la función main con argumentos    // crear 2 objetos ClaseCadena    ClaseCadena cadena1, cadena2;

    cadena1.m_rellena("Inicializando el objeto cadena1");    cadena2.m_rellena("Inicializando el objeto cadena2");           cout << "buffer en cadena1: ";    cout << cadena1.buffer << endl;   // tenemos acceso a buffer al ser público.

    cout << "buffer en cadena2: ";    cout << cadena2.buffer << endl;

return (0);}

Page 5: Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemos adquirido

Programación Mª Dolores Molina

Un ejemplo simplePara iniciar nuestra introducción a gtkmm, vamos a empezar con el programa más simple posible.Este programa va a crear una ventana vacía de 200 x 200 píxeles.

10 #include <gtkmm.h>using namespace Gtk;

20 int main(int argc, char argv[]) 30 {40 auto app =Gtk::Application::create(argc, argv,"org.gtkmm.examples.base");

50 Window window60 window.set_default_size(200, 200);

70 return app->run(window);80 }

Vamos a ir analizando cada una de las líneas que componen nuestro programa:• La línea 10 incluye las cabeceras gtkmm. El fichero gtkmm.h incluye el kit completo de

funciones gtkmm. Esto no suele ser una buena idea, ya que incluye casi un megabyte decabeceras, pero para programas sencillos, basta.

• La línea 20 incluye la llamada a la función principal main, incluyendo los posiblesparámetros que le podamos pasar a la función.

• La línea 40 crea el objeto app del tipo Application (no se especifica el tipo ya que la palabrareservada auto hace que el compilador elija el tipo de dato más adecuado para el objeto queestamos definiendo y así nos ahorramos escribir código y posibles errores de sintaxis comohemos explicado anteriormente) y lo almacena en un puntero. Esta instrucción es necesariaen toda las aplicaciones en la que utilicemos gtkmm. El método create() del objeto appinicializa gtkmm y verifica los argumentos que le hemos pasado en la línea de comandoscuando hemos llamado a nuestra aplicación, buscando opciones estándar tales como--display. Toma los argumentos que reconoce de la lista, dejando cualquier otra cosa paraque nuestra aplicación la procese o la ignore. Esto asegura que todas las aplicaciones gtkmmacepten el mismo conjunto de argumentos estándar.

• La línea 50 define el objeto window del tipo Window.

• La línea 60 establece la propiedad tamaño por defecto para el objeto window a 200 px deancho y 200 px de alto.

• La línea 70 muestra la ventana y entra al bucle principal de gtkmm, que terminará cuando laventana se cierre. La función principal terminará su ejecución con una orden return quedevolverá un valor entero indicando si la ejecución se ha completado con éxito o se haproducido algún tipo de error.

Para compilar este programa escribiremos en la terminal la siguientes instrucción:

g++ base.cpp -o base `pkg-config gtkmm-3.0 --cflags –libs`

Page 6: Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemos adquirido

Programación Mª Dolores Molina

Para simplificar la compilación, se usa pkg-config, que está presente en todas las instalacionescorrectas de gtkmm. Este programa «sabe» qué opciones de compilación son necesarias paracompilar los programas que usan gtkmm. Antes de tener pkg-config cada paquete y bibliotecadebían tener su propio script de configuración para pasar las opciones necesarias de compilación yenlazado. Ahora cada paquete simplemente instala un fichero package.pc que contiene informaciónsobre el destino su instalación, de qué otros paquetes depende, y qué opciones de compilador yenlazador necesita. Debemos colocar las comillas para que la llamada a pkg-config sea consideradadentro de la llamada al compilador y utilice la salida que devuelve como parte de la instrucción decompilación. La opción --cflags hace que pkg-config devuelva la lista de carpetas en las que elcompilador buscará los archivos de cabecera; la opción --libs solicita la lista de bibliotecas a las queel compilador enlazará y las carpetas en las que encontrarlas.

¿Qué son los Widgets?Las aplicaciones gtkmm están compuestas por ventanas, estas a su vez contienen widgets, talescomo botones y cuadros de texto. Cada widget contenido en una ventana de una aplicación debetener su definición como objeto en el código de dicha aplicación, es decir, si queremos utilizar unbotón deberemos incluir la definición del objeto botón para poder utilizarlo en nuestro programa.Para interactuar con un widget visible solo es necesario llamar a uno de los métodoscorrespondientes a dicha clase de objeto, por ejemplo el método show_all_children() que muestrauna ventana y todo lo que contiene en su interior.

Existen widgets contenedores, es decir, cuya única función es contener en su interior a otros widgetscomo las cajas, los marcos, las tablas …En este manual vamos a describir el funcionamiento de loswidgets mas utilizados.

¿Qué son las señales?En gtkmm, como la mayoría de kits de herramientas de la IGU (Interfaz Gráfica de Usuario), estádirigido por eventos. Cuando ocurre un evento, como la pulsación de un botón del ratón sobre unwidget, éste emitirá la señal apropiada. Cada widget puede emitir un conjunto de señales diferente.Para hacer que la pulsación de un botón resulte en una acción, establecemos un manejador deseñales para recoger la señal «clicked» del botón. El manejador de señales puede ser cualquier tipode función pero normalmente se suele definir como un método de la clase correspondiente al objetocon el que se conecta.

El entorno gtkmm utiliza la librería libsigc++ para implementar señales. En el siguiente ejemplopodemos ver el código necesario para conectar la señal “clicked” de un botón con su manejador alque hemos llamado boton_clic.

boton.signal_clicked().connect( sigc::mem_fun(*this,&HelloWorld::boton_clic) );

Los manejadores de señales nos permitirán incluir el código necesario para establecer que accionesqueremos que se ejecuten cuando se active una señal determinada, en el ejemplo, cuando hagamosclic en el botón.

Page 7: Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemos adquirido

Programación Mª Dolores Molina

Un programa en varios fuentesCuando un fichero fuente se hace muy grande o cuando un programa debe ser realizado por variosprogramadores al mismo tiempo se suele dividir el código en varios ficheros fuentes.

En C++ existen principalmente dos tipos de ficheros, los de cabecera (normalmente .h o .hpp) y losficheros fuentes (normalmente con extensión .cpp). La diferencia entre ellos es puramenteconceptual, ambos son ficheros de texto plano el compilador no distingue entre uno y otro, solo esuna diferenciación que es necesaria para los programadores.

Un programador puede crear sus propios ficheros de cabecera para posteriormente incluirlos con ladirectiva #include como hemos hecho con los ficheros de cabecera de la librería estándar. En losficheros de cabecera se suelen incluir los siguientes elementos:

• Definición de estructuras y clases. • Definición de tipos (typedef).• Prototipos de funciones. • Variables Globales (ver más adelante). • Constantes • Macros #define

Veamos un ejemplo.

El código fuente del fichero cabecera sería el siguiente:

/* fichero ejemplo.h que incluye la declaración de funciones*/

void uno(); /* Prototipos de las funciones uno y dos*/void dos(int numero);

Y el código fuente del fichero principal sería el siguiente:

/*fichero ejemplo.ccp que incluye el fuente principal y el fichero de cabecera ejemplo.h*/

#include <iostream>#include "ejemplo.h"

int main(){

cout << “Estamos en el cuerpo del programa” << endl;uno();dos(3);return 0;

}

Aquí es importante recordar la diferencia en la forma de indicar los dos ficheros de cabecera:

<stdio.h> Se indica entre corchetes angulares porque el fichero de cabecera es propio delcompilador (el ordenador lo buscará en los directorios del compilador).

"ejemplo.h" Se indica entre comillas porque el fichero cabecera es nuestro (el ordenador lo buscaráen el mismo directorio que están nuestros fuentes).

Page 8: Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemos adquirido

Programación Mª Dolores Molina

Finalmente, conviene hacer una consideración: si varios fuentes distintos necesitaran acceder aejemplo.h, deberíamos evitar que este fichero se incluyese varias veces, ya que tendríamosdeclaraciones redundantes que nos generarían errores de compilación. Esto se suele conseguirdefiniendo una variable simbólica la primera vez que se enlaza, de modo que podamos comprobar apartir de entonces si dicha variable está definida, con #ifdef, así:

/* ejemplo.h mejorado */

#ifndef _ejemplo_h#define _ejemplo_hvoid uno(); /* Prototipos de las funciones */void dos(int numero);#endif

La directiva ifndef comprueba si se ha declarado la variable simbólica ejemplo_h, si no la encuentrala define e incluye las definiciones de las funciones, si la variable ya existe pasaría directamente a lainstrucción #endif para evitar tener una duplicada la definición de ambas funciones.

Métodos constructores y destructoresde una Clase

Cuando creamos una nueva clase es necesario definir dos funciones especiales llamadas constructory destructor de clase. Los constructores son funciones miembro especiales que sirven para inicializar un objeto de unadeterminada clase al mismo tiempo que se declara. Los constructores son especiales por variosmotivos:

• Tienen el mismo nombre que la clase a la que pertenecen. • No tienen tipo de retorno, y por lo tanto no retornan ningún valor. • No pueden ser heredados. • Por último, deben ser públicos, no tendría ningún sentido declarar un constructor como

privado, ya que siempre se usan desde el exterior de la clase, ni tampoco como protegido, yaque no puede ser heredado.

Los destructores son funciones miembro especiales que sirven para eliminar un objeto de unadeterminada clase. El destructor realizará procesos necesarios cuando un objeto termine su ámbitotemporal, por ejemplo liberando la memoria dinámica utilizada por dicho objeto o liberandorecursos usados, como ficheros, dispositivos, etc.Al igual que los constructores, los destructorestambién tienen algunas características especiales:

• También tienen el mismo nombre que la clase a la que pertenecen, pero tienen el símbolo ˜ delante.

• No tienen tipo de retorno, y por lo tanto no retornan ningún valor. • No tienen parámetros. • No pueden ser heredados. • Deben ser públicos, no tendría ningún sentido declarar un destructor como privado, ya que

siempre se usan desde el exterior de la clase, ni tampoco como protegido, ya que no puede ser heredado.

• No pueden ser sobrecargados, lo cual es lógico, puesto que no tienen valor de retorno ni parámetros, no hay posibilidad de sobrecarga.

Page 9: Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemos adquirido

Programación Mª Dolores Molina

Cuando se define un destructor para una clase, éste es llamado automáticamente cuando seabandona el ámbito en el que fue definido. Esto es así salvo cuando el objeto fue creadodinámicamente con el operador new, ya que en ese caso, cuando es necesario eliminarlo, hay quehacerlo explícitamente usando el operador delete. En general, será necesario definir un destructorcuando nuestra clase tenga datos miembro de tipo puntero, aunque esto no es una regla estricta.

Si no definimos un constructor el compilador creará uno por defecto, sin parámetros, que no haráabsolutamente nada. Los datos miembros del los objetos declarados en el programa contendránbasura, lo mismo ocurrirá si no definimos un destructor.

Veamos un ejemplo en la que definimos un método constructor y destructor para una clase:

#include <iostream> using namespace std; class pareja { public: pareja(int a2, int b2); // Declaración del constructor ~pareja(); // Declaración del destructor void Lee(int &a2, int &b2); // Funciones miembro de la clase "pareja" void Guarda(int a2, int b2); private: // Al ser declarados como private solo se pueden acceder desde dentro de la clase int a, b; // Datos miembro de la clase "pareja" };

pareja::pareja(int a2, int b2) //Definición del constructor{ a = a2; b = b2;}

pareja::~pareja() //Definición del destructor{}

void pareja::Lee(int &a2, int &b2) //Declaración de la función Lee{ a2 = a; b2 = b;}

void pareja::Guarda(int a2, int b2) //Declaración de la función Guarda{ a = a2; b = b2;}

int main() { pareja par1(12, 32); //declaramos un objeto de la clase pareja e inicializamos sus valores int x, y;

par1.Lee(x, y); //la función lee asigna los valores 12 y 32 a las variables x e y

Page 10: Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemos adquirido

Programación Mª Dolores Molina

cout << "Valor de par1.a: " << x << endl; //mostrará un 12 cout << "Valor de par1.b: " << y << endl; //mostrará un 32 return 0;}

Hay un modo específico para inicializar los datos miembros de los objetos en los constructores, queconsiste en invocar los constructores de los objetos miembro antes de las llaves de la definición delconstructor. Sólo los constructores de las clases admiten inicializadores. Cada inicializador consisteen el nombre de la variable miembro a inicializar, seguida de la expresión que se usará parainicializarla entre paréntesis. Los inicializadores se añadirán a continuación del paréntesis cerradoque encierra a los parámetros del constructor, antes del cuerpo del constructor y separado delparéntesis por dos puntos ":".

Por ejemplo, en el caso anterior de la clase "pareja" teníamos este constructor:

pareja::pareja(int a2, int b2) { a = a2; b = b2;}

Podemos (y debemos) sustituir ese constructor por este otro:

pareja::pareja(int a2, int b2) : a(a2), b(b2) {}

Hola mundo en gtkmmAhora que ya hemos aprendido lo suficiente para ver un ejemplo real y vamos a definir el clásico programa “Hola Mundo” con el que todo aprendiz de programación se inicia.

Fichero cabecera holamundo.h

ifndef HOLAMUNDO_H //declaración de variables simbólicas para evitar duplicidades#define HOLAMUNDO_H

#include <gtkmm/button.h> // incluimos las librerías que incluyen solo los botones y#include <gtkmm/window.h> // las ventanas, en lugar de toda la librería gtkmmusing namespace Gtk; // indicamos que vamos a utilizar el espacio de nombres Gtk

class HolaMundo : public Window //definimos la clase secundaria HolaMundo que hereda { // atributos y métodos de la clase principal Gtk::Window

public: //Parte pública de la clase HolaMundo(); // Declaración del constructor de la clase virtual ~HolaMundo(); // Declaración del destructor de la clase

protected: //Parte privada de la clase void clic_boton(); // Manejador de la señal hacer clic en el botón

Page 11: Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemos adquirido

Programación Mª Dolores Molina

Button boton; //Widget botón};

#endif

Fichero fuente holamundo.cpp

#include "holamundo.h"#include <iostream>

using namespace std;using namespace Gtk;

HolaMundo::HolaMundo():boton("Hola Mundo") // definimos la función constructor del objeto{ // inicializamos el botón con la cadena Hola Mundo // Establecemos el borde de la ventana a 10px; set_border_width(10);

// Definimos el manejador de señal para el botón clicked que llamará a la función clic_boton boton.signal_clicked().connect(sigc::mem_fun(*this, &HolaMundo::clic_boton));

// Añadimos el botón a la ventana que es un widget contenedor add(boton);

// Por último mostramos el botón que hemos creado boton.show();}

HolaMundo::~HolaMundo() //definimos la función destructor del objeto que en este caso{ // no hace nada}

void HolaMundo::clic_boton() //definimos la función que se ejecutará cuando hagamos clic { cout << "Hola Mundo" << endl;}

Fichero fuente main.cpp para el programa holamundo

#include "holamundo.h" //incluimos el fichero de cabecera, al ser nuestro va entre comillas#include <gtkmm/application.h> //incluimos el fichero de cabecera application.h using namespace Gtk;

int main (int argc, char *argv[]) //función principal con sus argumentos{ auto app = Application::create(argc, argv, "org.gtkmm.example"); //inicializamos gtkmm;

HolaMundo VHolaMundo; //declaramos una instancia de la clase Ventana;

//mostramos la ventana que retornará un valor cuando finalice el programa

Page 12: Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemos adquirido

Programación Mª Dolores Molina

return app->run(VHolaMundo);}

EmpaquetadoEn la mayoría del los casos cuando creamos un programa que incluye una interfaz gráfica, este seejecuta partiendo de una ventana principal a partir de la cual se abren nuevas ventanas, menús,cuadros de diálogo emergentes.La mayoría de los conjuntos de herramientas de la IGU (Interfaz Gráfica de Usuario) obligan acolocar los widgets en una ventana, utilizando posicionamiento absoluto, esto lleva a muchosproblemas:

• Los widgets no se reordenan cuando la ventana se redimensiona. Algunos se escondencuando las ventanas se hacen más pequeñas, y aparece un montón de espacio sin utilizarcuando la ventana se agranda.

• Es imposible predecir la cantidad de espacio necesaria para texto después de que se hatraducido a otros idiomas, o se ha mostrado en otro tipo de letra.

Gtkmm utiliza el sistema de empaquetado para resolver estos problemas.En gtkmm los widgets seordenan jerárquicamente, usando contenedores. Un widget contenedor contiene a otros widgets. Lamayoría de los widgets de gtkmm son contenedores. Las ventanas, las pestañas de los cuadernos ylos botones son todos widgets contenedores. Gtkmm incluye dos tipos de widgets de contenedores:

• Contenedores de un sólo hijo: solo se puede colocar un elemento en su interior, de este tiposon las ventanas( Gtk::Window)

• Contenedores de múltiples hijos: podemos colocar varios elementos en su interior.

Como hemos indicado en una ventana sólo puede contener un widget, de forma que para hacernuestros programas deberemos añadir un contenedor de múltiples hijos en la ventana. Los widgetscontenedores más útiles son Gtk::Grid y Gtk::Box.

Veamos un ejemplo de una ventana que incluye un contenedor del tipo Gtk::Box con dos botones ensu interior.

Código del fichero empaquetado.h

#ifndef GTKMM_EJEMPLO_EMPAQUETADO_H

#define GTKMM_EJEMPLO_EMPAQUETADO_H

#include <gtkmm/box.h> // Librería para el widget caja

#include <gtkmm/button.h> // librería para el widget boton

#include <gtkmm/window.h> // libería para el widget ventana

class Empaquetado : public Gtk::Window{

public:

Empaquetado();

virtual ~Empaquetado();

Page 13: Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemos adquirido

Programación Mª Dolores Molina

protected:

// Manejador de señal para el clic del botón. Si nos fijamos esta función recibe como parámetro

// un tipo de dato Glib::ustring. Para nosotros en este manual va a ser lo mismo que el tipostd::string

// que ya hemos usado con anterioridad

void boton_clic(Glib::ustring nombre_boton);

// Widgets que contendrá la ventana

Gtk::Box caja;

Gtk::Button boton1, boton2;

};

#endif // GTKMM_EJEMPLO_EMPAQUETADO_H

Código del fichero empaquetado.cpp

#include "empaquetado.h" //Incluimos nuestro fichero.h#include <iostream> //añadimos la librería para entrada y salida estándar

// definición del constructor e inicialización de los atributos boton1 y boton2Empaquetado::Empaquetado(): boton1("Botón 1"), boton2("Botón 2") { // Establecemos el título de nuestra ventana con la función set_title set_title("Ejemplo de ventana con varios elementos");

// Establecemos el tamaño del borde de la ventana con la función set_border_width set_border_width(10);

// añadimos el widget caja a la ventana add(caja);

// Conectamos los dos botones a la misma función manejadora pero cambiando el valor que recibe// como parámetro boton1.signal_clicked().connect(sigc::bind<Glib::ustring>( sigc::mem_fun(*this, &Empaquetado::boton_clic), "botón 1")); boton2.signal_clicked().connect(sigc::bind<-1, Glib::ustring>( sigc::mem_fun(*this, &Empaquetado::boton_clic), "botón 2"));

// Añadimos los botones al widget caja que está incluido dentro de la ventana caja.pack_start(boton1); caja.pack_start(boton2); // mostramos los botones // siempre debemos recordar realizar este paso, ya que le dice a GTK que el botón ya está//preparado y se puede mostrar boton1.show(); boton2.show();

Page 14: Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemos adquirido

Programación Mª Dolores Molina

// Mostramos la caja, aunque realmente no se mostrará hasta que se muestre la ventana que los // contiene caja.show();}

Empaquetado::~Empaquetado() //definimos el destructor de la clase que no hace nada{}

// Implementamos nuestra función manejadora del evento clic del botón// en este caso muestra por la salida estándar, la pantalla, el nombre del botón que hemos pulsadovoid Empaquetado::boton_clic(Glib::ustring data){ std::cout << "Has pulsado el " << data << std::endl;}

Código del fichero empaquetado.cpp#include "empaquetado.h"#include <gtkmm/application.h>

int main (int argc, char *argv[]){ auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

Empaquetado Vempaquetado;

//Mostramos la ventana y terminamos la ejecuación cuando se cierre. return app->run(Vempaquetado);}

Botones, Etiquetas y Cuadros de TextoUna vez que hemos desarrollado los conceptos previos necesarios para trabajar con gtkmm, vamosa ir aprendiendo el funcionamiento de los widgets básicos, en este caso vamos a comenzar con losbotones, etiquetas y cuadros de texto.

Botones:

Gtkmm proporciona varios tipos de botones, en este apartado nosotros vamos a describir elfuncionamiento de widget Gtk::Button, también llamados botones estándar. Los botones estándar,normalmente incluyen una etiqueta o imagen en su interior que describe la función que se va aejecutar al presionarlos, ya hemos utilizado algunos en nuestros ejemplos anteriores.

Hay dos maneras de crear un botón. Puede especificar una etiqueta en el constructor de Gtk::Button,o establecerla más tarde con set_label(). Para definir un atajo para la navegación por teclado pongaun guión bajo antes de uno de los caracteres que aparecen en su etiqueta y especifique true para elparámetro opcional mnemonic. Por ejemplo:

Gtk::Button* pButton = new Gtk::Button("_Salir", true);

Page 15: Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemos adquirido

Programación Mª Dolores Molina

Gtk::Button también es un contenedor, por lo que puede poner otro widget, como un Gtk::Image dentro de él. Los botones llevan asociada una señal del tipo clicked (pulsado) que será emitida al pulsar y liberar el botón.

Etiquetas:

Las etiquetas son la forma principal de incluir textos no editables en una ventana, por ejemplo paracolocar un título junto a un cuadro de texto que indique al usuario que tipo de información se le estásolicitando. Se puede especificar el texto de la etiqueta en el constructor cuando la declaramos oestablecerla mas tarde con los métodos set_text() or set_markup().El ancho de la etiqueta se ajusta automáticamente. Se pueden introducir etiquetas con varias líneasponiendo saltos de línea («\n») en la cadena de la etiqueta.

El texto de la etiqueta se puede justificar usando el método set_justify(). El widget también es capazde justificar el texto que contiene en su interior, lo que se puede activar con set_line_wrap().

Gtk::Label permite algunas propiedades para dar formato al texto como la negrita, color del textoo tamaño del texto. Para aplicar formato debemos pasar al método set_markup() una cadenaformateada siguiendo la sintaxis Pango Markup, <b>texto en negrita</b> y <u>textosubrayado</u> .

Cuadros de texto o Entry:

Los widgets de «entry» le permiten al usuario introducir texto. Puede cambiar el contenido con elmétodo set_text(), y leer el contenido actual con el método get_text(). Podemos definir cuadros detexto no editable pasándole false al método set_editable(). Para introducir contraseñas, frases depaso, y otra información que no quiera que aparezca en la pantalla, llamar a set_visibility() confalse hará que el texto permanezca oculto.

Los cuadros de texto llevan asociadas entre otras los siguientes tipos de señales:• activate: Se emite cuando el usuario pulsa la tecla Enter teniendo activo el cuadro de texto.• Changed: Se emite cuando cambia el cuadro de texto.• focus_out_event: Se emite cuando el cuadro de texto pierde el foco, es decir, cuando el

usuario pulsa sobre otro elemento de la pantalla o se cambia a otro cuadro de textoutilizando los atajos de teclado. Esta señal puede ser utilizada para comprobar o filtrar eltexto introducido por el usuario en el cuadro de texto.

Veamos un ejemplo del hola mundo un poco más avanzado. En este ejemplo el usuario introduce sunombre por pantalla en una caja de texto y recibe un saludo por parte del programa en una etiquetacuando se pulsa el botón aceptar, se limpia el cuadro de texto y la etiqueta que muestra el saludocuando pulsamos reiniciar y salimos del programa al pulsar el botón salir. Si al pulsar aceptar elcuadro de texto con el nombre está vacío en lugar del saludo se muestra un mensaje indicando alusuario que introduzca su nombre.

Código fichero saludo.h

#ifndef HOLAMUNDOA_H //declaración de variables simbólicas para evitar duplicidades#define HOLAMUNDOA_H

#include <gtkmm.h> // incluimos las librerías gtkmmusing namespace Gtk; // indicamos que vamos a utilizar el espacio de nombres Gtk

Page 16: Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemos adquirido

Programación Mª Dolores Molina

class Saludo : public Window //definimos la clase secundaria Saludo que hereda { // atributos y métodos de la clase principal Gtk::Window

public: //Parte pública de la clase Saludo(); // constructor de la clase virtual ~Saludo(); // destructor de la clase

protected: void clic_boton_aceptar(); //Manejador señal clic boton aceptar void clic_boton_salir(); // Manejador señal clic boton salir

void clic_boton_reiniciar(); //Manejador señal clic boton reiniciar

Box Vcaja,Hcaja1,Hcaja2; //Widget tipo caja que va a contener los botones y las etiquetas

Button boton_aceptar, boton_salir, boton_reiniciar; //Widgets botones Label etiqueta_nombre, etiqueta_saludo; // Widgets etiquetas Entry entrada_nombre; // widget caja de texto};#endif

Código del fichero saludo.cpp#include "saludo.h"#include <iostream>

using namespace std;using namespace Gtk;

/* definimos la función constructor del objeto inicializamos los objetos miembros a sus valores */Saludo::Saludo():Vcaja(ORIENTATION_VERTICAL), Hcaja1(ORIENTATION_HORIZONTAL), Hcaja2(ORIENTATION_HORIZONTAL),

boton_aceptar("Aceptar"),boton_salir("Salir"),boton_reiniciar("Reiniciar"),etiqueta_nombre("Nombre:"),

etiqueta_saludo(""){ // Establecemos el título de la ventana y el tamaño del borde set_title("Saludo"); set_border_width(10);

// Conectamos las señales con sus funciones manejadores boton_aceptar.signal_clicked().connect(sigc::mem_fun(*this,&Saludo::clic_boton_aceptar)); boton_salir.signal_clicked().connect(sigc::mem_fun(*this,&Saludo::clic_boton_salir)); boton_reiniciar.signal_clicked().connect(sigc::mem_fun(*this,&Saludo::clic_boton_reiniciar));

// Añadimos la etiqueta y la caja de texto a la caja horizontal 1 Hcaja1.pack_start(etiqueta_nombre); Hcaja1.pack_start(entrada_nombre);

Page 17: Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemos adquirido

Programación Mª Dolores Molina

// Añadimos los 3 botones a la caja horizontal 2 Hcaja2.pack_start(boton_aceptar); Hcaja2.pack_start(boton_reiniciar); Hcaja2.pack_start(boton_salir); // añadimos las dos cajas horizontales y la etiqueta con el saludo a una caja vertical que //incluiremos en la ventana y añadimos esta última a la ventana Vcaja.pack_start(Hcaja1); Vcaja.pack_start(Hcaja2); Vcaja.pack_start(etiqueta_saludo); add(Vcaja); // Por último mostramos todos los elementos contenidos en la ventana show_all_children();}

Saludo::~Saludo() //definimos la función destructor del objeto que en este caso no hace nada{}

void Saludo::clic_boton_aceptar() //definimos la función clic en el botón_aceptar{ if(entrada_nombre.get_text()!="") //si la caja de texto no está vacía { etiqueta_saludo.set_text("Hola "+entrada_nombre.get_text()); entrada_nombre.set_text(""); } else etiqueta_saludo.set_text("Introduce tu nombre para recibir un saludo");}

void Saludo::clic_boton_salir() //definimos la función clic en el botón salir{ hide();}void Saludo::clic_boton_reiniciar() //definimos la función clic en el botón reiniciar{ etiqueta_saludo.set_text(""); entrada_nombre.set_text("");}

Código de la función main.cpp

#include "saludo.h" //incluimos el fichero de cabecera que hemos creado, atentos a las comillas#include <gtkmm/application.h> //incluimos el fichero de cabecera application.h en lugar de todaslas cabeceras gtkmmusing namespace Gtk;

int main (int argc, char *argv[]) //función principal con sus argumentos{ auto app = Application::create(argc, argv, "org.gtkmm.example"); //inicializamos gtkmm;

Page 18: Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemos adquirido

Programación Mª Dolores Molina

Saludo VSaludo; //declaramos una instancia de la clase Ventana;

//mostramos la ventana que retornará un valor cuando finalice el programa return app->run(VSaludo);}

Definición del GUI desde GladeGlade es una herramienta para el desarrollo de interfaces gráficas de usuario utilizando GTK, esindependiente del lenguaje de programación y genera un archivo en formato XML queposteriormente podremos cargar en tiempo de ejecución y obtener un puntero que nos permitareferenciar a todos los widgets: ventanas, botones, marcos … que hayamos incluido en él.

Imagen de la pantalla de trabajo de Glade

En capítulos posteriores describiremos con más detalle el funcionamiento del programa Glade, peroen este momento nos vamos a centrar en cómo hacer que el fichero XML que Glade genera se cargeen tiempo de ejecución en nuestro programa y cómo referenciar todos los widgets que hayamosincluido en él.

Para cargar el fichero XML en nuestra aplicación vamos a utilizar la API Gtk::Builder. Esta APIdebe ser utilizada mediante Glib::RefPtr. Como todos los tipos de clases necesitamos utilizar elmétodo create() para crear una instancia de la misma. Por ejemplo:

auto punteroglade = Gtk::Builder::create_from_file("basic.glade");

Si analizamos con detalle esta instrucción vemos de nuevo el tipo auto asociado a al variablepunteroglade que hace referencia al fichero basic.glade que contiene las definiciones XML quehemos generado desde el programa de diseño gráfico. Esta instrucción creará una instancia de lasventanas definidas en el archivo .glade, aunque no se mostrarán inmediatamente a menos que lohayamos especificado a través de la ventana Propiedades en Glade.

Para crear una instancia de solo una ventana, o solo uno de los widgets secundarios, se puedeespecificar el nombre de un widget como segundo parámetro. Por ejemplo:

auto punteroglade = Gtk::Builder::create_from_file("basic.glade", "Hcaja1");

Para acceder a un widget utilizaremos el método get_widget(), pasando como parámetro el nombredel widget. Este nombre debe haber sido especificado en la ventana de Propiedades de Glade. Si no

Page 19: Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemos adquirido

Programación Mª Dolores Molina

se puede encontrar el widget o el tipo del widget no es correcto la función get_widget devolverá unpuntero nulo.

Box* punteroHcaja1=nullptr; //declaración de un puntero nulo al tipo widget Boxpunteroglade->get_widget(“Hcaja1”,punteroHcaja1);

Gtk::Builder verifica si el puntero es nulo y si el widget es del tipo esperado, y le advertirá en lalínea de comandos de estos casos. Recuerde que no está instanciando un widget con get_widget(),sólo está obteniendo un puntero a uno que ya existe. Siempre recibirá un puntero a la mismainstancia cuando llame a get_widget() en el mismo Gtk::Builder con el mismo nombre de widget.

Si queremos comprobar que el acceso al fichero se ha realizado correctamente y controlar losposibles errores desde el programa, en caso contrario, se utiliza la siguiente secuencia deinstrucciones:

1. Definimos y creamos una instancia de un puntero a widget con la siguiente instrucción:

auto punteroawidget = Gtk::Builder::create()

2. Añadimos un bloque try para enlazar el puntero que hemos creado en el paso anterior alfichero glade. Dentro de un bloque try se pretende evaluar una o más expresiones. Si dentrode dicho bloque se produce un algo que no se espera se lanza por medio de throw unaexcepción, la misma que deberá ser capturada por un catch específico. Para ejecutar estepaso se utiliza la siguiente secuencia de instrucciones:

try

{ punteroawidget->add_from_file("rutadelficheroglade") }

3. Puesto que desde un bloque try pueden ser lanzados diferentes tipos de errores de excepciónes que puede haber más de un catch para capturar a cada uno de los mismos. Veamos un ejemplo:

catch(const Glib::FileError& ex) //Analizamos los errores en caso que no haya encontrado el fichero{

cerr << "FileError: " << ex.what() << endl; //errores asociados al fichero

return 1;

}

catch(const Glib::MarkupError& ex){

cerr << "MarkupError: " << ex.what() << endl; //errores asociados a la sintaxis del fichero

return 1;

}

catch(const BuilderError& ex){

cerr << "BuilderError: " << ex.what() << endl; //errores asociados a la API Builder

return 1;

}

4. Si desde un try se lanza una excepción y no existe el mecanismo catch para tratar dicha excepción el programa se interumpirá abruptamente después de haber pasado por todos los catch que se hayan definido y de no haber encontrado el adecuado.

Page 20: Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemos adquirido

Programación Mª Dolores Molina

Para entender todo este proceso vamos a codificar el ejemplo saludo que hemos incluido en elapartado anterior pero realizando el diseño de pantalla con el editor Glade.

#include <gtkmm.h>#include <iostream>using namespace Gtk;using namespace std;

//Definimos los punteros a los widgets de la pantallaWindow* pSaludo = nullptr;Button* pboton_aceptar = nullptr;Button* pboton_salir = nullptr;Button* pboton_reiniciar = nullptr;Label* petiqueta_saludo = nullptr;Entry* pentrada_nombre = nullptr;

staticvoid clic_boton_aceptar() //definimos la función que se ejecutará cuando hagamos clic en elbotón_aceptar{ if(pentrada_nombre->get_text()!="") //si el cuadro de texto no está en blanco saluda { petiqueta_saludo->set_text("Hola "+pentrada_nombre->get_text()); pentrada_nombre->set_text(""); } else petiqueta_saludo->set_text("Introduce tu nombre para recibir un saludo");}

staticvoid clic_boton_salir() //definimos la función que se ejecutará cuando hagamos clic en el botónsalir{ if(pSaludo) pSaludo->hide(); // cuando se pulse el botón cerramos la aplicación}

staticvoid clic_boton_reiniciar() //definimos la función que se ejecutará cuando hagamos clic en elbotón reiniciar{ petiqueta_saludo->set_text(""); pentrada_nombre->set_text("");}

// Definición de la función principal int main (int argc, char **argv){ auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example"); //inicializamos gtkmm

//Definimos y creamos una instancia de un puntero a widget auto punteroawidget = Gtk::Builder::create();

Page 21: Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemos adquirido

Programación Mª Dolores Molina

try { //Enlazamos el puntero con el fichero saludo.glade que contiene las definiciones XML del GUI punteroawidget->add_from_file("glade/saludo.glade"); } catch(const Glib::FileError& ex) //Analizamos los errores si no encuentra el fichero { cerr << "FileError: " << ex.what() << endl; //errores asociados al fichero return 1; } catch(const Glib::MarkupError& ex) { cerr << "MarkupError: " << ex.what() << endl; //errores asociados a la sintaxis del fichero return 1; } catch(const BuilderError& ex) { cerr << "BuilderError: " << ex.what() << endl; //errores asociados a la API Builder return 1; }

//apuntamos nuestro puntero a la ventana del programa punteroawidget->get_widget("VSaludo", pSaludo); if(pSaludo) //Si la función get_widget ha devuelto un puntero porque ha encontrado la ventana { //apuntamos los punteros hacia los elementos de la pantalla y enlazamos con sus manejadores punteroawidget->get_widget("boton_aceptar", pboton_aceptar); if(pboton_aceptar) { pboton_aceptar->signal_clicked().connect( sigc::ptr_fun(clic_boton_aceptar) ); } punteroawidget->get_widget("boton_reiniciar", pboton_reiniciar); if(pboton_reiniciar) { pboton_reiniciar->signal_clicked().connect( sigc::ptr_fun(clic_boton_reiniciar) ); } punteroawidget->get_widget("boton_salir", pboton_salir); if(pboton_salir) { pboton_salir->signal_clicked().connect( sigc::ptr_fun(clic_boton_salir) ); } //Apuntamos los punteros de la entrada y la etiqueta hacia los elementos definidos en la pantalla punteroawidget->get_widget("entrada_nombre", pentrada_nombre); punteroawidget->get_widget("etiqueta_saludo", petiqueta_saludo);

app->run(*pSaludo); // Lanzamos la aplicación } delete pSaludo; return 0;

Page 22: Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemos adquirido

Programación Mª Dolores Molina

}

Creación de un proyecto con AnjutaAnjuta es un IDE (Integrated Development Environment) o entorno de desarrollo integrado quepermite a un programador tener en un solo programa todas las herramientas necesarias para crear unprograma: compilador, depurador, entorno para diseñar la interfaz gráfica de usuario, … . Anjuta essoftware libre y permite trabajar con múltiples lenguajes de programación como C, C++, Java,Phyton o Vala, además permite utilizar Glade para el desarrollo de la interfaz gráfica de usuario.

Anjuta está disponible en la mayoría de distribuciones Linux con lo que solo tendremos que ir algestor de paquetes correspondiente con nuestra versión e instalar el paquete “anjuta”. En estemanual nos vamos a centrar en la creación de un proyecto utilizando C++ , Glade y Gtkmm.

Para crear un proyecto nuevo deberemos realizar la siguiente secuencia de instrucciones:1. Abrimos Anjuta y seleccionamos la opción Crear un Nuevo Proyecto.2. Seleccionamos la pestaña C++ y después la opción Gtkmm simple.3. Rellenamos los datos básicos para la creación del proyecto: nombre del proyecto, autor,dirección de correo y versión y pulsamos siguiente.4. Seleccionamos la ubicación para los ficheros del proyecto, el tipo de licencia para el proyecto.Si marcamos la opción de añadir internacionalización Anjuta añadirá el soporte para que nuestroproyecto pueda ser traducido a otros idiomas. La opción añadir soporte para bibliotecascompartidas nos permitirá compartir con otros programadores las librerías que diseñemos ennuestro proyecto. La opción usar GtkBuilder para la interfaz de usuario nos va a permitir cargaren tiempo de ejecución el fichero XML que hayamos diseñado con Glade. Una vez hayamosseleccionado estas opciones pulsaremos siguiente.5. La siguiente pantalla nos muestra todos los datos de nuestro proyecto pulsaremos aceptar paraconfirmarlos.6. Una vez completado nos aparecerá la ventana de trabajo de Anjuta en la que podremos editar losficheros fuente del proyecto para construir nuestro programa. Podremos ver el fichero principalmain.cc si desplegamos la pestaña de icono que muestra el nombre del proyecto y podremosmodificar el GUI accediendo al fichero con el nombre del proyecto y extensión .ui. Debemoscomentar que algunas versiones de Anjuta presentan un bug al intentar abrir el fichero .ui haciendodoble clic sobre él pero podemos solventar este problema abriendo el fichero con el botón derechodel ratón y seleccionando la opción Abrir con Glade. En la siguiente imagen se muestra desglosadoel árbol de opciones para acceder a los ficheros que antes hemos mencionado.

Page 23: Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemos adquirido

Programación Mª Dolores Molina

Una vez hemos creado el proyecto Anjuta creará un fichero con extensión .ui dentro de la carpetasrc que contendrá las definiciones XML para la interfaz gráfica. Podremos llamar a GLADE desdeel mismo entorno de Anjuta haciendo clic con el botón derecho del ratón y seleccionando la opciónAbrir con / Glade ya que existe un bug en algunas versiones de Anjuta que impiden abrir el ficherocon extensión .ui haciendo doble clic sobre él. Una vez hayamos terminado de definir nuestrainterfaz gráfica seguiremos el mismo procedimiento que hemos explicado en el apartado anterior“Definición del GUI desde Glade” para hacer referencia a los widgets que hayamos definido en elfichero XML.Para entender mejor todo este proceso vamos a incluir los códigos fuente de una aplicación creadacon Anjuta y GLADE que permite convertir de euros a pesetas.

Código del fichero main.cc

#include <gtkmm.h> //librerías necesarias para la creación del proyecto#include <iostream>#include "config.h"#include <string>

using namespace Gtk;using namespace std;using namespace Glib;

#ifdef ENABLE_NLS# include <libintl.h>#endif

//Constante simbólica que define Anjuta para hacer referencia al nombre del fichero/* For testing propose use the local (not installed) ui file *//* #define UI_FILE PACKAGE_DATA_DIR"/ui/conversoreu.ui" */#define UI_FILE "src/conversoreu.ui"

/* definimos los punteros para hacer referencias a los widgets creados *//* en el fichero glade */Window* pVconversor = nullptr;Button* pB_aceptar = nullptr;Button* pB_salir = nullptr;Button* pB_reiniciar = nullptr;Entry* pCT_euros = nullptr;Entry* pCT_ptas=nullptr;

static void clic_B_aceptar() //definimos la función que se ejecutará al hacer clic en botón_aceptar{ ustring auxcadena; //declaramos variables auxiliares para convertir cadenas a números float auxnumero;

auxcadena=pCT_euros->get_text(); if(auxcadena!="")

{ auxnumero=atof(auxcadena.c_str()); //función atof convierte una cadena a decimal if(auxnumero)

Page 24: Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemos adquirido

Programación Mª Dolores Molina

{ auxnumero=auxnumero*166,386; auxcadena=to_string(auxnumero); pCT_ptas->set_text(auxcadena);

} }

else{

auxcadena=pCT_ptas->get_text(); if(auxcadena!="") {

auxnumero=atof(auxcadena.c_str());if(auxnumero){

auxnumero=auxnumero/166,386;auxcadena=to_string(auxnumero);pCT_euros->set_text(auxcadena);

}}

}}

staticvoid clic_B_salir() //definimos la función que se ejecutará cuando hagamos clic en el botón salir{

if(pVconversor) pVconversor->hide();}

staticvoid clic_B_reiniciar() //definimos la función que se ejecutará al hacer clic en el botón reiniciar{ pCT_euros->set_text(""); pCT_ptas->set_text("");}

intmain (int argc, char *argv[]) //código de la función principal{

Gtk::Main kit(argc, argv);

//cargamos el fichero Glade e instanciamos sus widgetsGlib::RefPtr<Gtk::Builder> builder; //declaración del punterotry{

builder = Gtk::Builder::create_from_file(UI_FILE); //asignación del puntero al fichero}catch (const Glib::FileError & ex) //control de errores al asignar el puntero{

std::cerr << ex.what() << std::endl;return 1;

Page 25: Programación Gráfica en C++€¦ · Programación Gráfica en C++ Introducción Este manual ha sido diseñado para dar el paso siguiente en la programación, una vez hemos adquirido

Programación Mª Dolores Molina

}//apuntamos el puntero a la ventanabuilder->get_widget("Vconversor", pVconversor);if (pVconversor){//apuntamos los punteros hacia los elementos de la pantalla y enlazamos con sus manejadores

builder->get_widget("B_aceptar", pB_aceptar); if(pB_aceptar) { pB_aceptar->signal_clicked().connect( sigc::ptr_fun(clic_B_aceptar) ); } builder->get_widget("B_reiniciar", pB_reiniciar); if(pB_reiniciar) { pB_reiniciar->signal_clicked().connect( sigc::ptr_fun(clic_B_reiniciar) ); } builder->get_widget("B_salir", pB_salir); if(pB_salir) { pB_salir->signal_clicked().connect( sigc::ptr_fun(clic_B_salir) ); }

//Apuntamos los punteros de la entrada y la etiqueta hacia los elementos definidos en la pantalla builder->get_widget("CT_euros", pCT_euros); builder->get_widget("CT_ptas", pCT_ptas);

kit.run(*pVconversor); //ejecutamos la aplicación}delete pVconversor;return 0;

}

Bibliografía

Web “Programar con gtkmm3”: https://developer.gnome.org/gtkmm-tutorial/stable/index.html.es

Fundamentos de programación con el lenguaje de programación C++. Vicente Benjumea y ManuelRoldán. Dpto Lenguajes y Ciencias de la Computación. Universidad de Málaga.

Wiki “Programación de software de Sistemas” de Ciencias de la Computación de la Universidad deChile: https://wiki.dcc.uchile.cl/cc3301/punteros.

Curso de C++: “C++ con Clase”: http://c.conclase.net/curso/?cap=000#inicio