Connexion aux gestionnaires de signal

Les classes d'éléments graphiques gtkmm ont des fonctions membres d'accès aux signaux, comme Gtk::Button::signal_clicked(), qui permettent de se connecter au gestionnaire de signal. Grâce à la souplesse de libsigc++, la bibliothèque utilisée par gtkmm, le gestionnaire de signal peut être n'importe quel type de fonction, mais vous souhaiterez probablement utiliser une fonction membre de la classe. Les programmeurs C de GTK+ nomment souvent ces gestionnaires de signal « fonctions de rappel » (callback).

Voici un exemple de gestionnaire de signal connecté à un signal :

#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));
}

Il y a un certain nombre de choses à considérer à propos de ce code (non fonctionnel). D'abord identifions les parties concernées :

  • Le gestionnaire de signal est on_button_clicked().
  • Nous le raccrochons à l'objet Gtk::Button nommé button.
  • Quand le bouton émet le signal clicked, on_button_clicked() est appelé.

Maintenant ré-examinons la connexion :

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

Notez que nous ne passons pas directement un pointeur sur on_button_clicked() à la fonction membre connect() du signal. À la place, nous faisons appel à sigc::ptr_fun() et passons le résultat à connect().

sigc::ptr_fun() génère un objet sigc::slot. Un connecteur est un objet qui ressemble et réagit comme une fonction, mais qui, en réalité, est bien un objet. Il est également connu sous le nom d'objet fonction ou « functor ». sigc::ptr_fun() génère un connecteur pour une fonction autonome ou une fonction membre statique. sigc::mem_fun() génère un connecteur pour une fonction membre d'une instance donnée.

Voici un exemple un peu plus développé de connecteurs en action :

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) );
}

Le premier appel à connect() est identique à celui que nous avons vu la fois précédente ; rien de plus à dire.

Le suivant est plus intéressant. sigc::mem_fun() est appelé avec deux paramètres. Le premier paramètre est some_object, qui est l'objet sur lequel notre nouveau connecteur pointe. Le second paramètre est un pointeur sur une de ses fonctions membres. Cette version particulière de sigc::mem_fun() crée un connecteur qui, quand il sera « appelé », fera lui-même appel à la fonction membre pointée de l'objet désigné, dans ce cas some_object.on_button_clicked().

Autre chose à noter dans cet exemple : nous appelons connect() deux fois pour le même objet signal. Cela est parfaitement correct — quand le bouton est cliqué, les deux gestionnaires de signal sont appelés.

Nous venons de vous dire que le signal clicked d'un bouton s'attend à appeler une fonction membre sans paramètre d'entrée. Tous les signaux ont des exigences de ce type — vous ne pouvez pas accrocher une fonction avec deux paramètres à un signal n'en attendant aucun (sauf si vous utilisez un adaptateur, tels que sigc::bind(), bien entendu). C'est pourquoi il est important de savoir quel type de gestionnaire de signal vous devez connecter à un signal donné.