Les boutons (partie 2)
Nous allons étudier cette fois-ci trois nouveaux types de boutons qui
dérivent du widget GtkButton (chapitre V). L'étude de ces widgets
sera rapide car ils ne comportent que très peu de fonctions.
1. Le widget GtkToggleButton
Il s'agit ici d'un bouton poussoir qui ne peut prendre que deux états
: enfoncé ou relâché. Ce widget dérive de GtkButton.

1.1 Création du bouton
Vous allez voir ici, il n'y a rien de bien compliqué vu que c'est toujours
le même principe :
GtkWidget* gtk_toggle_button_new(void);
GtkWidget* gtk_toggle_button_new_with_label(const gchar* label);
GtkWidget* gtk_toggle_button_new_with_mnemonics(const gchar* label);
La première fonction crée un nouveau bouton vide, alors que la
seconde ajoute du texte à l'intérieur et la troisième ajoute
en plus un raccourci clavier.
1.2 Etats du bouton
Il peut être intéressant de connaître l'état du bouton
pour agir en conséquence. Une fois encore, rien de plus simple on utilise
la fonction :
gboolean gtk_toggle_button_get_active (GtkToggleButton *toggle_button);
Cette dernière nous renvoie TRUE si le bouton est enfoncé
et FALSE sinon. Afin de pouvoir utiliser le paramètre toggle_button
qui est le bouton dont on veut connaître l'état, il faut utiliser
la macro GTK_TOGGLE_BUTTON().
Pour modifier l'état du bouton, c'est aussi simple :
void gtk_toggle_button_set_active (GtkToggleButton *toggle_button,
gboolean is_active);
Il suffit de mettre le paramètre is_active à TRUE
si l'on veut enfoncer le bouton ou à FALSE pour le relâcher.
Il existe cependant un troisième état qui n'est pas accessible
en cliquant sur le bouton mais par une fonction spécifique. Ce troisième
état, vous le connaissez sûrement. Le meilleur exemple est celui
des éditeurs de texte :
Vous avez une phrase dans laquelle il y a du texte normal et du texte en gras.
Si vous sélectionnez le texte en gras, le bouton permettant justement
de le mettre en gras s'enfonce (en général B). Par contre si vous
sélectionnez le texte normal, ce même bouton repasse à son
état relâché. Venons en au troisième état,
qui apparaît lorsque vous sélectionnez la phrase entière.
Le bouton ne change pas d'état mais seulement d'aspect, il donne l'impression
d'être inactif.
Ce changement d'aspect doit se faire manuellement, et s'effectue avec cette
fonction :
void gtk_toggle_button_set_inconsistent(GtkToggleButton *toggle_button,
gboolean setting);
Il suffit de mettre le paramètre setting à TRUE
pour donner au bouton l'aspect inactif.
Et pour connaître l'aspect du bouton, il y a tout naturellement la fonction
:
gboolean gtk_toggle_button_get_inconsistent(GtkToggleButton
*toggle_button);
Evidemment, toutes les fonctions du widget GtkButton sont utilisables
avec ce type de bouton.
1.3 Exemple
Nous allons construire une application contenant un GtkToggleButton
qui changera d'état (bien sûr) lorsque nous cliquerons dessus,
mais aussi lorsque nous cliquerons sur un deuxième bouton (le changement
d'état entraînera la modification du label). De plus, il y aura
un troisième bouton pour changer l'aspect du bouton (idem, on change
aussi le label).
La création des widgets étant classique, nous n'allons donc pas
revenir dessus.
Il va donc nous falloir trois fonctions callback. La première pour changer
le texte lorsque l'on clique sur le GtkToggleButton, la deuxième
pour changer son état lorsque l'on clique sur le deuxième bouton,
et la troisième pour changer l'aspect.
Pour la première fonction callback, il faut intercepter le signal "toggled"
qui est émis lorsque le bouton change d'état :
g_signal_connect(G_OBJECT(pToggleBtn), "toggled",
G_CALLBACK(OnToggle), NULL);
Dans cette fonction nous allons récupérer l'état du bouton
ainsi que son aspect afin de changer le label en fonction des valeurs de retour.
Pour la deuxième, on interceptera le signal "clicked"
:
g_signal_connect(G_OBJECT(pEtatBtn), "clicked",
G_CALLBACK(OnEtatBtn), pToggleBtn);
Lorsque l'on changera l'état du bouton toggle, le signal "toggled"
sera émis pour celui-ci et le texte se changera automatiquement.
Pour la troisième, le signal à intercepter et le même ("clicked")
:
g_signal_connect(G_OBJECT(pAspectBtn), "clicked",
G_CALLBACK(OnAspectBtn), pToggleBtn);
Cette fois, lorsque l'on change l'aspect du bouton, le signal "toggled"
n'est pas émis et le texte ne changera donc pas. Il faut donc émettre
soit même ce signal avec la fonction :
void gtk_toggle_button_toggled (GtkToggleButton *toggle_button);
Cette fonction ne change en aucun cas l'état du bouton, elle ne fait
qu'émettre le signal.
Mais le plus simple est de regarder le code source suivant.
1.4 Programme exemple
#include <stdlib.h>
#include <gtk/gtk.h> void
OnToggle(GtkWidget *pToggle, gpointer data);
void OnEtatBtn(GtkWidget *pWidget, gpointer
pToggle);
void OnAspectBtn(GtkWidget *pWidget, gpointer
pToggle);
int main(int
argc, char **argv)
{
GtkWidget *pWindow;
GtkWidget *pToggleBtn;
GtkWidget *pEtatBtn;
GtkWidget *pAspectBtn;
GtkWidget *pVBox;
gchar *sLabel;
gtk_init(&argc,&argv);
pWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(pWindow), "GtkToggleButton");
gtk_window_set_default_size(GTK_WINDOW(pWindow),
320, 200);
pVBox = gtk_vbox_new(TRUE, 0);
gtk_container_add(GTK_CONTAINER(pWindow), pVBox);
/* Creation du label du
bouton */
sLabel = g_locale_to_utf8("Etat : Relâché
- Aspect : Normal", -1, NULL, NULL, NULL);
/* Creation du bouton GtkToggleButton
*/
pToggleBtn = gtk_toggle_button_new_with_label(sLabel);
/* Le label sLabel n'est
plus utile */
g_free(sLabel);
gtk_box_pack_start(GTK_BOX(pVBox), pToggleBtn,
FALSE, FALSE, 0);
pEtatBtn = gtk_button_new_with_label("CHANGER
ETAT");
gtk_box_pack_start(GTK_BOX(pVBox), pEtatBtn, FALSE,
FALSE, 0);
pAspectBtn = gtk_button_new_with_label("CHANGER
ASPECT");
gtk_box_pack_start(GTK_BOX(pVBox), pAspectBtn,
FALSE, FALSE, 0);
gtk_widget_show_all(pWindow);
/* Connexion des signaux
*/
g_signal_connect(G_OBJECT(pWindow), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(G_OBJECT(pToggleBtn), "toggled",
G_CALLBACK(OnToggle), NULL);
g_signal_connect(G_OBJECT(pEtatBtn), "clicked",
G_CALLBACK(OnEtatBtn), pToggleBtn);
g_signal_connect(G_OBJECT(pAspectBtn), "clicked",
G_CALLBACK(OnAspectBtn), pToggleBtn);
gtk_main();
return EXIT_SUCCESS;
}
void OnEtatBtn(GtkWidget *pWidget, gpointer
pToggle)
{
gboolean bEtat;
/* Recuperation de l etat
du bouton */
bEtat = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pToggle));
/* Modification de l etat
du bouton */
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pToggle),
(bEtat ^ TRUE));
}
void OnAspectBtn(GtkWidget *pEtatBtn, gpointer
pToggle)
{
gboolean bInconsistent;
/* Recuperation de l aspect
du bouton */
bInconsistent = gtk_toggle_button_get_inconsistent(GTK_TOGGLE_BUTTON(pToggle));
/* Modification de l aspect
du bouton */
gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(pToggle),
(bInconsistent ^ TRUE));
/* On emet le signal "toggle"
pour changer le texte du bouton */
gtk_toggle_button_toggled(GTK_TOGGLE_BUTTON(pToggle));
}
void OnToggle(GtkWidget *pToggle, gpointer
data)
{
gboolean bEtat;
gboolean bInconsistent;
gchar *sLabel;
gchar *sLabelUtf8;
/* Recuperation de l etat
du bouton */
bEtat = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pToggle));
/* Recuperation de l aspect
du bouton */
bInconsistent = gtk_toggle_button_get_inconsistent(GTK_TOGGLE_BUTTON(pToggle));
/* Construction du label
du bouton */
sLabel = g_strdup_printf("Etat : %s - Aspect
: %s",
bEtat ? "Enfoncé"
: "Relâché",
bInconsistent ? "Modifié"
: "Normal");
/* Encodage du label en
UTF8 */
sLabelUtf8 = g_locale_to_utf8(sLabel, -1, NULL,
NULL, NULL);
/* Modification du label
du bouton */
gtk_button_set_label(GTK_BUTTON(pToggle), sLabelUtf8);
/* Les chaines sLabel et
sLabelUtf8 n'ont plus d'utilite */
g_free(sLabel);
g_free(sLabelUtf8);
} |
| Résultat :



|
2. Le widget GtkCheckButton
Sous ce nom se cachent tout simplement les cases à cocher que tout le
monde connaît. Il s'agit là aussi d'un type de bouton binaire,
d'ailleurs ce widget dérive de GtkToggleButton :

2.1 Création d'un GtkCheckButton
Une fois encore, la syntaxe et l'utilisation des fonctions de création
restent classiques :
GtkWidget* gtk_check_button_new (void);
GtkWidget* gtk_check_button_new_with_label (const gchar *label);
GtkWidget* gtk_check_button_new_with_mnemonic(const gchar *label);
2.2 Utilisation d'un GtkCheckButton
Il n'existe pas d'autres fonctions pour ce widget, il faut donc utiliser les
fonctions des widgets GtkToggleButton et GtkButton
pour récupérer les propriétés du widget (état,
aspect, label, ...).
Nous n'allons pas ici créer de programme exemple pour ce widget car
il suffit juste de remplacer gtk_toggle_button_new_with_label par
gtk_check_button_new_with_label dans l'exemple précédent
pour obtenir ceci :



3. Le widget GtkRadioButton
Nous passons maintenant au widget GtkRadioButton qui se différencie
par la possibilité d'en grouper plusieurs. De ce fait, lors que par exemple
nous avons un groupe de trois boutons, il n'y en a qu'un seul qui peut être
actif. On pourrait très bien faire cela avec le widget GtkCheckButton
mais cela serait beaucoup plus long à programmer. Dans la hiérarchie
des widgets, GtkRadioButton dérive de GtkCheckButton.

3.1 Création d'un groupe de GtkRadioButton.
Afin de grouper les boutons radio, GTK+ utilise les listes simplement chaînées
de GLib. Pour créer le premier bouton radio du groupe, il faut obligatoirement
passer par une de ces fonctions :
GtkWidget* gtk_radio_button_new (GSList *group);
GtkWidget* gtk_radio_button_new_with_label (GSList *group, const gchar *label);
GtkWidget* gtk_radio_button_new_with_mnemonic(GSList *group, const gchar *label);
Au moment de la création, le bouton radio est automatiquement ajouté
à la liste group. Cependant, ce paramètre n'est pas
obligatoire. Nous pouvons très bien mettre NULL comme valeur
et cela marchera de la même manière, sauf que nous n'aurons pas
de pointeur sur la liste. La valeur de retour de cette fonction sera aussi copiée
dans la variable data de la liste chaînée.
Ensuite pour rajouter les autres boutons au groupe, il y a plusieurs possibilités.
La première est d'utiliser une des trois fonctions précédentes
mais ce n'est pas tout, car autant pour le premier bouton du groupe, il n'est
pas nécessaire d'avoir une liste, autant pour les autres boutons cela
devient obligatoire. Pour cela, GTK+ nous fournit une fonction qui permet d'obtenir
la liste dans laquelle les boutons du groupe sont ajoutés :
GSList* gtk_radio_button_get_group(GtkRadioButton *radio_button);
Avec cette fonction, nous pouvons donc connaître la liste à laquelle
appartient le bouton radio_button, ce qui va nous permettre d'ajouter de nouveau
bouton au groupe. Mais là où cela se complique, c'est qu'il faut
récupérer la liste avant chaque ajout de bouton avec le dernier
bouton ajouté comme paramètre. Voici ce que cela donnerai pour
un groupe de trois boutons :
pRadio1 = gtk_radio_button_new_with_label(NULL, "Radio
1");
pGroup = gtk_radio_button_get_group(GTK_RADIO_BUTTON(pRadio1));
pRadio2 = gtk_radio_button_new_with_label(pGroup, "Radio 2");
pGroup = gtk_radio_button_get_group(GTK_RADIO_BUTTON(pRadio2));
pRadio3 = gtk_radio_button_new_with_label(pGroup, "Radio 3");
Ce système peut donc s'avérer lourd lors de la création
du groupe, surtout s'il contient un grand nombre de boutons. Heureusement, les
concepteurs de GTK+ ont pensé à nous simplifier la vie en ajoutant
ces trois fonctions :
GtkWidget* gtk_radio_button_new_from_widget(GtkRadioButton
*group);
GtkWidget* gtk_radio_button_new_with_label_from_widget(GtkRadioButton *group,
const gchar *label);
GtkWidget* gtk_radio_button_new_with_mnemonic_from_widget(GtkRadioButton *group,
const gchar *label);
Cette fois group ne correspond pas à la liste mais à un des boutons
du groupe. A chaque appel d'une de ces fonctions GTK+ va s'occuper de récupérer
correctement la liste à laquelle appartient le bouton group et ajouter
le nouveau bouton. Cela aura pour conséquence, dans le cas d'un groupe
de trois boutons, de réduire le nombre de lignes de code de 5 à
3 (et oui, une ligne de code c'est une ligne de code).
Nous savons maintenant tout ce qu'il faut pour créer un groupe de GtkRadioButton.
3.2 Exemple
Le programme exemple se présente sous la forme d'un mini sondage à
trois choix :
- Pour ;
- Contre ;
- Sans opinion.
La possibilité de choisir parmi une de ces valeurs est rendue possible
par l'utilisation des GtkRadioButton. Un autre bouton (tout ce qu'il y a de
plus normal) permet de valider le choix fait par l'utilisateur ainsi que d'afficher
le résultat dans une boîte de dialogue.
3.3 Programme exemple
#include <stdlib.h>
#include <gtk/gtk.h> void
OnValider(GtkWidget *pBtn, gpointer data);
int main(int
argc, char **argv)
{
GtkWidget *pWindow;
GtkWidget *pVBox;
GtkWidget *pRadio1;
GtkWidget *pRadio2;
GtkWidget *pRadio3;
GtkWidget *pValider;
GtkWidget *pLabel;
gtk_init(&argc, &argv);
pWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(pWindow), "GtkRadioButton");
gtk_window_set_default_size(GTK_WINDOW(pWindow),
320, 200);
pVBox = gtk_vbox_new(TRUE, 0);
gtk_container_add(GTK_CONTAINER(pWindow),pVBox);
pLabel = gtk_label_new("Votre choix :");
gtk_box_pack_start(GTK_BOX(pVBox), pLabel, FALSE,
FALSE, 0);
/* Creation du premier
bouton radio */
pRadio1 = gtk_radio_button_new_with_label(NULL,
"Pour");
gtk_box_pack_start(GTK_BOX (pVBox), pRadio1, FALSE,
FALSE, 0);
/* Ajout du deuxieme */
pRadio2 = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON
(pRadio1), "Contre");
gtk_box_pack_start(GTK_BOX (pVBox), pRadio2, FALSE,
FALSE, 0);
/* Ajout du troisieme */
pRadio3 = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON
(pRadio1), "Sans opinion");
gtk_box_pack_start(GTK_BOX (pVBox), pRadio3, FALSE,
FALSE, 0);
pValider = gtk_button_new_from_stock(GTK_STOCK_OK);
gtk_box_pack_start(GTK_BOX (pVBox), pValider,
FALSE, FALSE, 0);
gtk_widget_show_all(pWindow);
/* Connexion des signaux
*/
g_signal_connect(G_OBJECT(pWindow), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(G_OBJECT(pValider), "clicked",
G_CALLBACK(OnValider), pRadio1);
gtk_main();
return EXIT_SUCCESS;
}
void OnValider(GtkWidget *pBtn, gpointer
data)
{
GtkWidget *pInfo;
GtkWidget *pWindow;
GSList *pList;
const gchar *sLabel;
/* Recuperation de la liste
des boutons */
pList = gtk_radio_button_get_group(GTK_RADIO_BUTTON(data));
/* Parcours de la liste
*/
while(pList)
{
/*
Le bouton est il selectionne */
if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pList->data)))
{
/*
OUI -> on copie le label du bouton */
sLabel
= gtk_button_get_label(GTK_BUTTON(pList->data));
/*
On met la liste a NULL pour sortir de la boucle */
pList
= NULL;
}
else
{
/*
NON -> on passe au bouton suivant */
pList
= g_slist_next(pList);
}
}
/* On recupere la fenetre
principale */
/*
/* A partir d'un widget, gtk_widget_get_toplevel
/* remonte jusqu'a la fenetre mere qui nous est
/* utile pour l'affichage de la boite de dialogue
*/
pWindow = gtk_widget_get_toplevel(GTK_WIDGET(data));
pInfo = gtk_message_dialog_new (GTK_WINDOW(pWindow),
GTK_DIALOG_MODAL,
GTK_MESSAGE_INFO,
GTK_BUTTONS_OK,
"Vous avez choisi
: %s", sLabel);
gtk_dialog_run(GTK_DIALOG(pInfo));
gtk_widget_destroy(pInfo);
} |
| Résultat :


|
4. En savoir plus
4.1 Fonctions documentées
GtkToggleButton
| void gtk_toggle_button_set_mode (GtkToggleButton *toggle_button,
gboolean draw_indicator); |
Définit si le bouton est visible ou pas. Entrée(s)
: toggle_button : le bouton. draw_indicator : TRUE
pour visible, FALSE pour invisible. Sortie : rien |
| gboolean gtk_toggle_button_get_mode (GtkToggleButton *toggle_button); |
Pour savoir si le bouton est visible ou pas. Entrée(s)
: toggle_button : le bouton. Sortie : TRUE
si le bouton est visible, FALSE sinon. |
GtkRadioButton
| void gtk_radio_button_set_group (GtkRadioButton *radio_button,
GSList *group); |
Pour changer un bouton radio de groupe. Entrée(s)
: toggle_button : le bouton. group : le nouveau groupe
du bouton. Sortie : rien |
Date de mise à jour : 18 septembre 2002