Cronómetro Double-Dutch
¿Quééeeeeee?
A
partir de aquí, básicamente iré
añadiendo
programas que implican la utilización de determinados
controles
o aspectos. Voy a desarrollarlos progresivamente, poco a poco como buen
aficionado, aunque lo suyo es primero realizar un diseño
completo y luego proceder a su implementación, pero no es
ésta una página 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
número de saltos posible [Speed]. Cada deportista tiene 45
segundos [bueno, el primero dispone 45 s., el resto tiene 45 s., pero
en ellos están incluidos el tiempo del intercambio
de las posiciones.
Mi problema es que mi
reloj no me permite realizar cuenta atrás de menos de 1 minuto
[un secretillo, maestrodenada es -soy- un profesor de
educación física]. Podría cambiar de reloj,
o
aprovechar la ocasión para realizar un programilla
[afortunadamente, trabajo en un centro TIC y dispongo de ordenadores
con software libre, ¡qué lujazo!].
Pero, ¿cómo 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 vídeo,
que esperará a que terminarás de editar un archivo de
texto para enviar a la tarjeta de sonido la información del
audio... sería imposible escuchar música a la par que
escribíamos un texto.
Hay programas que requieren una realización ajustada al tiempo
real... es el caso de nuestro cronómetro. Cada x tiempo,
nuestro programa debe recuperar el control del ordenador.
¿control timer?
En otros lenguajes existe un
control timer (invisible) que uno
añade a sus ventanas, este control tiene asociada una
función, 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 añadir fácilmente a nuestra ventana.
Una vez conocido el procedimiento, resulta igualmente
fácil.Para controlar el tiempo, tenemos que crear un
objeto
sigc::connection (como las señales de
eventos), con la peculiaridad)que el
slot al que va asociado es una
función
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 función asociada. Esta
"conexión" lleva un segundo argumento que es el tiempo, en
milisegundos, en el que se suceden la ejecución de la
función... bla, bla, bla... Vayamos a la
práctica:
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 botón de nombre boton. En la etiqueta nos aparecerá el deportista que ha de saltar y
el tiempo restante. La prueba comenzará al pulsar el
botón (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 algún 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();
// señales
virtual void pulsa_contador();
};
#endif
En la declaración de la clase se observa el
objeto
sigc::connection (conexion) y la función
bool tiempo().
También hemos incorporado una variable entera
segundos, donde
vamos a controlar el número de segundos y la variable
veces
que utilizaremos para ver cuántas veces se ha realizado la
cuenta (el número 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();}
// función para manipular la señal
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 (duración de la prueba para cada deportista) y veces.
En el destructor: se llama al método
disconnect(), por si el programa termina precipitadamente y no concluye la cuenta atrás.
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 botón, 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 función crono::tiempo().
conexion=con;con es
una variable local. Es decir, que una vez fuera de la función no
podemos acceder a ella. Como crea una conexión que no se
destruye al terminar la función, asignamos su valor a la
variable
conexion de la clase, para poder deshacer la misma si fuera necesario.
boton->set_sensitive(false);
Inhabilita el botón.
En crono::tiempo():
bool crono::tiempo(){Observamos que es una función booleana, esto es devuelve verdadero o falso.
if (veces<=4){
//código 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 botón. Observa que no lleva argumentos la
función, esto es así, porque es una función
definida con valores por omisión, esto es, que si no introduces
el argumento, la función asigna uno por defecto. En este caso,
true, que activa el botón. Luego, vuelve a devolver a
veces el valor de inicio y devuelve
false, que provoca la destrucción del objeto sigc::connection, como el método
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 crearía el mensaje y luego se
realizaría la resta (de 45 a 2). La primera vez que se ejecuta
la función 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 próximo valor sería 0 segundos,
pero ahí nosotros debemos avisar que cambien, salvo que sea el
último, en cuyo caso debemos dejar la etiqueta como estaba (esto
también podía hacerse junto a la habilitación del
botón.
La función devuelve true, con ello, continúa el timeout establecido.