En el siguiente ejemplo, crearemos un objeto de la clase Sender (que será nuestra ventana principal)
[ sendermain.cpp ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #include <gtkmm.h> #include "sender.h" int main(int argc, char *argv[]) { Main entorno (argc, argv); // Creamos la ventana Sender swin; // Ejecutamos entorno.run(swin); return 0; } |
Ahora creamos el archivo cabecera sender.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #ifndef _HWORLD_H #define _HWORLD_H #include <gtkmm.h> using namespace Gtk; class Sender : public Window { public: Sender(); ~Sender(); void click(Widget *sender); HButtonBox botonera; Button *boton[10]; /* 10 botones */ }; #endif |
Vemos cómo hemos creado los elementos, sólo una HButtonBox y 10 botones, y sólo un método click() con el parámetro sender de tipo Widget, lo que hacemos es que, cuando llamamos a click() le debemos pasar un puntero indicando la referencia del objeto que hemos hecho click. Lo veremos más claro en sender.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | #include "sender.h" #include <sstream> // Para hacer texto<<string<<int Sender::Sender() { int i; std::stringstream texto; // Configuro la ventana set_title("Un método para gobernarlos a todos"); set_border_width(5); set_default_size(400, 200); // Configuro la botonera boton[0]=new Button(Stock::QUIT); botonera.pack_start(*boton[0], PACK_SHRINK); boton[0]->signal_clicked().connect(sigc::bind<Widget*>(sigc::mem_fun(*this, &Sender::click),(Widget*)boton[0])); for (i=1; i<10; i++) { texto.str(""); texto<<"Boton "<<i; boton[i]=new Button(texto.str().data()); boton[i]->signal_clicked().connect(sigc::bind<Widget*>(sigc::mem_fun(*this, &Sender::click), (Widget*)boton[i])); botonera.pack_start(*boton[i], PACK_SHRINK); } // Configuro la división add(botonera); show_all_children(); } Sender::~Sender() { int i; for (i=0; i<10; i++) delete boton[i]; } void Sender::click(Widget *sender) { int i, res; std::stringstream ss; // Comprobamos procedencia for (i=0; i<10; i++) { if (sender==boton[i]) break; } if (i==0) hide(); else { ss<<"Has pulsado el boton "<<i; MessageDialog dialog(ss.str().data()); res=dialog.run(); } } |
En este código vemos cómo cuando llamamos a .connect() añadimos lo siguiente:
- sigc::bind() enlazará una llamada a un método con un parámetro de tipo «Tipo»
- sigc::mem_fun() como vimos anteriormente generará la referencia de la llamada al método
El tipo del parámetro utilizado será Widget* para que se pueda crear un puntero (hacemos la comparación fácil y rápida) y además, este método sea portable a más tipos de objeto y no sólo botones (además, el método click() espera como parámetro un Widget*).
Por otra parte, en el método click vemos que con:
1 2 3 4 5 | for (i=0; i<10; i++) { if (sender==boton[i]) break; } |
Averiguamos de qué botón viene el click (comparando el sender con todos los posibles botones de procedencia, cuando lo encontramos, salimos del bucle. Si i vale 11 tal vez hayamos cometido un error, o tenemos un sender que no está monitorizado).
Luego si el sender es 0 (el botón de salir) ejecutamos una cosa, y si no mostramos en pantalla un texto.
¡Ok hasta aquí! Aunque queremos métodos para hacer esto más fácil, porque aprender la línea de sigc::bind(sigc::connect(etc,etc…) es trabajo duro, y además nos podemos equivocar fácilmente. Podemos utilizar la siguiente macro (que podemos colocar en sender.h):
1 | #define CLICK_CONNECT(widget, method) widget->signal_clicked().connect(sigc::bind<Widget*>(sigc::mem_fun(*this, &method),(Widget*)widget)) |
y por tanto, las conexiones con signal_click() quedarían así (el texto antiguo aparece comentado):
1 2 3 4 5 6 7 8 9 10 11 | // boton[0]->signal_clicked().connect(sigc::bind<Widget*>(sigc::mem_fun(*this, &Sender::click),(Widget*)boton[0])); CLICK_CONNECT(boton[0], Sender::click); for (i=1; i<10; i++) { texto.str(""); texto<<"Boton "<<i; boton[i]=new Button(texto.str().data()); // boton[i]->signal_clicked().connect(sigc::bind<Widget*>(sigc::mem_fun(*this, &Sender::click), (Widget*)boton[i])); CLICK_CONNECT(boton[i], Sender::click); botonera.pack_start(*boton[i], PACK_SHRINK); } |
Aunque gtkmm es muy grande y tiene muchas posibilidades, siempre podemos completar con nuestro código para facilitarnos la vida.
Para compilar este proyecto, podemos hacer lo siguiente:
$ g++ -o sender sendermain.cpp sender.cpp `pkg-config –cflags gtkmm-2.4` `pkg-config –libs gtkmm-2.4`