Sei sulla pagina 1di 96

TUTORIAL DE ANJUTA

Qu es?
C++ con AGU, es el "manual" de un aficionado a la informtica [no busques soluciones profesionales], para dar los primeros pasos en la programacin visual en el lenguaje C++, utilizando las aplicaciones Anjuta y Glade en el entorno Gnome (Gnome es un entorno de escritorio habitual en los sistemas Linux). Al saltar al software libre, no tuve problemas con mis aplicaciones habituales, existen aplicaciones similares para todos ellas, excepto para la programacin visual en C++. Consulte en foros y todas las respuestas me animaban al uso de Anjuta y Glade, pero no fue fcil al principio, pues obtena errores continuos, motivados por mi desconocimiento del sistema operativo y de la falta de documentacin (en castellano) sobre el tema. Con mi bad english, un poco de intuicin y mucha, mucha paciencia consegu realizar programas con Anjuta y Glade en C++. El segundo paso era elaborar un manual que ayudara a todo aquel que pasara por mis mismas dificultades y autocontestar aquellas dudas dejadas en los foros. Como mis errores estaban motivados por el desconocimiento de aspectos simples, a veces obviados por los profesionales, decid escribir extensamente sobre los distintos puntos. La nueva versin de Anjuta, hace que ste manual no tenga mucho sentido, pues muchas de las dificultades encontradas con la versin anterior, han sido solucionadas

PROGRAMAR?
Si eres un aficionado a la Informtica, como yo, quizs cuestiones la utilidad de programar. Difcilmente nuestras aplicaciones alcancen la grandeza de millares de lneas de cdigo de otras aplicaciones, y probablemente las tareas realizadas por nuestros programas, quizs ya se encuentren en los repositorios de Ubuntu, concepto que desarrollamos ms adelante. Por tanto, para qu programar? Para m, es un hobby e independientemente de la utilidad, que alguna vez la ha tenido, es una actividad excelente para ejercitar el cerebro,... mi forma personal de luchar contra el Alzheimer y otros males de la edad [confo en tener xito], otros hacen puzzles, sudoku,... yo programo. Aprender a programar es una excelente actividad mental, aunque no seamos profesionales.

U de UBuntu
Entre las motivaciones del manual, est el ser eminentemente prctico; razn por la que he seleccionado una distribucin Linux especfica, en mi caso Ubuntu, existen muchas otras Fedora, Suse, Debian, Mandrake, Red Hat, Guadalinex,... y el uso de una u otra no ha de ser relevante.

Adems, Anjuta nos va a generar aplicaciones para el escritorio Gnome, con lo que incluso podremos transportar nuestro cdigo a otros sistemas basados en Unix.

Instalacin de Ubuntu
Mi eleccin descansa en el hecho de que Ubuntu, en mi caso, tiene una instalacin sencilla y reconoce todos mis perifricos (impresora, scaner, sonido,...) a la primera. Simplemente nos descargamos la imagen de la distribucin, la "quemamos" (recuerda que es una imagen ISO, requiere utilizar la opcin de Grabar imagen, del programa de grabacin), y reiniciamos el ordenador con el CD, ajustando la BIOS, si fuera necesario, para que comience leyendo el cdrom. Tambin existe la opcin de comprar el CD de instalacin. Existen muchas pginas explicando los pasos del proceso, ejemplo

CONSEJO: Utiliza un cuaderno para anotar las cuestiones que no entiendas y preguntarlas en algn foro (no te faltarn respuestas). Y sobretodo anota el nombre de usuario y las clave.

Los Repositorios

El grfico de la derecha muestra una de las grandes ventajas de Ubuntu: con el CD de instalacin y una buena conexin a Internet, ya no necesitas ms CDs para almacenar programas. Existen bases de datos, con diferentes aplicaciones/paquetes situados en servidores externos: los repositorios. Utilizando unos programas, gestores de descarga, incorporados en la instalacin bsica, podemos instalar aquellas aplicaciones y paquetes que necesitemos en nuestro ordenador. Estos gestores de descarga, por defecto tiene asignadas las direcciones (localizacin) de los repositorios oficiales, los programas all almacenados estn supervisados por el equipo del propio Ubuntu y afortunadamente cuando existe una actualizacin de un paquete descargado por t, ellos mismos te avisan para proceder a su actualizacin. Existen dos grandes gestores de descarga grficos: gnome-app-install (para aplicaciones) y synaptic (para paquetes). utilizaremos ambos en la instalacin de Glade y Anjuta.

G de Glade
Glade es la aplicacin que nos va a permitir desarrollar de forma visual el interfaz de usuario, las "ventanicas" de nuestra aplicacin.

Instalacin de Glade
En la barra del sistema [Aplicaciones] se puede acceder a gnome-app-install [/usr/bin/gnome-app-install] haciendo click en Aadir y quitar.

Este programa nos permite instalar muchas de las aplicaciones habituales de los usuarios. Instala una aplicacin y los paquetes necesarios para su utilizacin. Tan slo es necesario marcar aquellas que deseemos, en nuestro caso, en la categora programacin marcamos Glade (y de paso DevHelp - programa que nos permitir la lectura de la documentacin de ayuda de ciertos paquetes), y aplicamos los cambios.

A de Anjuta
Anjuta es un IDE para Gnome, un entorno de desarrollo integrado. Esto es, una aplicacin que integra o coordina las aplicaciones habituales que requiere un programador para el desarrollo de aplicaciones para Gnome (uno de los escritorio habituales en las distribuciones Linux).

Instalacin de Anjuta: uso de Synaptic


Anjuta no aparece como aplicacin disponible en gnome-app-install, por lo que recurriremos a synaptic. Para acceder a esta aplicacin buscamos en [Sistema->Administracin->Gestor de Paquetes Synaptic].

Una de las ideas del software libre, que no necesariamente gratuito, es la libertad de reutilizacin del cdigo. Lo habitual, es aprovechar los recursos de otros por lo que una aplicacin crea una serie de recursos y reutiliza otros. Estos recursos, se distribuyen en forma de paquetes. as que normalmente un programa usa o requiere uno o ms paquetes. Para instalar un paquete, el primer paso, sobretodo se lleva mucho tiempo sin utilizarse es dar a la opcin del men RECARGAR, que actualiza el listado de paquetes del repositorio. Para buscar rpidamente un archivo conocido, en nuestro caso Anjuta, haz click con el ratn en la ventana que contiene los archivos para que adquieran el foco y teclea el nombre del paquete, anjuta. Una vez localizado los marcamos para instalar (doble click), en el caso de anjuta, synaptic te informar de la existencia de paquetes asociados, obviamente confirmamos. Al aplicar los cambios ya tendremos instalado el programa en nuestro ordenador (sencillo, no?).

NOTA Recuerda que Anjuta "coordina" varias aplicaciones, los desarrolladores no saben si nosotros vamos a programar en C, en C++, en Java, Phyton, usando una librera grfica u otra, as que existen una serie de paquetes que debemos instalar igualmente: autogen automake

intltool libtool libglib libglibmm-2.4.-dev g++ libgtkmm2.4-dev As como todos los asociados con estos.

C++ Lenguaje de programacin de alto nivel


CDIGO MQUINA
Un ordenador es una mquina electrnica digital que distingue entre dos tipos de valores de tensin (por ejemplo, 5 voltios y 0 voltios). Uno de estos valores se interpreta como 0 y el otro como 1 [lo cual permite utilizar el lgebra booleana y el sistema binario en su programacin].

Al microprocesador le llega una seal elctrica que se interpreta como una sucesin de 0 y 1. Los pulsos vienen establecidos por la velocidad del mismo. Al microprocesador puede llegarle una seal electrnica tal que : 1010001111000001110001001. Esta seal la divide en "grupos", uno de ellos es la instruccin y las otras operandos. En funcin de la instruccin, el microprocesador realiza una determinada operacin con los operandos. Por ejemplo, 00010010 podra significar que sumara los operandos, y 00010011 que envie un operando al lugar indicado por el otro (por ejemplo, la impresora). Esto se denomina cdigo mquina y depende del procesador, esto es, 0010010 puede significar una instruccin en Intel y otra distinta en un Mac.

Toda, absolutamente toda, la informacin son combinaciones de 1y 0: Fotos, videos, msica, textos,... todo son 0 y 1, correctamente gestionados (cuando no falla) por el microprocesador (el corazn de nuestra mquina).

ENSAMBLADOR
Observa que la programacin en cdigo mquina es muy complicada para un simple mortal, porque es fcil confundirse y equivocarse, as que se diseo un pequeo traductor que facilitara el trabajo en cdigo mquina: el lenguaje ensamblador. Cada secuencia de dgitos es traducida por una pequea palabra que da una ligera explicacin de la misma: MOV AX, 0F79 en vez de 101110000111100100001111 El lenguaje ensamblador sigue siendo especfico de la estructura, pues no es sino una traduccin directa del cdigo mquina.

LENGUAJES DE ALTO NIVEL


C++, pertenece a otro nivel, utiliza un lenguaje ms comprensible para el humano [especialmente si habla ingls], que adems no es exclusivo del chip o microprocesador que utilicemos, ni del sistema operativo: se puede programar en C++ en Linux, como en otros. Al archivo escrito en C++ se le denomina cdigo fuente, y es un archivo de texto que no puede ejecutarse. Se requiere pasar de cdigo fuente a cdigo mquina, aspecto que desarrollar sucintamente en el siguiente apartado.

Compilacin, enlazado, make,...


Este apartado tiene cierta dificultad para aquellos que se inician en C++, la idea es que el cdigo fuente (archivo de texto) hay que convertirlo en un programa ejecutable (cdigo mquina), esto en aplicaciones extensas entraa cierta dificultad, pero afortunadamente Anjuta lo hace por nosotros. Con ello, pagamos un precio, aparecen muchos archivos en nuestro proyecto que no son cdigo fuente.

DE CDIGO FUENTE A ARCHIVO EJECUTABLE


El cdigo fuente escrito en C++, no deja de ser un archivo de texto incomprensible para el microprocesador. Es necesario pasar este texto escrito en el lenguaje C++ al lenguaje comprensible por el ordenador: el cdigo mquina. Este proceso se denomina compilacin. La compilacin requiere normalmente dos pasos: el compilado en s, donde el cdigo fuente se convierte a cdigo mquina, y el enlazado (linker), donde distintos cdigos objetos son enlazados para construir un archivo ejecutable. Lo normal es que una aplicacin la compongan una serie de archivos, de lo contrario el cdigo fuente sera un archivo enorme, con el que sera muy difcil trabajar. La compilacin de C++ incluye un paso previo, que es el preprocesado. En C++ se distinguen dos tipos de texto, las directivas del preprocesador y el cdigo en s. El primer paso es coger estas directivas (que se caracterizan por empezar por #) e interpretarlas, pasarlas a cdigo. Veamos un ejemplo: Crea una carpeta en el directorio /home/usuario, por ejemplo programauno. En l edita el siguiente cdigo (con el editor de textos) y grabalo como kk.cc: #define ketonta 1 int main () { int x=10; x= x*ketonta; return 0; } Abrimos un terminal [Aplicaciones->Accesorios->Terminal], en ste, nos vamos al directorio de trabajo, y ejecutamos la primera fase del prepocesado: ~$ cd /home/usuario/programauno ~/programauno$ g++ -E kk.cc > prepocesado.txt Observars que en la carpeta se ha creado un archivo de texto - de nombre prepocesado.txt -que bsicamente sustituye el sinnimo establecido en el programa con la directiva define. El siguiente paso, es la compilacin en s, pasar el cdigo fuente a cdigo objeto, lo cual se puede realizar as: ~/programauno$ g++ -c kk.ccse crear un kk.o que no podrs editar como archivo de texto, pues es un binario (debes instalar un editor como bless). Si alguna vez deseamos "pulir" nuestro programa en ensamblador, yo nunca lo he hecho, pero ms de una vez le sobre ello (con antiguos procesadores, microchips programables), podemos obtener la versin en ensamblador con: ~/programauno$ g++ -S kk.ccte aparecer un nuevo archivo kk.s con el cdigo en ensamblador, lo cual es ms inteligible que el cdigo objeto.

Un ltimo paso es el enlazado, que nos permite la creacin de un ejecutable. Antes de proceder vamos a borrar todos los archivos anteriores y reeditamos kk.cc con el siguiente texto, para que el programa haga algo (no te preocupes si ahora no lo entiendes). #include <iostream> #define ketonta 2 int main () { int x=10; x= x*ketonta; std::cout<<" el doble de 10 es: "<<x<< std::endl; std::cout<<" Que tont !!"<<std::endl; return 0; } Vamos a preprocesar y compilar en un primer paso (obtendremos kk.o) [si preprocesas nicamente vers porque razn utilizamos el programa anterior] y luego vamos a enlazar: ~/programauno$ g++ -c kk.cc ~/programauno$ g++ kk.o Te aparecer un nuevo archivo a.out, ste es un archivo ejecutable. Para ejecutarlo, desde la consola, lo llamamos, pero recuerda que si pongo solo a.out el sistema busca en los directorios de ejecutables del sistema y nos da un error [bash: a.out: orden no encontrada], es necesario buscar en el directorio actual, por lo que escribimos./a.out, y obtendremos la siguiente salida: ~/programauno$ ./a.out el doble de 10 es: 20 Que tont !! ~/programauno$ Ahora, renombro este archivo a mi gusto y se acab. Todo en uno En realidad, g++ nos permite realizar todo en un paso: g++ -o minombre kk.ccy obtendremos un programa ejecutable con el archivo minombre. Volviendo al enlazado existen dos posibilidades, que este sea esttico o dinmico. El enlazado esttico implica que las funciones de una librera externa se incorporan a nuestro ejecutable, no siendo necesario las mismas para su ejecucin (por si luego ejecutamos nuestra aplicacin en otro ordenador donde no tenemos acceso como root y no podemos instalar las libreras); mientras que en el dinmico, opcin por defecto, la aplicacin requiere la existencia en el ordenador de las libreras. Para realizar un enlazado esttico utilizamos: g++ -static -o minombre2 kk.cc Si observas la diferencia entre minombre, 8,8 Kb, y minombre2, 1,2Mb, vers porque normalmente los programadores utilizan el enlace dinmico. Para una aplicacin con varios archivos, la compilacin se realiza:

g++ -o nombreejecutable archivo1.cc archivo2.cc archivo3.cc archivon.cc

MAKE Y MAKEFILE
Antes hemos visto que se puede realizar todo el proceso en una sola fase, as es, pero no es lo habitual. La razn es muy sencilla, cuando un programa tiene varios archivos y modificamos uno, en la compilacin se vuelven a compilar todos otra vez, aunque no se hayan modificado. Esto en pequeos programas no tiene mayor problema, pero cuando ste alcanza cierta dimensin, la prdida de tiempo es considerable. Por ello, se ideo una compilacin separada donde al realizar una pequea modificacin solo se compilan los archivos afectados y no todos. Y este es el propsito del comando make. Cuando en un directorio, ejecutamos make, l busca rpidamente las reglas de compilacin establecidas en el archivo makefile. No es el propsito de este manual explicar (por ahora) como escribir los makefile, porque precisamente Anjuta se va a encargar de eso por nosotros.

Instalacin: Mi primer programa C++ con Anjuta

PRIMER PASO: ABRIR UN PROYECTO NUEVO


El primer paso es crear un proyecto nuevo. Anjuta nos proporciona la capacidad de crear proyectos Gnome, que luego podrn distribuirse. Esto provoca la existencia de muchos archivos (authors, configure.in,...) adems del cdigo fuente.

La primera vez que ejecutamos esta opcin da un error (nos falta instalar autogen versin 5). Abrimos Synaptic, buscamos e instalamos autogen.
Se abrir el asistente de aplicaciones de Anjuta, que nos permite crear el proyecto de forma sencilla. El primer paso del asistente es una presentacin del mismo, que nos advierte de la necesidad de responder con atencin a las preguntas que durante el mismo se nos realizan.

El siguiente paso del asistente, y bsicamente el primero, es determinar que tipo de proyecto vamos a realizar. Este asistente, contempla la posibilidad de usar Anjuta con diversos lenguajes de programacin. En este caso seleccionamos dentro de C++, la categora Generic C++, que nos permitir la realizacin de un programa clsico de consola.

El siguiente paso es determinar el nombre de la aplicacin, del proyecto, del autor, contacto y versin.

Este es el paso ms delicado, pues implica la eleccin de aspectos ms importantes. Por ahora solo fijarnos en: Destino: es la carpeta donde se crea el proyecto. Personalmente te recomiendo que tengas una carpeta [programas] en tu directorio personal, y en cada proyecto creas una nueva carpeta [primero].

Finalmente, solo se nos da un resumen del proyecto antes de proceder a la autogeneracin del proyecto.

NOTA En la autogeneracin del primer proyecto tendremos varios paquetes ausentes que debemos instalar con Synaptic (por supuesto) : automake (existen varias versiones para instalar, inicialmente instala la superior, si algn da tienes un proyecto de otra persona y tienes problemas puedes instalar la que precises).
intltool libtool

glib (esta librera que nos pide Anjuta, no existe como tal, hay que instalar libglib

SEGUNDO PASO: CONSTRUIR PROYECTO


En los archivos del proyecto, una vez generado adecuadamente, nos encontramos con muchos archivos, el cdigo fuente se encuentra en el directorio src, y es el archivo main.cc . Anjuta, a modo de ejemplo, edita el clsico programa "hello world" - suele ser el primer programa en la mayora de los libros de programacin. Ahora no vamos a editarlo, simplemente lo vamos a ejecutar, para lo cual es necesario construir el proyecto (construir=make): [Construir>Construir poyecto] o Mayscula+F11.

NOTA

La primera vez nos pedir la instalacin del compilador g++. Lo buscamos en Synaptic y lo instalamos

TERCER PASO: EJECUTAR EL PROGRAMA


Si el paso anterior no da problemas, que no debiera (salvo el sealado), hemos creado un programa que podemos ejecutar. En Anjuta -[construir->Ejecutar el programa] o F3-, primero nos aparece la siguiente ventana:

Aceptamos y en el terminal de Anjuta, nos aparece el siguiente resultado:

Si lo ejecutamos en un terminal de Gnome (abre un terminal, [aplicaciones->accesorios->terminal] del men del escritorio, e introduce toda la lnea que viene tras executing/home/xxxxxx/primero/ src/primerprograma, no se nos avisa de nada.

Estructura Bsica de un Programa

PROGRAMA EJEMPLO
El programa "hello world" de Anjuta, tpico y habitual en cualquier lenguaje de programacin, no rene las caractersticas bsicas de un programa. As que, nuestro primer programa ser algo ms complicado, pero ms acorde con la realidad.

Vamos a editar el archivo main.cc del programa anterior (proyecto c++ genrico), remplazando el contenido del archivo main.cc por el cdigo siguiente (copia y pega):
/* * main.cc * por maestrodenada 2008 * */ #include <iostream> int menor (int,int); int main() { int num1, num2; std::cout<<"Introduce dos nmeros (separados por un espacio): "; std::cin>>num1>>num2; num1=menor(num1,num2); if (num1==0) std::cout<<"los nmeros son iguales"; else std::cout<<num1<<" es el menor de los dos nmeros"; return 0; } int menor (int n1, int n2) { if (n1==n2) return 0; else if(n1>n2) return n2; else return n1; } El programa pide la introduccin de dos nmeros y luego nos devuelve el menor de ellos.

Recuerda que para ejecutarlo en Anjuta, primero debemos grabarlo, luego construir el proyecto [Maysculas+F11] y finalmente ejecutarlo [F3].
En los prximos apartados analizamos los elementos bsicos de un programa, con lo que al trmino de este captulo debieras comprender el funcionamiento de este programa.

COMENTARIOS
Difcilmente encontrars un programa donde no encuentres comentarios, hasta los malos. Los comentarios son texto que no forma parte del cdigo fuente (ni de las directivas del preprocesador). Su objetivo principal es explicar y documentar el programa. Es buena prctica ir comentando lo que se hace, porque de lo contrario reutilizar o depurar el cdigo, pasado un cierto tiempo puede convertirse en tarea imposible, y especialmente cuando el cdigo se escribe por varios autores. Existen dos formas de hacer comentarios en C++: Una heredada de C, los comentarios comienzan con '/*' y terminan con '*/'. Este tipo de comentario es til en: largas explicaciones: /****************************************************************** Calcular la media ponderada de la nota de los tres trimestres el primer trimestre debe tener una valoracin del 30 % el segundo, el ms importante, 40% el tercero el restante 30% ******************************************************************/ Modificacin de pequeos fragmentos intermedios de cdigo. Este aspecto es muy interesante para depurar programas (corregir errores de programacin -a veces los programas no hacen exactamente lo que queremos, errar es humano y en programacin somos todos muy humanos). Por ejemplo: std::cout <</*"X =" << x <<*/"Y= " << y; es igual a std::cout<<"Y= "<<y; Una segunda especfica de C++ donde los comentarios comienzan con '//' y terminan con el final de lnea. Este tipo de comentario es ms rpido de utilizar, pero como inconveniente tiene que no permite modificar pequeos fragmentos de cdigo: // Todo lo escrito aqu no es cdigo hasta el final de lnea.

x=x+y; //el final es comentario, el principio si es cdigo, habitual para comentar lneas. Un aspecto importante es que el comentario no puede ir entre comillas, pues se considera parte del literal, por ejemplo: std::cout<<"Hola /*amigo*/Pablo"; tendra como salida Hola /*amigo*/Pablo y no Hola Pablo, como quizs alguno podra esperar.

CDIGO = DATOS + INSTRUCCIONES

Declaracin de Variables
Un programa opera con datos. Informacin que se almacena en la memoria. La memoria se divide en bytes, unidad bsica de almacenamiento, de ah que al vender el ordenador te digan que tiene tantos Megas (megabytes 1M = 1 048 576 bytes) o Gigas (gigabytes) de RAM. Un byte es una sucesin de bits fija, normalmente ocho, y un bit es la unidad mnima de almacenamiento,un 0 o un 1. Para acceder a la memoria, en vez de indicar el nmero de byte al que deseamos acceder, utilizamos un identificador -un nombre- que nos permite acceder al contenido ubicado en la memoria de forma simple. El identificador nos devuelve el contenido de un lugar especfico de la memoria del ordenador, pero este contenido puede cambiar durante la ejecucin del programa, razn por la que se denominan variables. En la memoria, en el ordenador, todo es una sucesin de unos y ceros: fotos, msica, nmeros, texto,... todo son unos y ceros. Un programa puede manipular estos unos y ceros como nmeros en un programa de clculo, como letras en el procesador de textos, como imagenes,... las operaciones a realizar con ellos dependen del significado, del tipo de dato. Para gestionar estos 1 y 0, de maneras diferentes, precisamos decirle al ordenador que tipo de variable es. Esto se consigue con la declaracin de variables, donde antes de su uso toda variable ha de ser declarada, indicando el tipo de dato y el nombre de la variable o identificador, tal que as:

tipo_de_variable identificador_nombre_de_variable;

En otros lenguajes no es obligatorio declarar las variables, pero si en C++.


En nuestro programa encontramos cuatro variables declaradas: num1, num2, n1 y n2. #include <iostream> int menor (int,int); int main() {

int num1, num2;


std::cout<<"Introduce dos nmeros (separados por un espacio): "; std::cin>>num1>>num2; num1=menor(num1,num2); if (num1==0) std::cout<<"los nmeros son iguales"; else std::cout<<num1<<" es el menor de los dos nmeros"; return 0; } int menor (int n1, int n2) { if (n1==n2) return 0; else if(n1>n2) return n2; else return n1; } int es una variable predefinida, esto es propia de C++, que indica al ordenador que seleccione un nmero de bytes (4, normalmente) y los interprete como un nmero entero (pero no un entero cualquiera pues 4 bytes permiten un nmero entero de 0 a 4294967295). Existen ms tipos predefinidos (char ->carcter alfanumrico; bool -> booleano -verdad o falso; float o double -> nmeros reales; ... estos pueden modificarse con modificadores [signed|unsigned][short|long]).

IMPORTANTE. A la hora de establecer el identificador recuerda: C++ es "case sensitive". Esto es, que distingue entre maysculas y minsculas. No es igual num que Num, que nUm,... Usa el alfabeto ingls (nada de , tildes), nmeros y el guin bajo. Comienza siempre por una letra: ej. id3_contador Y, por supuesto, no utilices palabras claves (propias del C++) , a saber: asm, auto, bool, break, case, catch, char, class, const, const_cast, continue, default, delete, do, double, dynamic_cast, else, enum, explicit, export, extern, false, float, for, friend, goto, if, inline, int, long, mutable, namespace, new, operator, private, protected, public, register, reinterpret_cast, return, short, signed, sizeof, static, static_cast, struct, switch,

template, this, throw, true, try, typedef, typeid, typename, union, unsigned, using, virtual, void, volatile, wchar_t, while

Vamos a reforzar el concepto anterior con este nuevo programa, reedita nuestro archivo main.cc , construye el proyecto y ejectalo:
#include <iostream> int main() { int x; x=64; char y; y=x; std::cout << "X= " << x << " Y= " << y << std::endl; return 0; } La salida es X= 64 Y=@. La explicacin es muy sencilla, x es declarada como nmero entero y se le asigna el valor 64, 01000000. A y se le asigna el mismo valor (y=x), pero se declara como carcter alfanumrico. Si ahora, buscamos en una tabla ASCII, veremos que el 64 es la famosa arroba.

// ver resultados en la pantalla

CDIGO = DATOS + INSTRUCCIONES


Instrucciones
Un programa opera con datos. Se realizan una serie de operaciones con los datos. Una instruccin elemental termina con un punto y coma [;] . En C++, el final de lnea no indica final de instruccin. Los dos programas siguientes son iguales: #include <iostream> int menor (int,int); int main() { int num1, num2; std::cout<<"Introduce dos nmeros (separados por un espacio): "; std::cin>>num1>>num2;

num1=menor(num1,num2); if (num1==0) std::cout<<"los nmeros son iguales"; else std::cout<<num1<<" es el menor de los dos nmeros"; return 0; } int menor (int n1, int n2) { if (n1==n2) return 0; else if(n1>n2) return n2; else return n1; } #include <iostream> int menor (int,int); int main(){ int num1, num2; std:: cout<<"Introduce dos nmeros (separados por un espacio): "; std::cin>>num1>>num2; num1=menor(num1,num2); if (num1==0) std::cout<<"los nmeros son iguales"; else std::cout<<num1<<" es el menor de los dos nmeros"; return 0;} int menor (int n1, int n2){ if (n1==n2) return 0; else if(n1>n2) return n2; } else return n1;

No son pocas las veces, que nos aparece un error de compilacin, por un punto y coma que hemos omitido (al menos en mi caso).
Ademas de las declaraciones de variables, es habitual encontrarse este tipo de instrucciones o sentencias:

Asignaciones, inicializacin
Las asignaciones son una instruccin especial, que tienen a la izquierda el nombre de la variable, luego el operador de asignacin [=] y a la derecha una expresin. nombre_variable = expresin ; Esto significa que en el espacio de memoria asignado a nombre_variable, se introduzca el resultado de la expresin. Esto permite comprender expresiones tal como x=x+y; muy habituales en programacin y que no quieren decir que y sea igual a 2x, sino que se sume el contenido de la variable x con el contenido de la variable y, y el resultado se coloque en x. Por ejemplo, si x=20 e y=3, tras esta instruccin x = 23, y por supuesto, y= 3. Especialmente habitual es la asignacin x=x+1; que en C y C++ se puede escribir como x++;. Esto nos permite comprender el significado de C++: un paso adelante del lenguaje C.

A la primera asignacin de una variable, se le denomina inicializacin, no es obligatoria, pero debiera serlo. Puede realizarse la declaracin y la inicializacin en un slo paso, por ejemplo: int x= 10;// declaracin e inicializacin en un solo paso es igual que: int x; //declaracin de la variable x x=10; //inicializacin

Estructuras de control
Pocos sern los programas que realices donde la ejecucin siga un proceso secuencial nico. Lo habitual es que muchas operaciones se repitan y otras se ejecuten o no en funcin de las circunstancias. En nuestro ejemplo, si el usuario introduce dos nmeros iguales tendremos que informarle que ambos nmeros son iguales, mientras que si son diferentes debemos ver cual de ellos es el ms pequeo. Es una clsica instruccin de decisin: if (expresin) cdigo_si else cdigo_no si la expresin es cierta se realiza el cdigo_si, mientras que si es falsa se ejecuta cdigo_no. Donde cdigo puede representar como en el ejemplo una nica instruccin, o un bloque de cdigo (varias instrucciones), porque lo normal es que queramos hacer varias cosas en una situacin determinada. El bloque de cdigo se realiza con los parntesis {...}

cdigo => instruccin; => { instruccin; instruccin2; intruccinn;}


Existen estructuras de repeticin (para repetir un nmero de veces un cdigo determinado). Por ejemplo, el siguiente cdigo pide la introduccin de un nmero y luego calcula el factorial [n * (n-1)* (n-2)*...*1], ej factorial de 5 es igual a 5x4x3x2x1 . Por cierto, habra que advertir al usuario que con nmeros elevados el resultado no es el correcto, ya que el espacio utilizado para almacenar el nmero es reducido (4 bytes): #include <iostream> int main(){ int num1, total;//declaracin de las variables //pedimos un nmero al usuario std::cout<<"Introduce un nmero: "; std::cin>>num1; //inicializacin de total total=1; /* Inicialmente se asigna a la variable i el valor del nmero. / en cada iteracin i disminuye una unidad i-- => i=i-1; / y se repite mientras que i sea mayor que 1, es decir hasta 2, / multiplicar por 1 es tonteria. / Si num1 = 5, la primera vez total= 5*1 (hablando de tonteras) / la segunda vez, total=5*4 (20), la tercera total=20*3 (60), la cuarta

/ total=60*2 (120), la quinta i vale 1 y ya no se ejecuta la instruccin.*/ for (int i=num1;i>1;i--) total=total*i; //mostramos el resultado std::cout<<"el factorial de "<<num1<<" es "<<total; return 0; }

FUNCIONES
Con frecuencia, un algoritmo se repite, variando acaso el valor de los datos: ordenar una lista, calcular la media, ...Para evitar reescribir el mismo cdigo una y otra vez, se disean las funciones, un conjunto de instrucciones que cumplen una funcin determinada (valga la redundancia), son el alma de la programacin y para poder utilizarse deben: declararse y definirse.

Declaracin de Funciones
Al igual que con los datos en C y C++ antes de usarse una funcin sta debe declararse, lo cual se hace tal que as: tipo_dato_devuelto nombre_funcin ([argumentos]); Los argumentos son opcionales porque existen funciones que no tienen argumentos. Cuando una funcin tiene argumentos, en la declaracin es suficiente con indicar el tipo de datos que recibe; si son varios estos se separan por coma [,] A veces una funcin no precisa devolver dato alguno, lo que en otros lenguajes se denominan subrutinas, esto se especfica en C declarando la funcin del tipo void. En nuestro ejemplo tenemos: int menor (int, int); que le dice al compilador que va a utilizarse una funcin denominada menor, que recibe como argumentos dos nmeros enteros y como resultado devuelve otro entero. Todas las funciones deben declararse antes de utilizarse. Si eliminas [comentas] esa lnea de cdigo, al construir te aparece el mensaje: 'menor' no se declar en este mbito. #include <iostream> // int menor (int,int); int main()

{ int num1, num2; std::cout<<"Introduce dos nmeros (separados por un espacio): "; std::cin>>num1>>num2; num1=menor(num1,num2); if (num1==0) std::cout<<"los nmeros son iguales"; else std::cout<<num1<<" es el menor de los dos nmeros"; return 0; } int menor (int n1, int n2) { if (n1==n2) return 0; else if(n1>n2) return n2; else return n1; }

Definicin de funciones
La definicin consiste en el desarrollo de la funcin, el cdigo en s. Las definiciones estn en los archivos fuente (.cc,.cpp,.c). Tienen una estructura similar a la declaracin, de hecho, si una funcin es definida con anterioridad a main, es a la vez declarada. tipo_dato_devuelto nombre_funcin ([argumentos]){ codigo} A diferencia de la declaracin, la definicin requiere que los argumentos vengan declarados, es decir no slo indican el tipo, tambin el identificador o nombre de la variable utilizada en el cdigo. En la declaracin, tambin puede ponerse el identificador pero no es estrictamente necesario. En nuestro caso: int menor (int n1, int n2) { if (n1==n2) return 0; else if(n1>n2) return n2; else return n1; } return es la palabra clave utilizada para devolver el valor. En nuestra funcin devuelve 0 cuando ambos nmeros son iguales y devuelve el nmero menor en caso contrario. En nuestra funcin main devuelve 0 que es el valor utilizado para comunicar al sistema que el programa a terminado con xito.

Si las funciones se definen antes que main, declaracin y definicin se realizaran en un solo paso. El siguiente programa compila perfectamente.
#include <iostream> int menor (int n1, int n2) { if (n1==n2) return 0; else if(n1>n2) return n2; else return n1; } int main() { int num1, num2; std::cout<<"Introduce dos nmeros (separados por un espacio): "; std::cin>>num1>>num2; num1=menor(num1,num2); if (num1==0) std::cout<<"los nmeros son iguales"; else std::cout<<num1<<" es el menor de los dos nmeros"; return 0; }

main es la funcin donde se inicia la ejecucin del programa. Es una funcin esencial y nica. Es obligatorio que todo programa tenga una, y solo una, definicin (y declaracin) de main.
Como luego veremos, entre las cosas maravillosas de C++, est la sobrecarga de funciones, es decir, poder definir varias funciones con el mismo nombre, donde la diferencia que permita seleccionar la correcta al compilador descanse en los argumentos, as puedo definir int suma (int, int); y int suma (int, char); que el ordenador sabr seleccionar una u otra en funcin de los argumentos utilizados,... Por ejemplo, en nuestro programa existen una funcin, algo especial, que est sobrecargada, es la funcin std::cout<<, que nos ha permitido imprimir tanto nmeros (int) como texto (char*). main es la excepcin y no puede sobrecargarse. Y si std::cout<< es una funcin, y toda funcin debe declararse antes de usarse, donde?

DIRECTIVAS DEL PREPROCESADOR


Hemos dicho que todas las funciones y variables deben declararse antes de usarse, pero nosotros utilizamos std::cout<<, funcin que enva a la salida estndar (la consola) la expresin de la derecha. std::cin>>, funcin que asigna el dato introducido por el usuario a la variables de la derecha. las cuales, no hemos declarado (ni definido). Bueno, en realidad s la hemos declarado. La expresin: #include <librera> como todas las expresiones que comienzan con #, es una directiva del preprocesador, que como ya vimos en una primera fase de la compilacin se interpreta y se sustituye en el archivo que finalmente se compilar. En nuestro caso, la directiva include aade el archivo de cabecera (.h) de la librera que se van a utilizar. Los archivos de cabecera (.h) incluyen las declaraciones de las funciones, las cuales estn definidas en los archivos .cc (o .cpp, o .cxx,...) Estas directivas no son instrucciones, por lo que no terminan en punto y coma.

Puedes eliminar esta lnea y compilar el proyecto, te dar una buena cantidad de errores. Tambin puedes realizar por consola el preprocesado y ver el archivo resultante.
(en el directorio de main.cc)~$ g++ -E main.cc > preproc (en el directorio de main.cc)~$ more preproc(pulsar Intro para avanzar en el documento y q para terminar de verlo).

C++, algo ms que un paso adelante: Las clases


C++ es un lenguaje de programacin basado en C. Sabiendo que x++, significa en C (y C++) x=x+1, podemos entender la intencin de Stroustrup (el creador) de avanzar un paso ms, pero en mi modesta opinin, no es un paso es un abismo.

DEFINICIN DE NUEVOS TIPOS DE DATOS


Todos los lenguajes de programacin, vienen con unos tipos predefinidos (ms o menos igual en todos ellos). En la mayora el usuario, puede crear nuevos datos combinacin de los predefinidos. Un "tipo de dato", no es solo una combinacin de datos, sino que adems implica una serie de operaciones, de funciones que las caracteriza. Por ejemplo, la suma de 'a'(97) y 'b' (98) debiera dar 'ab', mientras que la suma como enteros (97+98=195).

Por ejemplo, podemos definir un tipo de variable punto, que almacena dos enteros 'x' e 'y' y definimos la funcin asigna para dar valores, suma para sumar dos puntos eimprimir para mostrar por consola. #include <iostream> // la palabra struct crea un nuevo tipo de dato, en este caso denominado punto. // que tiene dos variables enteras struct punto{ int x; int y; }; // prototipo de las funciones utilizadas, observa los valores de retorno punto asigna(int,int); // devuelve un punto punto suma (punto,punto);// devuelve un punto void imprimir(punto);// no devuelve ningun valor, solo realiza instrucciones. int main() { punto A;// declaracin de una variable punto definida por nosotros punto B; A=asigna(3,7);// llamada a la funcin asigna, que devuelve un punto B=asigna(10,3); imprimir(A);// llamada a la funcin imprimir imprimir(B); B=suma(A,B);// llamada a la funcin suma, que devuelve un punto imprimir(B); return 0; } punto asigna(int j, int i){ punto Z; Z.x=j; // para acceder a un elemento de la estructura se utiliza el operador '.' Z.y=i; return Z; } void imprimir (punto Z){ std::cout<< "X= "<< Z.x <<" Y = "<< Z.y<<std::endl; } punto suma (punto Z, punto M){ punto R; R.x=Z.x+M.x; R.y=Z.y+M.y; return R; }

Hasta aqu todos los lenguajes (que conozco) pueden hacer algo similar. Pero C++ tiene la posibilidad de definir nuevos tipos de datos: las clases. Las clases facilitan la creacin de grandes proyectos (especialmente entre varios autores). Las clases no solo establecen los datos de nuestro "tipo de dato", sino que adems incorporan las funciones que permiten su uso. Normalmente, los datos son inaccesibles (privados) y solo se manipulan a travs de sus funciones (mtodos). Esto quiere decir que si elaboro un tipo de dato coherente, una clase que funciona correctamente, podrn utilizarlos otros sin problemas. Las clases pueden heredarse, queeee?, esto es, que si tengo establecido un tipo de dato "alumno", y preciso crear un tipo de dato "delegado" (que no es sino un tipo de alumno) puedo desarrollar "delegado" a partir de "alumno", sin necesidad de reescribir mucho cdigo, tan solo aquel que implemente las nuevas caractersticas.

Afortunadamente para nosotros, utilizaremos abundantemente la herencia, y con escasas lneas de cdigo haremos aplicaciones grficas, aprovechando las clases de Gtkmm.

ADEMS
Adems C++ incorpora la sobrecarga de funciones (polimorfismo). En muchos lenguajes, esto no se permite, y si deseo realizar una funcin que sume a un punto un nmero, o que sume dos puntos, preciso implementar (dos)tres funciones distintas, con nombres diferentes: sumapuntoint (punto,int); sumaintpunto(int,punto); sumapuntopunto(punto,punto); Sin embargo, en C++, podemos definir dos o ms funciones con un mismo nombre, a condicin que los argumentos o el tipo devuelto, difieran y el ordenador pueda determinar que funcin suma es llamada durante el programa. Y, por si fuera poco, permite la sobrecarga de operadores, funciones especiales, que permiten al programador un uso de los datos similar al de los datos predefinidos. Veamos un ejemplo de este ltimo punto. El objeto no es aprender a disear las clases, sino a ver parte del potencial de C++. #include <iostream> class punto{ int x; int y; public: punto(int,int); punto operator + (punto); friend std::ostream& operator << (std::ostream&,punto); }; // funcion main

int main() { punto A(7,7); punto B(10,3); std::cout<<"A es igual: "<<A<<std::endl; std::cout<<"B es igual: "<<B<<std::endl; B= A + B; std::cout<<"Tras la suma:"<<std::endl<<std::endl; std::cout<<"A es igual: "<<A<<std::endl; std::cout<<"B es igual: "<<B<<std::endl; return 0; } punto::punto(int j, int i){ x=j; y=i; } punto punto::operator+ (punto P){ return punto(x+P.x,y+P.y); } std::ostream& operator << (std::ostream& os, punto P){ os<< "X= "<< P.x <<"; Y = "<< P.y<<";"<<std::endl; return os; }

Creacin del Proyecto GTK Bsico


GTK, es la librera que vamos a utilizar para desarrollar aplicaciones grficas, con ventanicas. Segn tengo entendido, Gtk fue inicialmente desarrollada para el genial programa The Gimp. En la programacin con la librera GTKmm (GTK para C++), existent varias posibilidades: una de ellas es definir las ventanas en un archivo externo, realizado con Glade, e importarlo en la ejecucin de la aplicacin u, otra posibilidad, no hacer uso del mismo y definir las caractersticas de nuestras ventanicas en el cdigo fuente. Vamos a empezar con esta ltima opcin, para ello, tenemos varias posibilidades en la creacin del proyecto, lo que aprovechar para explicar algunas cosillas:

PROYECTO GTKMM GENRICO


La primera forma para realizar un proyecto GTKmm bsico, es seleccionar en el asistente de proyectos la opcin GTKmm genrico:

Tras completar todos los apartados del proyecto, se autogenerar un proyecto con un archivo main.cc que utiliza las libreras gtkmm y libglademm (que permite importar archivos desarrollados con el programa glade.)

Vamos a reeditar el archivo main.cc con el siguiente cdigo, la aplicacin ms simple que podemos desarrollar.
#include <gtkmm.h> int main(int argc, char *argv[]) {

Gtk::Main programa(argc, argv);

Gtk::Window miventanica; programa.run(miventanica);

return 0; } Al ejecutarlo la salida es una ventanica (200*200) cuyo ttulo es el nombre de la aplicacin.

EXPLICACIN CDIGO
Bsicamente, nuestras aplicaciones ejecutan otra aplicacin, un proceso Gtk, el cual correr con la ventana que nosotros diseamos. #include <gtkmm.h> La directiva include, va a incluir en nuestro archivo la definicin de clases Gtk, que nos van a permitir desarrollar programas grficos. Gtk::Main programa(argc, argv); Esta lnea declara un objeto de nombre programa, del tipo Gtk::Main. En Programacin orientada a objetos, Poo, es habitual hablar de objetos y en vez de variables. El objeto Gtk::Main es obligatorio en todo programa Gtk, y debe ser el primero en crearse. Es la aplicacin, el proceso que se va a ejecutar.

Gtk::Window miventanica; Declaramos y definimos nuestra ventana GTK. programa.run(miventanica); Lanzamos la aplicacin Gtk, con nuestra ventana. Esto es, estar capturando eventos (pulsacin de teclas, movimientos de ratn,...) y gestionndolos (en nuestro caso, no hemos aadido cdigo alguno, solo las opciones por defecto del objeto Gtk:Window [maximizar,...,cerrar]) hasta que se cierre la ventana; momento que volver a la funcin main, donde aparece el return 0; que indica el final con xito de nuestra aplicacin.

PROYECTO GENRICO C++ CON PAQUETES EXTERNOS Una segunda forma de realizar el proyecto GTKmm bsico, es partiendo de un proyecto C++ Genrico, pero incluyendo la opcinconfigurar paquetes externos.

Nos aparecer una ventana adicional para introducir el nombre del paquete:

No importa mucho, que en ese momento no nos acordemos del nombre de la librera, lo importante es que Anjuta va a configurar el proyecto para que al incluir las libreras necesarias, no obtengamos errores.

Al autogenerar, nos dar un problema (A en el grfico): no encuentra nuestra librera "noimportamucho". Entonces accedemos a la ventana propiedades del Proyecto [Proyecto->Propiedades], y en la pestaa paquetes podremos quitar "noimportamucho" y aadir el paquete correcto [gtkmm-2.4]. Al pulsar aadir paquete encontramos todos los paquetes disponibles, y la certeza de incluir la versin correcta. Una vez realizado esto al autogenerar no habr problemas.

Vamos a reeditar el archivo main.cc con el siguiente cdigo://


#include <gtkmm.h> // Incluir la librera que nos permite usar GString #include <glib.h>

Incluir la libreria gtkmm.h

int main(int argc, char *argv[]) { GString * mensaje; //declaracin de la cadena mensaje mensaje = g_string_new( "Hola "); // primera asignacin a la cadena mensaje if (argc>1) { g_string_append(mensaje, argv[1]); //si existe un argumento aade a Hola, el primer argumento. }else{ g_string_append (mensaje, "DESCONOCIDO");// si no hay argumentos el mensaje es Hola DESCONOCIDO } Gtk::Main programa(argc, argv); // Crear un objeto Main de Gtk, obligatorio en todo proyecto GTKmm Gtk::Window miventanica;// Crear un objeto Window de Gtk -la ventanica miventanica.set_title(mensaje->str);// establece el ttulo programa.run(miventanica);// Echar a correr nuestra ventana return 0; }

Al ejecutarlo la salida es igualmente una ventanica (200*200), donde hemos modificado el ttulo de la ventana.

EXPLICACIN CDIGO
Cuando nosotros escribimos en un terminal: nombre_programa -p 12 -q 4 El sistema ejecuta la funcin main con la variable argc, de tipo entero, indicando el nmero de argumentos ( 5 en nuestro caso). Y cada uno de estos argumentos va como cadenas de caracteres (aunque se traten de nmeros). As, argv[0]= "/directorio/nombre_programa" argv[1]= "-p" argv[2]= "12" argv[3]= "-q" argv[4]="4" #include <glib.h> GString * mensaje; C++, no incluye "de serie" el tipo cadena (string) [no poda ser perfecto], as que para manipularlas de forma fcil (sin preocuparnos por la gestin de memoria) he declarado un objeto GString, definido en glib.h. Exactamente, declaramos un puntero a un objeto, pero de las variables puntero hablaremos con mayor extensin en apartados posteriores. mensaje = g_string_new( "Hola "); if (argc>1) { g_string_append(mensaje, argv[1]); }else{ g_string_append (mensaje, "DESCONOCIDO"); } Inicializamos el objeto GString con la cadena Hola y, posteriormente, en funcin de si existen o no existen argumentos, aadimos el primer argumento a la cadena anterior o, en el caso que no existan argumentos "DESCONOCIDO", de tal suerte que si al ejecutarlo como parmetros aadimos Pablo, la cadena resultante ser Hola Pablo y si no existen parmetros ser Hola DESCONOCIDO miventanica.set_title (mensaje->str);

Entre la declaracin del objeto Window (Gtk::Window miventanica;) y el lanzamiento de la aplicacin Gtk::Main, hemos incluido el cdigo que configura nuestro interfazEn este caso solo una lnea de cdigo que modifica el ttulo de la ventana.
Esta funcin, set_title, recibe como argumento una cadena de caracteres.

PROYECTO C++ GENRICO


Puede ocurrir que al crear el proyecto con el asistente, creemos un proyecto C++ genrico y se nos olviden configurar los paquetes externos. Podras pensar que no importa, porque en las propiedades del proyecto [Proyecto->Propiedades], podemos aadir un mdulo y posteriormente aadir la librera gtkmm-2.4, de manera similar a como hicimos con el anterior (eso mismo pens yo), pero lo cierto es que tenemos que trastear algo ms.

Vamos a probarlo, creamos un proyecto C++ Genrico (en mi caso lo he llamado basico2 y la carpeta destino es Gtk3) e introducimos el siguiente cdigo en main.cc:
#include <gtkmm.h> #include <glib.h> int { main(int argc, char *argv[]) GString * mensaje; mensaje = g_string_new( "Hola "); if (argc>1) { g_string_append(mensaje, argv[1]); }else{ g_string_append (mensaje, "DESCONOCIDO"); } Gtk::Main programa(argc, argv); Gtk::Window miventanica; Gtk::Label etiqueta(mensaje->str); miventanica.add(etiqueta); etiqueta.show(); miventanica.set_title("maestrodenada.com"); Gtk::Main::run(miventanica);// Echar a correr nuestra ventana

return 0; } Al ir a construir el proyecto da infinidad de errores, comenzando porque no conoce gtkmm.h (origen de todos los dems). Para solucionarlo vamos editar las propiedades de proyecto, aadimos un modulo (en mi caso, BASICO de nombre) y luego nos permite aadir paquetes, aadiendo gtkmm2.4, como hicimos en el apartado anterior. Pero al construir el proyecto, vuelve a dar el mismo error why? A mi entender es un pequeo error de Anjuta, aunque quizs tenga alguna razn de ser... Pero vayamos a la solucin: Seleccionamos la visualizacin del proyecto (en rojo) y editamos las propiedades de main.cc:

En la caja de texto Bibliotecas, introducimos $(BASICO_LIBS):

Y en las propiedades de src, en las opciones del compilador, en preprocesado debe aadirse [espacio]$(BASICO_CFLAGS):

El espacio resulta esencial, sin el espacio no se realiza correctamente la construccin del proyecto.
Ahora s, funciona y al ejecutarlo (si como argumento introducimos lolo) aparece esto:

..que estirndolo...

EXPLICACIN CDIGO
Como novedad, vemos que en la configuracin de la interfaz hemos introducido las siguientes lneas: Gtk::Label etiqueta(mensaje->str); miventanica.add(etiqueta); etiqueta.show(); miventanica.set_title("maestrodenada.com"); Gtk::Label etiqueta(mensaje->str); Declara e inicializa un objeto del tipo Gtk::Label, texto no editable por el usuario. Este objeto al cual he nombrado etiqueta, se ha inicializado con el valor de la cadena mensaje, que anteriormente asocibamos al ttulo de la ventana. La cual ahora tiene por ttulo maestrodenada.com miventanica.add(etiqueta); En esta instruccin asociamos o aadimos nuestra etiqueta a miventanica. Nuestro Gtk::Label, ha sido creado en la instruccin anterior pero no perteneca al Gtk::Window, es preciso aadir a la ventana los distintos componentes. Esto es as, y no puede automatizarse, porque en las aplicaciones importantes puede haber varias ventanas, cada una de ellas con distintos componentes. etiqueta.show(); Muestra la etiqueta. En una aplicacin, puedo tener componentes visibles u ocultos en funcin de la situacin. En principio, todos los componentes estn ocultos, razn por la cual debemos hacerlos visibles.

Aadiendo Componentes: los contenedores


La funcin add de Gtk::Window nos ha permitido aadir el componente Label o etiqueta en el programa anterior. Quizs estemos tentados en aadir otro componente (por ejemplo, un botn Gtk::Button) aadiendo las siguientes lneas de cdigo a nuestro programa: #include <glib.h> int main(int argc, char *argv[]) { GString * mensaje; mensaje = g_string_new( "Hola "); if (argc>1) { g_string_append(mensaje, argv[1]); }else{ g_string_append (mensaje, "DESCONOCIDO"); } Gtk::Main programa(argc, argv); Gtk::Window miventanica; Gtk::Label etiqueta(mensaje->str); miventanica.add(etiqueta); etiqueta.show(); miventanica.set_title("maestrodenada.com"); Gtk::Button contador("pulsame"); miventanica.add(contador); contador.show(); Gtk::Main::run(miventanica);// Echar a correr nuestra ventana

return 0; } El programa compila y se ejecuta, pero sin aparecer el botn. Y en el terminal, nos aparece un mensaje de advertencia, indicndonos que el Gtk::Window solo admite un widget...qu decepcin! pues rara vez un programa tendr slo un componente. Esto es as, porque Gtk::Window est desarrollada como un contenedor simple, slo admite un widget [heredan de Gtk::Bin la funcin add() y remove(), para aadir y quitar el widget]. Para poder realizar ventanas con varios componentes, Gtkmm tiene implementados unos widget que admiten varios widgets: los contenedores. Para aadir controles en estos contenedores, se utiliza la funcin pack_start() [no add()]. Esto es, para realizar algo como esto:

Vamos a aadir a Gtk::Window un widget que permita aadir a su vez varios widgets, ser el Gtk::VBox , con la funcin add(). En este VBox (caja vertical) vamos a aadir tres elementos- con pack_start() -, un widgets etiqueta (Gtk::Label), una lnea separadora (Gtk::HSeparator) y un botn (Gtk::Button).

int main(int argc, char *argv[]) { Gtk::Main programa(argc, argv); // declarando todo lo que hace falta Gtk::Window miventanica; Gtk::VBox principal; Gtk::HSeparator linea; Gtk::Label etiqueta("Pulsa abajo"); Gtk::Button contador("AQU!"); // aadimos el VBox al Window miventanica.add(principal);

// aadiendo nuestros elementos al VBox principal.pack_start(etiqueta); principal.pack_start(linea); principal.pack_start(contador); // hacindolo todo visible principal.show(); etiqueta.show(); contador.show(); linea.show(); miventanica.set_title("maestrodenada.com"); miventanica.resize(260,110); Gtk::Main::run(miventanica); return 0; }

GTK++ con CLASES


En el estilo bsico utilizamos punteros (*) y referencias a objetos (&) para poder interactuar con el usuario, y para que las funciones tengan conocimiento de los objetos creados en la funcin main(). En grandes programas esta funcin main() resultara bastante complicada. Lo habitual es utilizar las clases de C++. Las clases son un tipo de dato definido por el usuario, que no slo almacenan los datos, sino que tambin establecen las operaciones a realizar por el mismo, ya hemos visto un pequeo ejemplo. Ahora vamos a realizar nuestro programa con clases. Modificamos el archivo de main.cc para que quede tal que as: /* ************************* * MAESTRODENADA.COM * * presenta * * el * * programa con clase *

* de Gtk * ***************************/ #include <gtkmm.h> #include <iostream> #include <stdlib.h> // DECLARACIN DE LA CLASE class ventana:public Gtk::Window { public: ventana(); // declaracin constructor virtual ~ventana(); // declaracin destructor protected: int veces; // ahora veces es miembro de ventana // Relacin de widgets Gtk::VBox principal; Gtk::HSeparator linea; Gtk::Label etiqueta; Gtk::Button contador; // seales virtual void pulsa_contador(); }; // DEFINICIN DE LOS MTODOS DE LA CLASE // constructor de la clase ventana::ventana():principal(),linea(),etiqueta("Pulsa abajo"),contador("AQU!") { veces=0; add(principal); principal.pack_start(etiqueta); principal.pack_start(linea); principal.pack_start(contador); principal.show(); etiqueta.show(); linea.show(); contador.show();

contador.signal_clicked().connect(sigc::mem_fun(*this,&ventana::pulsa_contador) ); set_title("maestrodenada.com"); resize(260,110); } // destructor ventana::~ventana(){std::cout<<"Eso es todo amigo"<<std::endl;} // funcin para manipular la seal void ventana::pulsa_contador(){ char h[30]; sprintf (h, "has pulsado %i veces el botn", ++veces); etiqueta.set_text(h); } // la funcin main, qu pequea! int main(int argc, char *argv[]) { Gtk::Main programa(argc, argv); ventana miventanica; Gtk::Main::run(miventanica); return 0; }

EXPLICACIN CDIGO:
Empecemos con la funcin main(), que cortita se ha quedado. La nica diferencia respecto a nuestra primera aplicacin es que donde apareca Gtk::Window miventanica; aparece ventana miventanica; Cuando declaramos la clase, aparece class ventana: public Gtk::Window, esto quiere decir que ventana es un tipo de Gtk::Window, con caractersticas especiales. A este proceso se denomina herencia, y con ello heredamos todos los mtodos de la clase madre Gtk::Window (add,

resize, set_title,...); a nosotros solo nos queda aadir las peculiaridades del tipo ventana. As cuando declaramos miventanica, este objeto es del tipo ventana, que a su vez es un Gtk::Window. Cuando se declara un objeto de una clase, se procesa una funcin especial denominada constructor . El mtodo constructor tiene siempre el mismo nombre que la clase. As que en la declaracin de la clase ventana encontramos el mtodo ventana(); , pero en la definicin del constructor encontramos: ventana::ventana():principal(),linea(),etiqueta("Pulsa abajo"),contador("AQU!") Los dos puntos indican que antes de procesar est funcin debe llamar

La definicin de la clase es como una descripcin de los mtodos y datos que contiene un objeto de este tipo, pero no son en s objetos.Observa por ejemplo que en la declaracin de la clase, encontramos
a los constructores de los diferentes widgets de Gtk (clases) que igualmente requieren ser llamados cuando se crea el objeto. Gtk::Button contador; que quiere decir que en la clase existir un botn con nombre contador. Pero es en el constructor donde aparece contador("AQU"), con lo cual se procesarlo mismo que con Gtk::Button contador("AQU");como en los programas anteriores. Una vez declarados los widgets, se inicializa veces (variable que ha pasado a ser de la clase y no global), y se procede como en el programa anterior. Quizs extrae que no utilizamos el operador punto [.]. En vez de miventanica.add(principal), se utiliza add(principal); e igual ocurre con los mtodos set_title y resize. La razn es sencilla, en main podramos tener varios objetos definidos y debemos determinar a que objeto nos referimos, pero aqu, el objeto est implcito. contador.signal_clicked().connect(sigc::mem_fun(*this,&ventana::pulsa_contador) ); Si existen diferencias en la conexin o manejo de seales. La funcin sigc::ptr_fun(&funcin) ha sido modificada por sigc::mem_fun(*this, &funcin). la funcin es diferente e incluye dos argumentos: el objeto y la funcin. this es una palabra clave de C++ que designa la direccin de memoria del objeto, un puntero al objeto; por lo que *this es el objeto en s. De hecho, cuando antes hemos dicho que add, ni resize utilizaban el operador [.], en realidad el compilador interpreta add(principal) como *this.add(principal). public, protected, private En la declaracin de la clase se observa que algunos mtodos o datos son declarados public, y otros protected. Estos son palabras clave que determinan el alcance de los mtodos y datos. Esto es, si declaro un elemento private o protected, ningn objeto podr manipularlo. Por ejemplo, si en main, antes de correr el proceso pongo miventanica.veces=5; obtendr un error en la compilacin porque es un dato protegido. La manipulacin de este dato solo puede realizarse internamente, en los mtodos de la clase. A los mtodos o datos pblicos, public, si puede accederse con el objeto. Lo normal al crear una clase es crear unos mtodos pblicos para el control de los datos, privados, evitando que el programador pueda realizar un uso incorrecto de los mismos. virtual Tambin puedes ver, que los mtodos se han declarado virtuales (virtual). Lo hago as por razones de diseo,( lo hacen as en los ejemplos que he podido consultar), pero es igualmente cierto que en este ejemplo no hace falta. La definicin de mtodos virtuales indican al compilador que las

clases derivadas tendrn implementaciones diferentes en mtodos con igual nombre, pero lo cierto es que nosotros no vamos a obtener una nueva clase a partir de ventana, al menos en este ejemplo. Puedes suprimirlo (/* virtual */ ) y vers como sigue funcionando. ventana::~ventana(){std::cout<<"Eso es todo amigo"<<std::endl;}Esta funcin especial que tiene el mismo nombre que la clase, pero va precedido por ~, se denomina destructor. Y es la funcin que se ejecuta cuando se libera un objeto. miventanica se crea en main, al concluir main se libera ejecutndose. Observa que la salida se realiza por consola.

GTK++ con CLASES


COMPILACIN SEPARADA
Lo habitual, aunque no es necesario, es que la clase se escriba en archivos distintos al archivo principal (main.cc). Y decimos archivos, en plural, porque en C++ existen dos tipos de archivo: los .h y los .cc Los .h son archivos de cabecera e incluyen las declaraciones de funciones y variables. Los .cc son los archivos de cdigo e incluyen todas las definiciones de las funciones, el cdigo.

Vamos a descomponer nuestro programa en varios archivos, empecemos con main.cc, que se quedar tal que as:
/* ************************* * MAESTRODENADA.COM * * presenta * * el * * programa con clase * * de Gtk * ***************************/ #include <gtkmm.h> #include "ventana.h" int main(int argc, char *argv[]) { Gtk::Main programa(argc, argv); ventana miventanica;

Gtk::Main::run(miventanica); return 0; } Observa que como en main(), solo se utilizan los tipos Gtk::Main, y ventana, en los include solo aparecen las libreras gtkmm.h y ventana.h. Como quiera que ventana.h es el archivo de cabecera donde vamos a declarar la clase y no va a estar en los directorios del sistema sino en el nuestro, no se utiliza <>, sino las comillas: #include "ventana.h"

El archivo ventana.h, (pulsa archivo nuevo, copia y pega, y lo grabas en el mismo directorio):
#ifndef _VENTANA_H #define _VENTANA_H #include <gtkmm.h> // DECLARACIN DE LA CLASE class ventana:public Gtk::Window { public: ventana(); // declaracin constructor virtual ~ventana(); // declaracin destructor protected: int veces; // ahora veces es miembro de ventana // Relacin de widgets Gtk::VBox principal; Gtk::HSeparator linea; Gtk::Label etiqueta; Gtk::Button contador; // seales virtual void pulsa_contador(); }; #endif Aqu solo aludimos a objetos GTK, por lo que solo precisamos incluir la libreria gtkmm. Ahora bien, se observan nuevas directivas del preprocesador:

#ifndef XXXXXXXXX

...

#endif

Esta directiva le dice al compilador que si no se encuentra definida la macro XXXXXXXXX se proceda a la lectura e interpretacin del texto hasta #endif. define XXXXXXXXXX Esta directiva define durante la compilacin la siguiente macro. Con esto, conseguimos que la declaracin de la clase se lea una nica vez por el compilador. La primera vez que se accede al archivo se define la macro, en futuras ocasiones ya se encuentra definida la macro y no se lee el archivo. Si esto no fuera as, y tuvisemos varios ficheros leyendo ventana.h, la clase sera declarada en varias ocasiones, generando confusin al compilador y, por tanto, error.

Por ltimo, ventana.cc:


#include #include #include #include "ventana.h" <gtkmm.h> <iostream> <stdlib.h>

// DEFINICIN DE LOS MTODOS DE LA CLASE // constructor de la clase ventana::ventana():principal(),linea(),etiqueta("Pulsa abajo"),contador("AQU!") { veces=0; add(principal); principal.pack_start(etiqueta); principal.pack_start(linea); principal.pack_start(contador); principal.show(); etiqueta.show(); linea.show(); contador.show(); contador.signal_clicked().connect(sigc::mem_fun(*this,&ventana::pulsa_contador) ); set_title("maestrodenada.com"); resize(260,110); } // destructor ventana::~ventana(){std::cout<<"Eso es todo amigo"<<std::endl;}

// funcin para manipular la seal void ventana::pulsa_contador(){ char h[30]; sprintf (h, "has pulsado %i veces el botn", ++veces); etiqueta.set_text(h); } En este cdigo se utilizan las libreras stdlib.h [sprintf()], iostream [std::cout], gtkmm [of ocurse] y ventana.h, que contiene la declaracin de la clase.

Vamos a proceder a construir el proyecto...ERROR no est definido ventana::ventana()

Esto es as, porque aunque estemos editando a la vez los distintos archivos, no hemos realizado la inclusin de los mismos en el proyecto, y las ordenes de compilacin solo hacen referencia a main.cc.

SOLUCIN

En el men proyecto, pulsamos sobre [Aadir archivo fuente], aparecindonos la ventana de la izquierda. Buscamos nuestro archivo cc, pulsando sobre el botn [Seleccione un archivo para aadir]. Como objetivo seleccionamos el ejecutable. En mi caso, basico2. Y finalmente [aadir] Tras esto la construccin del proyecto y la compilacin no dan errores.

Estructura Bsica
Vamos a crear un proyecto nuevo, del tipo GTKMM genrico. Eliminando los comentarios el cdigo resultante es: #include <libglademm/xml.h> #include <gtkmm.h> #include <iostream> #ifdef ENABLE_NLS # include <libintl.h> #endif #define GLADE_FILE "basico.glade" int main (int argc, char *argv[]) { Gtk::Main kit(argc, argv); Glib::RefPtr<Gnome::Glade::Xml> refXml; try { refXml = Gnome::Glade::Xml::create(GLADE_FILE); } catch(const Gnome::Glade::XmlError& ex) { std::cerr << ex.what() << std::endl; return 1; } Gtk::Window* main_win = 0;

refXml->get_widget("main_window", main_win); if (main_win) { kit.run(*main_win); } return 0; }

La salida es una ventana cuyo ttulo es Hello World! y poco ms. Tanto el ttulo como las dimensiones no aparecen en el cdigo, dnde se encuentran? en el archivo nombre_programa.glade. La utilizacin conjunta de Glade y C++, implica que nuestro programa ir acompaado con un archivo .glade, que es ledo por el programa durante la ejecucin del mismo (tiempo de ejecucin). Si editamos el archivo nombre_programa.glade:
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> <!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">

<glade-interface> <widget class="GtkWindow" id="main_window"> <property name="visible">True</property> <property name="title" translatable="yes">Hello World!</property> <property name="type">GTK_WINDOW_TOPLEVEL</property> <property name="window_position">GTK_WIN_POS_NONE</property> <property name="modal">False</property> <property name="default_width">500</property> <property name="default_height">400</property> <property name="resizable">True</property> <property name="destroy_with_parent">False</property> <property name="decorated">True</property> <property name="skip_taskbar_hint">False</property> <property name="skip_pager_hint">False</property> <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property> <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> <child> <placeholder/> </child> </widget> </glade-interface>

Observamos que es un archivo xml (un tipo de archivo de texto). Glib::RefPtr<Gnome::Glade::Xml> refXml; Esta es la declaracin de refXml. refXml es un puntero a un objeto Gnome::Glade::Xml, que es una clase que permite la manipulacin de los archivos xml, que es lo que genera Glade. Glib::RefPtr aspira a ser una clase genrica de Glib para la manipulacin de los punteros (Glib tiene "copias" de los tipos predefinidos gint para int,...) Segn la documentacin esto es necesario para una adecuada gestin de la memoria, ?; la cuestin es que el uso de Gnome::Glade::Xml* no es factible. <Gnome::Glade::Xml> es la forma (plantillas) de utilizar el puntero bsico de Glib como del tipo Gnome::Glade::Xml. Este puntero hay que inicializarlo, como cualquier otro, cosa que realiza la instruccin : refXml = Gnome::Glade::Xml::create(GLADE_FILE); Esta funcin recibe como argumento el archivo .glade de la aplicacin. try {

refXml = Gnome::Glade::Xml::create(GLADE_FILE); } catch(const Gnome::Glade::XmlError& ex) { std::cerr << ex.what() << std::endl; return 1; } Esta operacin de inicializacin puede fracasar, es decir, que no se pueda crear el objeto. Por eso, la inicializacin est inmersa en una estructura try ..catch. Si el cdigo de try fracasa, se ejecuta el cdigo de catch, que bsicamente informa del error y sale del programa indicando la existencia del mismo. Gtk::Window* main_win = 0; refXml->get_widget("main_window", main_win);refXml es un puntero, por lo que para acceder a sus funciones miembro, debemos usar el operador [->] . La funcin get_widget recibe dos argumentos, el primero es el nombre del widget en el archivo glade y el segundo es el puntero del widget. Esta funcin crea un nuevo widget, tomando los datos del archivo glade, y asigna la direccin de ste a la variable puntero. Observa que main_win es un puntero a un Gtk::Window y no un Gtk::Window, el cual es declarado e inicializado a 0 (no apunta a nada) en la primera instruccin. if (main_win) { kit.run(*main_win); }A la hora de lanzar o ejecutar el proceso, primero se comprueba que la operacin ha sido vlida. Si main_win es un puntero a Gtk::Window, *main_win es el objeto Gtk::Window. quizs te preguntes las diferencias entre if y try? Cuando verificamos con un if ( xxxx ), donde xxxx es un puntero al que hemos asignado una parte de la memoria dinmica, esta operacin solo comprueba si el puntero era NULL, operacin fallida, o no lo era (asignacin correcta). Pero carecemos de las razones por las que se ha producido el error, lo cual en programas extensos puede ser desalentador. Con try...catch, la clase exception, podemos generar excepciones y gestionarlas, informando de ellas. La clase exception, de la cual heredar Gnome::Glade::XmlError, tiene una funcin virtual what() que devuelve una cadena de texto (para informar del tipo de error). El programa intenta ejecutar el contenido de try, el mtodo Gnome::Glade::Xml::create(), si hubiera algn problema ste lanzar la excepcin correspondiente la excepcin se gestiona en catch, que no hace sino imprimir la cadena del error correspondiente y salir del programa de forma errnea (return 1;).

Glade Bsico
Ahora vamos a realizar nuestro programa usando Glade.
Para ello, vamos a editar nuestro archivo .glade. Podemos realizarlo con el interfaz de Anjuta, o abriendo el archivo con el programa Glade (Es mi opcin preferida, porque as se ven todos los aspectos del programa):

Vamos a crear nuestra interfaz de forma visual. En primer lugar, en la ventana de propiedades (abajo-derecha), vamos a cambiar las propiedades de main_window:

Nombre-> miventanica Ttulo-> maestrodenada.com Luego en el lateral izquierdo, seleccionamos (clikamos) el contenedor VBox (lnea roja fina) y pulsamos sobre la ventana grfica. Nos pregunta por el nmero de filas, ? Como nosotros, le aadamos tres cosas (tres pack_start), pues le decimos que tres (por defecto). Abajo (aqu el orden no importa mucho) ponemos el botn. Pero voy a hacer un poco de trampa, voy a seleccionar una caja de botones horizontal (marco rojo grueso) y "cliko" sobre la zona inferior. A la pregunta que me realiza (n de columnas) le voy a indicar que 1 (pues slo quiero un botn). El botn sale centrado (colocacin: default), pero podamos optar por que estuviese desplazado (colocacin:start o end). Luego Click sobre el control Button (lnea azul gruesa) y pulsamos en la caja de botones. Editamos sus propiedades: Nombre-> boton etiqueta-> AQU! Arriba el control Label (seleccin y pulsar sobre zona). En la ventana de propiedades, cambiamos: Nombre-> etiqueta etiqueta-> Pulsa el botn de abajo Seleccionamos la lnea horizontal y pulsamos en la zona media. Como no voy a modificar ste durante la programacin, dejo los valores por defecto. Ahh! lo que si voy a hacer es en la pestaa EMPAQUETADO del editor de propiedades, modificar la opcin de expandir: para la etiqueta voy a poner s y para la lnea no. Vara el tamao de la ventana y observa las diferencias (cuestin de gustos). Nuestro main.cc: #include <libglademm/xml.h> #include <gtkmm.h> #include <iostream> #ifdef ENABLE_NLS # include <libintl.h> #endif /* For testing propose use the local (not installed) glade file */ /* #define GLADE_FILE PACKAGE_DATA_DIR"/basico/glade/basico.glade" */ #define GLADE_FILE "basico.glade" Gtk::Label* etiqueta; int veces=0; void pulsar_boton(); int main (int argc, char *argv[]) { Gtk::Main kit(argc, argv);

//Load the Glade file and instiate its widgets: Glib::RefPtr<Gnome::Glade::Xml> refXml; try { refXml = Gnome::Glade::Xml::create(GLADE_FILE); } catch(const Gnome::Glade::XmlError& ex) { std::cerr << ex.what() << std::endl; return 1; } Gtk::Window* mi_aplicacion; // Inicializacin de mi_aplicacion refXml->get_widget("miventanica", mi_aplicacion); if (mi_aplicacion) { // Declaracin de boton Gtk::Button* boton; // Inicializacin de boton refXml->get_widget("boton", boton); if (boton){ boton->signal_clicked().connect(sigc::ptr_fun(pulsar_boton)); // Inicializacin de etiqueta refXml->get_widget("etiqueta", etiqueta); if (etiqueta){ // el programa se ejecuta si todo ha ido bien kit.run (*mi_aplicacion); } } } return 0; } void pulsar_boton() { char h[30]; sprintf (h, "Has pulsado %i veces el botn", ++veces); etiqueta->set_text(h); } El archivo .glade puedes cambiarlo sin necesidad de recompilar el proyecto, puedes modificarlo y ejecutar el programa, ... mientras se mantengan

los nombres de los widgets utilizados en el cdigo, no ha de haber problemas. Si alguna vez las modificaciones realizadas en el archivo .glade, no se observan en la ejecucin, quizs se deba al pequeo detalle de no haber grabado el programa.

Alguna vez me ha ocurrido que Anjuta se queda colgado en el proceso de carga si el proyecto contiene un Glade. La solucin pasa por ir al Monitor del sistema y finalizar el proceso Anjuta; luego ir a /home/usuario/.Anjuta/session y borrar el archivo session. Volvemos a iniciar Anjuta y ste se ejecuta sin cargar ningn proyecto anterior. En Recientes abrimos el proyecto y sin problemas.

EXPLICACIN
Lo nico a destacar es el anidamiento de if (un if dentro de otro), para ir comprobando que los distintos punteros son asignados correctamente, antes de ejecutar el programa. Observa nuevamente como a diferencia de programas anteriores, aqu ya todas las variables son puntero: etiqueta es global y boton es local.

Glade con Clases En C++, lo suyo es programar con clases. A mi parecer, libglademm no est muy bien desarrollado para trabajar con clases. He buscado ejemplos de implementaciones de C++ con Glade, y no he encontrado nada ms que un simple ejemplo, a partir del cual desarrollo el apartado posterior. Tal y como hicimos con Gtk(con clases), uno tiene la tentacin de simplificar al mximo la funcin main(), introduciendo casi todas las operaciones en el constructor de la clase, quedando as: // main.cc #include <gtkmm.h> #include <libglademm/xml.h> #include "miventanica.h"

int main(int argc, char *argv[]) { Gtk::Main programa(argc, argv); miventanica ventana;

Gtk::Main::run(*ventana.yo); return 0; } // miventanica.h #include <gtkmm.h> #include <libglademm/xml.h> // DECLARACIN DE LA CLASE class miventanica { public: miventanica(); virtual ~miventanica(); Gtk::Window * yo; protected: int veces; Glib::RefPtr<Gnome::Glade::Xml> refXml; Gtk::Button* boton; Gtk::Label* etiqueta; // seales virtual void pulsa_contador(); }; //miventanica.cc #include #include #include #include #include <gtkmm.h> <iostream> <stdlib.h> <libglademm/xml.h> "miventanica.h"

// DEFINICIN DE LOS MTODOS DE LA CLASE miventanica::miventanica() { veces=0; refXml = Gnome::Glade::Xml::create("basico.glade"); refXml->get_widget("miventanica", yo); refXml->get_widget("boton", boton); boton->signal_clicked().connect(sigc::mem_fun(*this,&miventanica::pulsa_contador)); refXml->get_widget("etiqueta", etiqueta); } // destructor miventanica::~miventanica(){std::cout<<"Eso es todo amigo"<<std::endl;} // funcin para manipular la seal void miventanica::pulsa_contador(){ char h[30]; sprintf (h, "has pulsado %i veces el botn", ++veces); etiqueta->set_text(h); } Bsicamente, hemos escondido el cdigo anterior en una clase. La nica modificacin importante es que la clase miventanica no es una Gtk::Window, sino que contiene una (*yo). Lo he hecho as porque ninguna de las instrucciones siguientes eran reconocidas: refXml->get_widget ("miventanica", this); refXml->get_widget ("miventanica", (Gtk::Window* )this); La funcin run de Gtk::Main requiere una Gtk::Window, miventanica no lo es, por tanto precisamos que *yo sea pblico, accesible. As, funciona... Sin embargo, no es muy buena opcin, ? En nuestro ejemplo, existe solo una ventana, pero qu ocurre con proyectos que utilizan muchas ventanas (fuentes, colores, grabar archivos, configuracin, impresin,...)? Si cada ventana tiene su clase (como es normal) y cada ventana "carga" el documento .glade, tendremos nuestra

RAM almacenando innecesariamente el mismo documento varias veces.

La solucin pasa por declarar en inicializar en main a refXml y que las clases tengan una referencia al mismo:

Para ello, debemos pasar la direccin de memoria del documento a la clase...

Modifiquemos nuestro archivo glade para que incluya una segunda ventana: un cuadro de dilogo, con un botn

[aceptar]. Tal que as:


Editemos el nombre de la ventana y del botn: ventana: Nombre-> dialogo botn: Nombre-> botonAceptar y SOBRETODO no se nos olvide poner dialogo como NO visible (en Propiedades- Comunes-Visible), porque de lo contrario no aparecer cuando nosotros queramos, sino al principio. //main.cc #include #include #include #include <gtkmm.h> <libglademm/xml.h> <iostream> "miventanica.h"

int main(int argc, char *argv[]) { Gtk::Main programa(argc, argv); Glib::RefPtr<Gnome::Glade::Xml> refXml; try { refXml = Gnome::Glade::Xml::create("basico.glade"); } catch(const Gnome::Glade::XmlError& ex) { std::cerr << ex.what() << std::endl; return 1; }

miventanica ventana(refXml); Gtk::Main::run(*ventana.yo); return 0; } //miventanica.h #include <gtkmm.h> #include <libglademm/xml.h> // DECLARACIN DE LA CLASE class miventanica { public: miventanica(Glib::RefPtr<Gnome::Glade::Xml>); virtual ~miventanica(); Gtk::Window * yo; protected: int veces; Glib::RefPtr<Gnome::Glade::Xml> refXml; Gtk::Button* boton; Gtk::Label* etiqueta; virtual void pulsa_contador(); }; //miventanica.cc #include <gtkmm.h> #include <iostream> #include <stdlib.h>

#include <libglademm/xml.h> #include "miventanica.h" #include "miventanica2.h" // DEFINICIN DE LOS MTODOS DE LA CLASE //constructor miventanica::miventanica(Glib::RefPtr<Gnome::Glade::Xml> xxx) { veces=0; refXml = xxx; refXml->get_widget("miventanica", yo); refXml->get_widget("boton", boton); boton->signal_clicked().connect(sigc::mem_fun(*this,&miventanica::pulsa_contador)); refXml->get_widget("etiqueta", etiqueta); } // destructor miventanica::~miventanica(){std::cout<<"Eso es todo amigo"<<std::endl;} // funcin para manipular la seal void miventanica::pulsa_contador(){ if (veces==0){ miventanica2 ventanita(refXml); ventanita.yo->run(); } char h[30]; sprintf (h, "has pulsado %i veces el botn", ++veces); etiqueta->set_text(h); } //miventanica2.h #include <gtkmm.h> #include <libglademm/xml.h>

class miventanica2 { public: miventanica2(Glib::RefPtr<Gnome::Glade::Xml>); virtual ~miventanica2(); Gtk::Dialog * yo; protected: Glib::RefPtr<Gnome::Glade::Xml> refXml; Gtk::Button* botonAceptar; virtual void pulsa_botonAceptar(); };

// miventanica2.cc #include <gtkmm.h> #include <libglademm/xml.h> #include "miventanica2.h" // DEFINICIN DE LOS MTODOS DE LA CLASE //constructor miventanica2::miventanica2(Glib::RefPtr<Gnome::Glade::Xml> xxx) { refXml = xxx; refXml->get_widget("dialogo", yo); refXml->get_widget("botonAceptar", botonAceptar); botonAceptar->signal_clicked().connect(sigc::mem_fun(*this,&miventanica2::pulsa_botonAceptar)); } miventanica2::~miventanica2(){} void miventanica2::pulsa_botonAceptar(){ yo->hide();}

Las diferencias estn en la declaracin de las ventanas, que incorporan como argumento la referencia al documento xml. El dilogo solo se abre la primera vez que se pulsa sobre el botn.

Glade con Clases


LA OPCIN DE GTK
Gtk tiene implementado un mtodo en Gnome::Glade::Xml que nos va a permitir heredar directamente de Gtk::Window: get_widget_derived("nombre_glade_widget", puntero_clase_derivada); El cual impone dos obligaciones al constructor: recibir un puntero bsico de las classes Gtk: BaseObjectType* recibir una instancia de Gnome::Glade::Xml (tal y como hicimos en el ejemplo ltimo). // main.cc #include #include #include #include <gtkmm.h> <libglademm/xml.h> <iostream> "miventanica.h"

int main(int argc, char *argv[]) { Gtk::Main programa(argc, argv); Glib::RefPtr<Gnome::Glade::Xml> refXml; try { refXml = Gnome::Glade::Xml::create("basico.glade");

} catch(const Gnome::Glade::XmlError& ex) { std::cerr << ex.what() << std::endl; return 1; } miventanica* ventana; refXml->get_widget_derived("miventanica", ventana); if(ventana){ Gtk::Main::run(*ventana); } return 0; } Este main es muy parecido a la estructura bsica que crea Anjuta. Simplemente debemos aadir el archivo de nuestra clase (#include "miventanica.h"), declarar un puntero a nuestra clase, en vez de a Gtk::Window (miventanica* ventana;), y finalmente sustituir get_widget por get_widget_derived. //miventanica.h #include <gtkmm.h> #include <libglademm/xml.h> class miventanica:public Gtk::Window { public: miventanica(BaseObjectType* www, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade); virtual ~miventanica(); protected: int veces; Glib::RefPtr<Gnome::Glade::Xml> refXml; Gtk::Button* boton; Gtk::Label* etiqueta; // seales

virtual void pulsa_contador(); }; //miventanica.cc #include <gtkmm.h> #include <iostream> #include <stdlib.h> #include <libglademm/xml.h> #include "miventanica.h" #include "miventanica2.h" // DEFINICIN DE LOS MTODOS DE LA CLASE // constructor de la primera clase miventanica::miventanica(BaseObjectType* www, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade):Gtk::Window(www),refXml(refGlade) { veces=0; refXml->get_widget("boton", boton); boton->signal_clicked().connect(sigc::mem_fun(*this,&miventanica::pulsa_contador)); refXml->get_widget("etiqueta", etiqueta); } // destructor miventanica::~miventanica(){std::cout<<"Eso es todo amigo"<<std::endl;} // funcin para manipular la seal void miventanica::pulsa_contador(){ if (veces==0){ miventanica2* ventanita; refXml->get_widget_derived("dialogo", ventanita); ventanita->run(); } char h[30]; sprintf (h, "has pulsado %i veces el botn", ++veces); etiqueta->set_text(h);

} //miventanica2.h #include <gtkmm.h> #include <libglademm/xml.h>

class miventanica2:public Gtk::Dialog { public: miventanica2(BaseObjectType* www, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade); virtual ~miventanica2(); protected: Glib::RefPtr<Gnome::Glade::Xml> refXml; Gtk::Button* botonAceptar; virtual void pulsa_botonAceptar(); };

// miventanica2.cc #include <gtkmm.h> #include <libglademm/xml.h> #include "miventanica2.h" miventanica2::miventanica2(BaseObjectType* www, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade):Gtk::Dialog(www),refXml(refGlade) { refXml->get_widget("botonAceptar", botonAceptar); botonAceptar->signal_clicked().connect(sigc::mem_fun(*this,&miventanica2::pulsa_botonAceptar)); } miventanica2::~miventanica2(){}

void miventanica2::pulsa_botonAceptar(){ hide();} Personalmente, veo dos problemillas: 1.El constructor de la clase resulta algo engorroso (entre otras cosas porque la creacin del objeto se produce dentro de get_widget_derived, y no alcanzo a saber a ciencia cierta la forma en que se realiza -limitaciones que tiene uno-). 2. y el "Eso es todo, amigo" ?. No se ha ejecutado el destructor , esto implica que la memoria ocupada no ha sido liberada. Podemos comprobarlo: abre un terminal y ejecuta cat /proc/meminfo , observa el valor de MemFree; luego ejecuta nuestro programa y cirralo, vuelve a comprobar la memoria (es menor), afortunadamente nuestro programa es pequeo. Para m, es un error, pues como muchas cosas en la vida (el que la hace, la paga; el que la tira va por ella,...), el encargado de reservar la memoria, debe ser el que la libere. Sin embargo, est formula no lo hace. Debemos liberarla nosotros. ... Lo cual es muy sencillo, simplemente aadir dos delete: // main.cc #include #include #include #include <gtkmm.h> <libglademm/xml.h> <iostream> "miventanica.h"

int main(int argc, char *argv[]) { Gtk::Main programa(argc, argv); Glib::RefPtr<Gnome::Glade::Xml> refXml; try { refXml = Gnome::Glade::Xml::create("basico.glade"); } catch(const Gnome::Glade::XmlError& ex) { std::cerr << ex.what() << std::endl; return 1; } miventanica* ventana;

refXml->get_widget_derived("miventanica", ventana); if(ventana){ Gtk::Main::run(*ventana); } delete ventana; return 0; } //miventanica.cc #include <gtkmm.h> #include <iostream> #include <stdlib.h> #include <libglademm/xml.h> #include "miventanica.h" #include "miventanica2.h" // DEFINICIN DE LOS MTODOS DE LA CLASE // constructor de la primera clase miventanica::miventanica(BaseObjectType* www, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade):Gtk::Window(www),refXml(refGlade) { veces=0; refXml->get_widget("boton", boton); boton->signal_clicked().connect(sigc::mem_fun(*this,&miventanica::pulsa_contador)); refXml->get_widget("etiqueta", etiqueta); } // destructor miventanica::~miventanica(){std::cout<<"Eso es todo amigo"<<std::endl;} // funcin para manipular la seal void miventanica::pulsa_contador(){ if (veces==0){ miventanica2* ventanita; refXml->get_widget_derived("dialogo", ventanita); ventanita->run(); delete ventanita;

} char h[30]; sprintf (h, "has pulsado %i veces el botn", ++veces); etiqueta->set_text(h); }Observa como ahora si aparece "Eso es todo amigo".

Cronmetro Double-Dutch
Queeeeee? A partir de aqu, bsicamente ir aadiendo programas que implican la utilizacin de determinados controles o aspectos. Voy a desarrollarlos progresivamente, poco a poco como buen aficionado, aunque lo suyo es primero realizar un diseo completo y luego proceder a su implementacin, pero no es sta una pgina profesional.

CONTROLANDO EL TIEMPO
Problema
En Rope-Skipping, modalidad deportiva de la comba, existe una prueba [DDRS] de saltos con dos combas [Double Dutch] donde cuatro deportistas se relevan [Relay] en el salto tratando de realizar el mayor nmero de saltos posible [Speed]. Cada deportista tiene 45 segundos [bueno, el primero dispone 45 s., el resto tiene 45 s., pero en ellos estn incluidos el tiempo del intercambio de las posiciones. Mi problema es que mi reloj no me permite realizar cuenta atrs de menos de 1 minuto [un secretillo, maestrodenada es -soy- un profesor de educacin fsica]. Podra cambiar de reloj, o aprovechar la ocasin para realizar un programilla [afortunadamente, trabajo en un centro TIC y dispongo de ordenadores con software libre, qu lujazo!]. Pero, cmo se controla el tiempo? Existen infinidad de programas que requieren realizar las operaciones en un tiempo determinado. Imagina por ejemplo un archivo de audio o vdeo, que esperar a que terminars de editar un archivo de texto para enviar a la tarjeta de sonido la informacin del audio... sera imposible escuchar msica a la par que escribamos un texto. Hay programas que requieren una realizacin ajustada al tiempo real... es el caso de nuestro cronmetro. Cada x tiempo, nuestro programa debe recuperar el control del ordenador.

control timer?
En otros lenguajes existe un control timer (invisible) que uno aade a sus ventanas, este control tiene asociada una funcin, la cual se ejecuta cada

cierto tiempo. Tiempo que viene establecido en una propiedad Interval o similar. En GTK no existen estos controles, no veremos en Glade un control timer que podamos aadir fcilmente a nuestra ventana. Una vez conocido el procedimiento, resulta igualmente fcil. Para controlar el tiempo, tenemos que crear un objeto sigc::connection (como las seales de eventos), con la peculiaridad)que el slot al que va asociado es una funcin bool (que devuelve true o false), si devuelve true, el proceso se continua ejecutando, pero si devuelve false se termina el proceso y no se vuelve a ejecutar la funcin asociada. Esta "conexin" lleva un segundo argumento que es el tiempo, en milisegundos, en el que se suceden la ejecucin de la funcin... bla, bla, bla... Vayamos a la prctica:

Crea un nuevo proyecto (o utiliza el anterior, de hecho...). Edita el archivo .glade igual a anteriores programas: una label con nombre etiqueta, y un botn de nombre boton.En la etiqueta nos aparecer el deportista que ha de saltar y el tiempo restante. La prueba comenzar al pulsar el botn (no al iniciar el
programa) y, una vez pulsado, debemos de desactivarlo hasta que ha finalizado la prueba. //main.cc #include <libglademm/xml.h> #include <gtkmm.h> #include <iostream> #ifdef ENABLE_NLS # include <libintl.h> #endif #include "cron.h" /* For testing propose use the local (not installed) glade file */ /* #define GLADE_FILE PACKAGE_DATA_DIR"/comba/glade/comba.glade" */ #define GLADE_FILE "comba.glade" int main (int argc, char *argv[]) { Gtk::Main popgrama(argc, argv); //Load the Glade file and instiate its widgets:

Glib::RefPtr<Gnome::Glade::Xml> refXml; try { refXml = Gnome::Glade::Xml::create(GLADE_FILE); } catch(const Gnome::Glade::XmlError& ex) { std::cerr << ex.what() << std::endl; return 1; } crono* ventana = 0; refXml->get_widget_derived("miventanica", ventana); if (ventana) { popgrama.run(*ventana); } delete ventana; return 0; } El archivo main.cc, no tiene nada que comentar. Si algo no comprendes es que te has saltado algn apartado o, lo que es peor, me explico fatal. Se detecta que he cambiado el nombre de los ficheros adjuntos (ya no son miventanica.h y miventanica.cc): cron.h y cron.cc. En cron.h debo encontrarme una clase heredada de Gtk::Window (por refXml->get_widget_derived) de nombre crono. //crono.h #ifndef _CRONO_COMBA_H #define _CRONO_COMBA_H #include <gtkmm.h> #include <libglademm/xml.h> class crono:public Gtk::Window { public: crono(BaseObjectType* www, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade); virtual ~crono(); protected: int segundos; int veces; Glib::RefPtr<Gnome::Glade::Xml> refXml;

Gtk::Button* boton; Gtk::Label* etiqueta; sigc::connection conexion; bool tiempo(); // seales virtual void pulsa_contador(); }; #endif En la declaracin de la clase se observa el objeto sigc::connection (conexion) y la funcin bool tiempo(). Tambin hemos incorporado una variable enterasegundos, donde vamos a controlar el nmero de segundos y la variable veces que utilizaremos para ver cuntas veces se ha realizado la cuenta (el nmero de sujetos que ha saltado). Todo el meollo del programa est en crono.cc //cron.cc #include <gtkmm.h> #include <iostream> #include <stdlib.h> #include <libglademm/xml.h> #include "cron.h" //constructor crono::crono(BaseObjectType* www, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade):Gtk::Window(www),refXml(refGlade) { segundos=45; veces=1; refXml->get_widget("boton", boton); boton->signal_clicked().connect(sigc::mem_fun(*this,&crono::pulsa_contador)); refXml->get_widget("etiqueta", etiqueta); } // destructor crono::~crono(){std::cout<<"Eso es todo amigo"<<std::endl; conexion.disconnect();} // funcin para manipular la seal

void crono::pulsa_contador(){ sigc::connection con = Glib::signal_timeout().connect((sigc::slot<bool>)sigc::mem_fun(*this,&crono::tiempo),1000); conexion=con; char h[40]; sprintf(h,"Sujeto 1-> 45:00 segundos"); etiqueta->set_text(h); boton->set_sensitive(false); } bool crono::tiempo(){ char h[40]; if (veces<=4){ if (segundos>1){ sprintf (h, "Sujeto %i-> %i:00 segundos",veces, --segundos); } else{ if(veces!=4)sprintf(h,"CAMBIO");else sprintf(h,"Pulsa abajo para comenzar la prueba"); segundos=45; veces++; } etiqueta->set_text(h); return true; } else{ boton->set_sensitive(); veces=1; return false; } } En el constructor: se inicializa segundos a 45 (duracin de la prueba para cada deportista) y veces. En el destructor: se llama al mtodo disconnect(), por si el programa termina precipitadamente y no concluye la cuenta atrs. En crono::pulsa_contador(): sigc::connection con = Glib::signal_timeout().connect((sigc::slot<bool>)sigc::mem_fun(*this,&crono::tiempo),1000); Cuando se pulsa el botn, se crea un sigc::connection, el slot se obtiene como siempre con sigc::mem_fun, pero se declara del tipo bool, (sigc::slot<bool>), como segundo argumento hemos puesto 1000, que indica 1000 milisegundos (1 segundo). Es decir, que cada segundo se ejecutar la funcin crono::tiempo(). conexion=con;con es una variable local. Es decir, que una vez fuera de la funcin no podemos acceder a ella. Como crea una conexin que no se destruye al terminar la funcin, asignamos su valor a la variable conexion de la clase, para poder deshacer la misma si fuera necesario.

boton->set_sensitive(false); Inhabilita el botn. En crono::tiempo(): bool crono::tiempo(){Observamos que es una funcin booleana, esto es devuelve verdadero o falso. if (veces<=4){ //cdigo cronometro. } else{ boton->set_sensitive(); veces=1; return false; }Existe una estructura if que ve si el valor de veces es menor o igual a 4. Si no es as, es que el proceso se ha completado. Por lo que habilita el botn. Observa que no lleva argumentos la funcin, esto es as, porque es una funcin definida con valores por omisin, esto es, que si no introduces el argumento, la funcin asigna uno por defecto. En este caso, true, que activa el botn. Luego, vuelve a devolver a veces el valor de inicio y devuelve false, que provoca la destruccin del objeto sigc::connection, como el mtodo disconnect. if (segundos>1){ sprintf (h, "Sujeto %i-> %i:00 segundos",veces, --segundos); } else{ if(veces!=4)sprintf(h,"CAMBIO");else sprintf(h,"Pulsa abajo para comenzar la prueba"); segundos=45; veces++; } etiqueta->set_text(h); return true;Dentro del if anterior, nos encontramos con este if anidado (dentro del otro), donde si los segundos son mayores a 1 (de 45 a 2) se resta 1 segundo y se crea el mensaje (de 44 s. a 1). Si en vez de --segundos, hubiesemos escrito segundos--, primero se creara el mensaje y luego se realizara la resta (de 45 a 2). La primera vez que se ejecuta la funcin ya ha transcurrido 1 segundo, luego el primer mensaje a escribir 44 segundos. Si segundos es igual a 1, es que ya se ha realizado la cuenta, el prximo valor sera 0 segundos, pero ah nosotros debemos avisar que cambien, salvo que sea el ltimo, en cuyo caso debemos dejar la etiqueta como estaba (esto tambin poda hacerse junto a la habilitacin del botn. La funcin devuelve true, con ello, contina el timeout establecido.

Cronmetro Double-Dutch
Problema
De las cosas que ms me gusta del reloj, es que sigue funcionando an cuando estemos utilizando otra aplicacin... Claro, que si no lo ves convendra que avisar en los cambios. Bueno leyendo las instrucciones del reglamento, dice que se avisar a los 15 y 30 segundos, y en el cambio. Cmo gestionar el audio? Convendr aclarar que no soy un especialista en audio -lo cierto es que no lo soy en nada, pero en audio mis limitaciones son mayores-. Buscando por Internet encuentro que en C++, existen dos libreras: gstreamermm y ccaudio2. Como sta ltima se puede instalar con Synaptic procedemos a trabajar con la librera ccaudio2. La librera gstreamermm, no es oficial, aunque si lo es la famosa gstreamer (para programacin en C). Para este proyecto, he realizado una adaptacin de este cdigo.

Vamos a necesitar tres archivos de audio: cambio.wav, quince.wav y treinta.wav. En mi caso, he utilizado el programa audacity para editar los archivos de audio, que como observas en la imagen son archivos muy cortos (menos de medio segundo, en el ejemplo), lo cual es importante para el correcto funcionamiento de nuestro programa. El cual queda as: main.cc (sin cambios) //crono.h #ifndef _CRONO_COMBA_H #define _CRONO_COMBA_H #include <gtkmm.h> #include <libglademm/xml.h> #include <cc++/audio2.h> class crono:public Gtk::Window

{ public: crono(BaseObjectType* www, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade); virtual ~crono(); protected: int segundos; int veces; Glib::RefPtr<Gnome::Glade::Xml> refXml; Gtk::Button* boton; Gtk::Label* etiqueta; sigc::connection conexion; bool tiempo(); int play(const char* file); // seales virtual void pulsa_contador(); }; #endif

Recuerda que la librera ccaudio2 (libccaudio2) debe aadirse a nuestro proyecto. Y previamente, cargada, con Synaptic a nuestro sistema.
Bsicamente aadimos una funcin play(), que va a ser la encargada de hacer sonar el archivo de sonido correspondiente, el cual se pasa como argumento. Esta funcin no la he desarrollado en el archivo crono.cc, sino en otro archivo crono2.cc: //crono2.cc #include "cron.h" int crono::play(const char* file){ //declaracin de datos ost::AudioDevice *dev;//dispositivo de sonido ost::AudioStream archivo;//archivo de sonido ost::Audio::Linear buffer;//memoria para almacenar el sonido en formato audio ost::Audio::Info info;// datos para el archivo

unsigned tamanobuffer, bloques; // Tratando de seleccionar un dispositivo de audio dev = archivo.getDevice(); // comprobando la operacin if(!dev) { return -1; //tenemos un problema con el dispositivo de audio //quizs lo est utilizando otro programa. } // Tratando de abrir el archivo de audio archivo.open(file,ost::Audio::modeRead,10); if(!archivo.isOpen()||!archivo.isStreamable()) { delete dev; // en esta operacin si est creado dev, como nos vamos, hay que borrarlo return -2; //problemas con el archivo de audio, puede ser que no //est en el directorio, o que no tengamos los codecs apropiados } archivo.getInfo(&info);//obtener datos archivo //configurar el dispositivo con las caractersticas de nuestro archivo // en bloques de 10 milisegundos if(!dev->setAudio((ost::Audio::Rate)info.rate, ost::Audio::isStereo(info.encoding), 10)) { delete dev; return -3; //El rate (frecuencia muestreo) no es soportada por dispositivo sonido } // Calcular el tamao del buffer. tamanobuffer = archivo.getCount();// devuelve el nmero de bloques de audio if(ost::Audio::isStereo(info.encoding)) std::cout<<"Play devuelve "<<play("cambio.wav")<<std::endl; buffer = new ost::Audio::Sample[tamanobuffer * 2];//si el archivo es estreo necesito el doble else buffer = new ost::Audio::Sample[tamanobuffer]; for(;;)

// Convertir un bloque del archivo de audio en una muestra de audio legible por el dispositivo de audio if(ost::Audio::isStereo(info.encoding)) bloques = archivo.getStereo(buffer, 1); else bloques = archivo.getMono(buffer, 1); // Si no hay bloque, es que ya hemos terminado de leerlos todos. if(!bloques) break; // Colocar las muestras en el dispositivo de audio ("p" que suene) dev->putSamples(buffer, tamanobuffer);

} dev->sync();//sincronizar el dispositivo de audio para nuevos usos delete dev;// eliminar/borrar el objeto dev archivo.close(); //cerrar el fichero abierto return 0; }

El archivo crono2.cc, debe aadirse al proyecto, teniendo como objetivo el archivo ejecutable.
El archivo est comentado, pero tratar de explicarlo (tal y como yo lo entiendo):

Empecemos diciendo que en el disco duro almacenamos informacin muy diversa: audio, imgenes, texto,... Dentro de los archivos de audio, existen numerosos formatos (wav, mp3, ogg...), cada uno de ellos almacena la informacin de una manera diferente. Y dentro de un mismo formato, puede variar la informacin segn la calidad de la grabacin: frecuencia de muestreo, estreo o mono... Esta informacin no es la misma que recibe la tarjeta de sonido, la cual convierte la informacin digital del ordenador en seal analgica, que es lo que precisa el altavoz para funcionar. Es decir, que precisamos convertir la seal digital de nuestro archivo audio, a una seal digital legible por

nuestro dispositivo de audio. Por otra parte, el archivo de audio, correctamente convertido no se transmite en su totalidad, sino que se realiza en paquetes o muestras (samples) enviadas cada x tiempo. Esto, denominado Stream, nos permite por ejemplo escuchar o ver un video por Internet sin habernos descargado el archivo en su totalidad. La funcin selecciona un dispositivo de audio (si por ejemplo otro programa est utilizando el audio, nos resulta imposible seleccionarlo). Si disponemos de audio, podemos pasar a abrir el archivo. Podra ocurrir que el archivo enviado a la funcin no fuera de audio, o no existiera, o fuese de un formato no compatible, as que tras abrirlo, comprobamos que puede ser interpretado por la librera libccaudio2. La siguiente cuestin es obtener los datos del archivo (estreo o mono, la frecuencia de muestreo,...) para preparar adecuadamente el dispositivo de sonido y calcular el tamao del buffer (memoria), que es el instrumento utilizado para conectar el archivo y el dispositivo. En for(;;) se van convirtiendo los distintos bloques del archivo en informacin interpretable por la tarjeta de sonido, almacenndolos en el buffer (getStereo o getMono); y se van enviando del buffer a la tarjeta de sonido (putSamples). Como este for no tiene condiciones se ejecuta indefinidamente, para salir del mismo es necesario unbreak;, lo cual se alcanza cuando bloques es igual a 0, bloques obtiene el valor de la funcin getMono (o getStereo) que devuelven el nmero de bloques ledos, cuando se alcanza el final del archivo, bloques=0, y se sale del bucle for(;;). El archivo crono.cc: //cron.cc #include #include #include #include <gtkmm.h> <iostream> <stdlib.h> <libglademm/xml.h>

#include "cron.h" // DEFINICIN DE LOS MTODOS DE LA CLASE // constructor de la primera clase crono::crono(BaseObjectType* www, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade):Gtk::Window(www),refXml(refGlade) { segundos=0; veces=1; refXml->get_widget("boton", boton); boton->signal_clicked().connect(sigc::mem_fun(*this,&crono::pulsa_contador)); refXml->get_widget("etiqueta", etiqueta);

// destructor crono::~crono(){std::cout<<"Eso es todo amigo"<<std::endl; conexion.disconnect();} // funcin para manipular la seal void crono::pulsa_contador(){ sigc::connection con = Glib::signal_timeout().connect((sigc::slot<bool>)sigc::mem_fun(*this,&crono::tiempo),1000); conexion=con; char h[40]; sprintf(h,"Sujeto 1-> 00:00 segundos"); etiqueta->set_text(h); boton->set_sensitive(false); } bool crono::tiempo(){ char h[40]; if (veces<=4){ if (segundos<44){ sprintf (h, "Sujeto %i-> %i:00 segundos",veces, ++segundos); if(segundos==15)play("quince.wav"); if(segundos==30)play("treinta.wav"); } else{ if(veces!=4){ sprintf(h,"CAMBIO"); std::cout<<"Play devuelve "<<play("cambio.wav")<<std::endl; }else sprintf(h,"Pulsa abajo para comenzar la prueba"); segundos=0; veces++; } etiqueta->set_text(h); return true; } else{ boton->set_sensitive(); veces=1; return false;

} } En primer lugar, observa que ahora en vez de contar para atrs, cuenta para adelante 1->45. En relacin al sonido, observa que llamamos a la funcin play con el archivo que nos interesa, en funcin del momento. En el momento del cambio, tambin se ve como aprovecho la consola para obtener informacin de la funcin play, lo cual puede ser interesante para analizar el error cuando el programa no funcione adecuadamente. Por razones que desconozco, el cdigo de audio funciona perfectamente aisladamente, pero al reproducir ste en un "timeout", el sonido se escucha regular. Ahhh! y si el archivo de audio es muy largo, acontece que se requiere la utilizacin del objeto (functor, que no funcin) cuando ste, no ha sido liberado, dando problemas; los archivos de audio deben ser muy cortos para que una vez ejecutado no se altere el audio... habr que probar con gstreamer.

Potrebbero piacerti anche