Gtk - Fonction callback

Résolu/Fermé
bonzime Messages postés 39 Date d'inscription jeudi 14 août 2008 Statut Membre Dernière intervention 12 juin 2010 - 9 déc. 2008 à 10:12
 loupius - 10 déc. 2008 à 03:33
Bonjour,

Je suis en apprenti developpeur. J'ai quelques bases en langages C et je suis en train d'apprendre la librairie GTK+ .
Je suis le tutorial du site https://www.gtk-fr.org/ L'apprentissage s'est bien passé jusqu'à ce que j'arrive à la partie Liste chaânée et Saisie des données.
Je ne comprend pas très bien le fonctionnement des fonctions callback qui sont utilisée pour les signaux.

gulong g_signal_connect(gpointer *object, const gchar *name, GCallback func, gpointer
func_data );


Une fonction callback doit être de la forme
void nom_de_la_fonction(GtkWidget *widget, gpointer data)

Doit-elle être forcément sous cette forme? Peut-on intégrer d'autres type de variables et appeller simplement la fonction en intégrant les données nécessaires ?

Seulement je ne comprends pas très bien comment marche le gpointer data. Comment va t il chercher la donnée supplémentaire à l'exécution de la fonction ? Et comment spécifier cette donnée ?

Voici le code exemple proposé par le tuto de programme qui présente les données entrées par l'utilisateur.

#include <stdlib.h>
#include <gtk/gtk.h>
struct _MainWindow
{
GtkWidget *pWindow;
GtkWidget *pVBox;
GtkWidget *pEntry;
GtkWidget *pButton;
GtkWidget *pLabel;
};
typedef struct _MainWindow MainWindow;
void OnUpdate(GtkWidget *pEntry, gpointer data);
int main(int argc, char **argv)
{
MainWindow *pApp;
gtk_init(&argc, &argv);
pApp = g_malloc(sizeof(MainWindow));
pApp>
pWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(pApp>
pWindow), "Le widget GtkEntry");
gtk_window_set_default_size(GTK_WINDOW(pApp>
pWindow), 320, 200);
g_signal_connect(G_OBJECT(pApp>
pWindow), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
pApp>
pVBox = gtk_vbox_new(TRUE, 0);
gtk_container_add(GTK_CONTAINER(pApp>
pWindow), pApp>
pVBox);
/* Creation du GtkEntry */
pApp>
pEntry = gtk_entry_new();
/* Insertion du GtkEntry dans la GtkVBox */
gtk_box_pack_start(GTK_BOX(pApp>
pVBox), pApp>
pEntry, TRUE, FALSE, 0);
pApp>
pButton = gtk_button_new_with_label("Copier");
gtk_box_pack_start(GTK_BOX(pApp>
pVBox), pApp>
pButton, TRUE, FALSE, 0);
pApp>
pLabel = gtk_label_new(NULL);
gtk_box_pack_start(GTK_BOX(pApp>
pVBox), pApp>
pLabel, TRUE, FALSE, 0);
/* Connexion du signal "activate" du GtkEntry */
g_signal_connect(G_OBJECT(pApp>
pEntry), "activate", G_CALLBACK(OnUpdate),
(gpointer) pApp);
Page 43/161
Gtk-Fr Cours Gtk+
/* Connexion du signal "clicked" du GtkButton */
/* La donnee supplementaire est la GtkVBox pVBox */
g_signal_connect(G_OBJECT(pApp>
pButton), "clicked", G_CALLBACK(OnUpdate),
(gpointer*) pApp);
gtk_widget_show_all(pApp>
pWindow);
gtk_main();
g_free(pApp);
return EXIT_SUCCESS;
}
/* Fonction callback execute lors du signal "activate" */
void OnUpdate(GtkWidget *pEntry, gpointer data)
{
const gchar *sText;
MainWindow *pApp;
/* Recuperation de data */
pApp = (MainWindow*) data;
/* Recuperation du texte contenu dans le GtkEntry */
sText = gtk_entry_get_text(GTK_ENTRY(pApp>
pEntry));
/* Modification du texte contenu dans le GtkLabel */
gtk_label_set_text(GTK_LABEL(pApp>
pLabel), sText);
}



Voici également un autre code proposé par une ancienne version de ce tuto

#include <stdlib.h>
#include <gtk/gtk.h>

void on_activate_entry(GtkWidget *pEntry, gpointer data);
void on_copier_button(GtkWidget *pButton, gpointer data);

int main(int argc, char **argv)
{
GtkWidget *pWindow;
GtkWidget *pVBox;
GtkWidget *pEntry;
GtkWidget *pButton;
GtkWidget *pLabel;

gtk_init(&argc, &argv);

pWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(pWindow), "Le widget GtkEntry");
gtk_window_set_default_size(GTK_WINDOW(pWindow), 320, 200);
g_signal_connect(G_OBJECT(pWindow), "destroy", G_CALLBACK(gtk_main_quit), NULL);

pVBox = gtk_vbox_new(TRUE, 0);
gtk_container_add(GTK_CONTAINER(pWindow), pVBox);

/* Creation du GtkEntry */
pEntry = gtk_entry_new();
/* Insertion du GtkEntry dans la GtkVBox */
gtk_box_pack_start(GTK_BOX(pVBox), pEntry, TRUE, FALSE, 0);

pButton = gtk_button_new_with_label("Copier");
gtk_box_pack_start(GTK_BOX(pVBox), pButton, TRUE, FALSE, 0);

pLabel = gtk_label_new(NULL);
gtk_box_pack_start(GTK_BOX(pVBox), pLabel, TRUE, FALSE, 0);

/* Connexion du signal "activate" du GtkEntry */
g_signal_connect(G_OBJECT(pEntry), "activate", G_CALLBACK(on_activate_entry), (GtkWidget*) pLabel);

/* Connexion du signal "clicked" du GtkButton */
/* La donnee supplementaire est la GtkVBox pVBox */
g_signal_connect(G_OBJECT(pButton), "clicked", G_CALLBACK(on_copier_button), (GtkWidget*) pVBox);

gtk_widget_show_all(pWindow);

gtk_main();

return EXIT_SUCCESS;
}

/* Fonction callback execute lors du signal "activate" */
void on_activate_entry(GtkWidget *pEntry, gpointer data)
{
const gchar *sText;

/* Recuperation du texte contenu dans le GtkEntry */
sText = gtk_entry_get_text(GTK_ENTRY(pEntry));

/* Modification du texte contenu dans le GtkLabel */
gtk_label_set_text(GTK_LABEL((GtkWidget*)data), sText);
}

/* Fonction callback executee lors du signal "clicked" */
void on_copier_button(GtkWidget *pButton, gpointer data)
{
GtkWidget *pTempEntry;
GtkWidget *pTempLabel;
GList *pList;
const gchar *sText;

/* Recuperation de la liste des elements que contient la GtkVBox */
pList = gtk_container_get_children(GTK_CONTAINER((GtkWidget*)data));

/* Le premier element est le GtkEntry */
pTempEntry = GTK_WIDGET(pList->data);

/* Passage a l element suivant : le GtkButton */
pList = g_list_next(pList);

/* Passage a l element suivant : le GtkLabel */
pList = g_list_next(pList);

/* Cet element est le GtkLabel */
pTempLabel = GTK_WIDGET(pList->data);

/* Recuperation du texte contenu dans le GtkEntry */
sText = gtk_entry_get_text(GTK_ENTRY(pTempEntry));

/* Modification du texte contenu dans le GtkLabel */
gtk_label_set_text(GTK_LABEL(pTempLabel), sText);

/* Liberation de la memoire utilisee par la liste */
g_list_free(pList);
}

1 réponse

>> Une fonction callback doit être de la forme
>> void nom_de_la_fonction(GtkWidget *widget, gpointer data)
>>
>> Doit-elle être forcément sous cette forme? Peut-on intégrer d'autres type de variables et appeller simplement la fonction en intégrant les données nécessaires ?

On n'a pas le choix (sinon il aurait fallu créer une fonction g_signal_connect par type de variable), mais cela ne pose pas de problème puisque par un pointeur on peut passer n'importe quel type de données; de plus, si l'on a besoin de passer plusieurs variables, il suffit de passer une structure.

>> Seulement je ne comprends pas très bien comment marche le gpointer data. Comment va t il chercher la donnée supplémentaire à l'exécution de la fonction ? Et comment spécifier cette donnée ?

La réponse est dans la question:
int main(int argc, char **argv)
{
   MainWindow* pApp;
   ...
   /* Connexion du signal "activate" du GtkEntry */
   g_signal_connect (G_OBJECT(pApp->pEntry), "activate", G_CALLBACK(OnUpdate), (gpointer)pApp);
   /* par le cast, pApp est tranformé en type gpointer (c'est un peu l'équivalent d'un pointeur de type void)*/
   ...
}

/* Fonction callback exécutée lors du signal "activate" */
void OnUpdate(GtkWidget* pEntry, gpointer data)
{
   MainWindow* pApp;
   /* Récupération de data */
   pApp = (MainWindow*)data;
   /* par le cast, pApp est récupéré comme un pointeur de type MainWindow */
   ...
} 
1