Conectar manejadores de señales

Las clases de widgets de gtkmm tienen métodos de acceso a señales, como Gtk::Button::signal_clicked(), que le permiten conectar su manejador de señales. Gracias a la flexibilidad de libsigc++, la biblioteca de retornos de llamada usada por gtkmm, el manejador de señales puede ser casi cualquier tipo de función, pero probablemente quiera usar un método de clase. Entre los programadores de C de GTK+, estos manejadores de señales a menudo se llaman retornos de llamada.

Aquí hay un ejemplo de un manejador de señales que se conecta a una señal:

#include <gtkmm/button.h>

void on_button_clicked()
{
    std::cout << "Hello World" << std::endl;
}

main()
{
    Gtk::Button button("Hello World");
    button.signal_clicked().connect(sigc::ptr_fun(&on_button_clicked));
}

Hay mucho para pensar en este código no funcional. Primero, identifique a los grupos involucrados:

  • El manejador de señales es on_button_clicked().
  • Se engancha al objeto Gtk::Button llamado button.
  • Cuando el botón emita su señal clicked, se llamará a on_button_clicked().

Ahora, mire la conexión nuevamente:

    ...
    button.signal_clicked().connect(sigc::ptr_fun(&on_button_clicked));
    ...

Tenga en cuenta que no se le pasa un puntero a on_button_clicked() directamente al método de la señal connect(). En su lugar, se llama a sigc::ptr_fun(), y se le pasa el resultado a connect().

sigc::ptr_fun() genera un sigc::slot. Un «slot» es un objeto que se comporta como una función, pero en realidad es un objeto. Estos también se llaman objetos función, o funtores. sigc::ptr_fun() genera un «slot» para una función independiente o un método estático. sigc::mem_fun() genera un «slot» para un método miembro de una instancia particular.

Aquí hay un ejemplo ligeramente más amplio de los «slots» en acción:

void on_button_clicked();

class some_class
{
    void on_button_clicked();
};

some_class some_object;

main()
{
    Gtk::Button button;
    button.signal_clicked().connect( sigc::ptr_fun(&on_button_clicked) );
    button.signal_clicked().connect( sigc::mem_fun(some_object, &some_class::on_button_clicked) );
}

La primera llamada a connect() es igual a la que vio la última vez; no hay nada nuevo aquí.

La siguiente es más interesante. sigc::mem_fun() se llama con dos argumentos. El primero es some_object, que es el objeto al que su nuevo «slot» apuntará. El segundo argumento es un puntero a uno de sus métodos. Esta versión particular de sigc::mem_fun() crea un «slot» que, cuando se «llame», llamará al método al que apunta del objeto especificado, en este caso, some_object.on_button_clicked().

Otra cosa notable acerca de este ejemplo es que se ha hecho la llamada a connect() dos veces para el mismo objeto de señal. Esto está perfectamente bien: cuando se pulse el botón, se llamará a ambos manejadores de señales.

Como se mencionó, la señal clicked del botón está esperando llamar a un método sin argumentos. Todas las señales tienen requerimientos como este: no puede enganchar una función con dos argumentos a una señal que no espera ninguno (a menos que use un adaptador, como sigc::bind(), por supuesto). Por lo tanto, es importante saber qué tipo de manejador de señales se esperará que conecte a una señal dada.