Hello la team Gtk,

habituellement je viens sur ce forum pour poser une question suite à un problème.

Cette fois c’est pour faire un petit cadeau.

Le code est livré plus loin.

Dans le programme gtk3-demo il y a certaine petite merveille il faut le dire.

Notamment la démo du spinbutton m’a particulièrement attiré pour la possibilité offerte de gérer un spinbutton avec des valeurs discrètes (celle d’un tableau ou d’une liste).

J’ai reconstitué du code «*pur*» Gtk sans passer par du xml et Glade. Les neurones ont un peu chauffé pour mettre les mains dans le cambouis*!!!!!!!.

Résultat:
un callback standardisé spin_button_input. Un tel callback permet par exemple d'actualiser la valeur numérique interne du SpinButton à partir de l'entry interne du spinbutton

* l'entrée est gtk_entry_get_text (GTK_ENTRY (spinbutton)
* la sortie est new_val

Pour l’appeler on capture le signal input
Au passage en l'état je ne sais pas traiter l'erreur en return GTK_INPUT_ERROR. Mais je n’en ai pas besoin.

Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
static gint spin_button_input (GtkSpinButton *spinbutton, gdouble * new_val, gpointer data)
{
	/** peu documenté un tel callback permet par exemple d'actualiser 
         * la valeur numérique interne du SpinButton (pointé avec new_val) à partir de l'entry interne du spinbutton
         * 
         * l'entrée est gtk_entry_get_text (GTK_ENTRY (spinbutton)
         * la sortie est new_val
         * 
         * en l'état je ne sais pas traiter l'erreur en return GTK_INPUT_ERROR 
         **/
	gint index;
	gchar *candidate_char, *wanted_char;
	gboolean found = FALSE;
 
	tab_t * array = (tab_t *) data;
 
	for (index = 1; index <= array->len; index++)  
    {
		candidate_char = g_ascii_strup (array->value_tab[index - 1], -1);
		/** comme le spinbutton a un gtk_entry qui a été chargé avec une valeur de texte
                * on peut le comparer a une valeur dans un tableau. A partir du rang trouvé **/
		wanted_char = g_ascii_strup (gtk_entry_get_text (GTK_ENTRY (spinbutton)), -1);
		if (strstr (candidate_char, wanted_char) == candidate_char)
		{
			g_print(" on cherchait %s et on l'a trouvé\n",wanted_char);
			found = TRUE;
		}
		g_free (wanted_char);
		g_free (candidate_char);
		if (found)
			break;
    }
	if (!found)
    {
		*new_val = 0.0;
		return GTK_INPUT_ERROR;
    }
    // actualisation ici
	*new_val = (gdouble) index;
	g_print( " la valeur de new_val est %d\n",index);
	return TRUE;
}
le callback suivant est le contraire du précédent spin_button_output

ce callback actualise une valeur de texte du gtkentry d'un spinbutton

* la donnée d'entrée numérique est celle de l' adjustement du spinbutton
* la sortie est l'actualisation du Gtkentry avec une valeur du tableau

Pour l’appeler on capture le signal output

Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
static gint spin_button_output (GtkSpinButton *spin_button, gpointer * data)
{
	/** ce callback actualise une valeur de texte du gtkentry
         * d'un spinbutton 
         * 
         * la donnée d'entrée numérique est celle de l' adjustement du spinbutton 
         * la sortie est l'actualisation du Gtkentry avec une valeur du tableau
         * **/
	GtkAdjustment *adjustment;
	gdouble value;
	gint i;
 
	tab_t * array = (tab_t *) data;
 
	adjustment = gtk_spin_button_get_adjustment (spin_button);
	value = gtk_adjustment_get_value (adjustment);
	g_print( " la valeur de value du spinbutton  est %d\n", (gint) value);	
 
	for (i = 1; i <= array->len; i++)
    if (fabs (value - (double)i) < 1e-5)
	{
		if (strcmp (array->value_tab[i-1], gtk_entry_get_text (GTK_ENTRY (spin_button))))
			gtk_entry_set_text (GTK_ENTRY (spin_button), array->value_tab[i-1]);
	}
	return TRUE;
}
la cerise sur le gateau de la démo des développeurs est de fournir une commande Gobject pour synchroniser un widget avec la valeur d’un autre en 2 coups de cuillère à pot.

Voici un code complet en tout modestie. D’ailleurs je critique déjà le code qui utilise un tableau dont la taille doit être limitée. Une première amélioration pour un code plus robuste et propre serait d’utiliser une GList au lieu d’un tableau.

Le code en totalité avec la commande pour le compiler pour ceux qui découvrent Gtk en version 3.

Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <math.h>
#include <stdlib.h>
 
//test_spine_discret_test.c
/*  Compile with:
 * 
 *  gcc -std=c11 -Wall -fmax-errors=10 -Wextra test_spine_discret_test.c -o test_spine_discret_test `pkg-config --cflags --libs gtk+-3.0`
 */
 
typedef struct
	{
	/* ----- longueur du tableau  ---------- */
	gint len;
	/* ----- widget value --------- */
	gchar *value_tab[50]; // obligé de mettre une limite ?
	} tab_t;
 
void cb_quit (GtkWidget *widget, gpointer user_data)
{
	gtk_main_quit();
	/* parametres inutilises */
	(void)widget;
	(void)user_data;
}
 
static gint spin_button_input (GtkSpinButton *spinbutton, gdouble * new_val, gpointer data)
{
	/** peu documenté un tel callback permet par exemple d'actualiser 
         * la valeur numérique interne du SpinButton (pointé avec new_val) à partir de l'entry interne du spinbutton
         * 
         * l'entrée est gtk_entry_get_text (GTK_ENTRY (spinbutton)
         * la sortie est new_val
         * 
         * en l'état je ne sais pas traiter l'erreur en return GTK_INPUT_ERROR 
         **/
	gint index;
	gchar *candidate_char, *wanted_char;
	gboolean found = FALSE;
 
	tab_t * array = (tab_t *) data;
 
	for (index = 1; index <= array->len; index++)  
    {
		candidate_char = g_ascii_strup (array->value_tab[index - 1], -1);
		/** comme le spinbutton a un gtk_entry qui a été chargé avec une valeur de texte
                * on peut le comparer a une valeur dans un tableau. A partir du rang trouvé **/
		wanted_char = g_ascii_strup (gtk_entry_get_text (GTK_ENTRY (spinbutton)), -1);
		if (strstr (candidate_char, wanted_char) == candidate_char)
		{
			g_print(" on cherchait %s et on l'a trouvé\n",wanted_char);
			found = TRUE;
		}
		g_free (wanted_char);
		g_free (candidate_char);
		if (found)
			break;
    }
	if (!found)
    {
		*new_val = 0.0;
		return GTK_INPUT_ERROR;
    }
    // actualisation ici
	*new_val = (gdouble) index;
	g_print( " la valeur de new_val est %d\n",index);
	return TRUE;
}
 
static gint spin_button_output (GtkSpinButton *spin_button, gpointer * data)
{
	/** ce callback actualise une valeur de texte du gtkentry
         * d'un spinbutton 
         * 
         * la donnée d'entrée numérique est celle de l' adjustement du spinbutton 
         * la sortie est l'actualisation du Gtkentry avec une valeur du tableau
         * **/
	GtkAdjustment *adjustment;
	gdouble value;
	gint i;
 
	tab_t * array = (tab_t *) data;
 
	adjustment = gtk_spin_button_get_adjustment (spin_button);
	value = gtk_adjustment_get_value (adjustment);
	g_print( " la valeur de value du spinbutton  est %d\n", (gint) value);	
 
	for (i = 1; i <= array->len; i++)
    if (fabs (value - (double)i) < 1e-5)
	{
		if (strcmp (array->value_tab[i-1], gtk_entry_get_text (GTK_ENTRY (spin_button))))
			gtk_entry_set_text (GTK_ENTRY (spin_button), array->value_tab[i-1]);
	}
	return TRUE;
}
 
static gboolean value_to_label (GBinding     *binding,
                const GValue *from,
                GValue       *to,
                gpointer      user_data)
{
	/** synchronise le label à partir de l'adjustement **/
	g_value_take_string (to, g_strdup_printf ("%g", g_value_get_double (from)));
	return TRUE;
	/* Parametre inutilise */
	(void)binding;	
	(void)user_data;	
}
 
static void do_spinbutton (GtkApplication *app)
{
	tab_t * month_array = g_slice_new (tab_t);
 
	month_array->len =12;
	month_array->value_tab[0] = "January";
	month_array->value_tab[1] = "February";
	month_array->value_tab[2] = "March";
	month_array->value_tab[3] = "April";
	month_array->value_tab[4] = "May";
	month_array->value_tab[5] = "June";
	month_array->value_tab[6] = "July";
	month_array->value_tab[7] = "August";
	month_array->value_tab[8] = "September";
	month_array->value_tab[9] = "October";	
	month_array->value_tab[10] = "November";
	month_array->value_tab[11] = "December";
 
	tab_t * day_array = g_slice_new (tab_t);
 
	day_array->len =7;
	day_array->value_tab[0] = "lundi";
	day_array->value_tab[1] = "mardi";
	day_array->value_tab[2] ="mercredi";
	day_array->value_tab[3] ="jeudi";
	day_array->value_tab[4] = "vendredi";
	day_array->value_tab[5] ="samedi";
	day_array->value_tab[6] ="dimanche";
 
  	/* Create a window with a title, and a default size */
	GtkWidget *window = gtk_application_window_new (app);
 
	/* Creation du conteneur principal */
	GtkWidget *main_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
	GtkWidget *vertical_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
	gtk_container_add (GTK_CONTAINER (window), vertical_box);
 
	/* taille minimum */
	gtk_window_set_default_size  (GTK_WINDOW (window), 500, -1);
 
	/*création d'une bordure interne à la fenêtre window*/ 
	gtk_container_set_border_width (GTK_CONTAINER (window), 10);
	gtk_window_set_title (GTK_WINDOW (window),"test de spin button discret avec adjustement ");
 
	g_signal_connect (window, "delete_event",G_CALLBACK (cb_quit), NULL);
 
	double value_month = 6;
	GtkAdjustment * adjustement_button_month = gtk_adjustment_new (value_month,1, 13,1,1,1); 
	double value_day = 2;
	GtkAdjustment * adjustement_button_day = gtk_adjustment_new (value_day,1, 8,1,1,1); 
 
	GtkWidget * spinbutton_month = gtk_spin_button_new(adjustement_button_month,1,1);
	gtk_entry_set_width_chars(GTK_ENTRY(spinbutton_month),20);
	gtk_widget_set_tooltip_markup(GTK_WIDGET(spinbutton_month),"Utilisez le + ou le - pour faire défiler les mois ou les flèches clavier V /\\");
	/** Définit l'indicateur qui détermine si la valeur d'un bouton de rotation revient 
         * à la limite opposée lorsque la limite supérieure ou inférieure de la plage est dépassée. **/
	gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(spinbutton_month), TRUE);
 
	GtkWidget * spinbutton_day = gtk_spin_button_new(adjustement_button_day,1,1);
	gtk_entry_set_width_chars(GTK_ENTRY(spinbutton_day),20);
	gtk_widget_set_tooltip_markup(GTK_WIDGET(spinbutton_day),"Utilisez le + ou le - pour faire défiler les jours ou les flèches clavier V /\\");
 
	gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(spinbutton_day), TRUE);
 
	GtkWidget * month_label = gtk_label_new(NULL);
	GtkWidget * legende_month_label = gtk_label_new("Numéro du mois");
	GtkWidget * day_label = gtk_label_new(NULL);
	GtkWidget * legende_day_label = gtk_label_new("Numéro du jour de la semaine");
 
	int expand = TRUE ;
	int fill = TRUE;
	int padding =1;
 
	// pour le premier spin month
	gtk_box_pack_start (GTK_BOX (main_box), spinbutton_month, !expand, fill, padding);
	GtkWidget * Separator = gtk_separator_new(GTK_ORIENTATION_VERTICAL);
	gtk_box_pack_start (GTK_BOX (main_box), Separator, FALSE, FALSE, 0);
	Separator = gtk_separator_new(GTK_ORIENTATION_VERTICAL);
	gtk_box_pack_start (GTK_BOX (main_box), legende_month_label, !expand, fill, padding);
	gtk_box_pack_start (GTK_BOX (main_box), month_label, !expand, fill, padding);
 
	gtk_box_pack_start (GTK_BOX (vertical_box), main_box, !expand, fill, padding);
 
	// pour le spin day
	main_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
 
	gtk_box_pack_start (GTK_BOX (main_box), spinbutton_day, !expand, fill, padding);
	Separator = gtk_separator_new(GTK_ORIENTATION_VERTICAL);
	gtk_box_pack_start (GTK_BOX (main_box), Separator, FALSE, FALSE, 0);
	Separator = gtk_separator_new(GTK_ORIENTATION_VERTICAL);
	gtk_box_pack_start (GTK_BOX (main_box), legende_day_label, !expand, fill, padding);
	gtk_box_pack_start (GTK_BOX (main_box), day_label, !expand, fill, padding);
 
	gtk_box_pack_start (GTK_BOX (vertical_box), main_box, !expand, fill, padding);
 
	/** The output signal can be used to change to formatting of the value that 
         * is displayed in the spin buttons entry. **/
	g_signal_connect (spinbutton_month, "output", G_CALLBACK (spin_button_output), month_array);
	g_signal_connect (spinbutton_day, "output", G_CALLBACK (spin_button_output), day_array);
 
	/** par rapport à la demo gtk3-demo module spinbutton j'ai rajouté un paramètre pour le callback
         * g_signal_connect (spinbutton, "input", G_CALLBACK (month_spin_input));
         **/
	g_signal_connect (spinbutton_month, "input", G_CALLBACK (spin_button_input), month_array); 
	g_signal_connect (spinbutton_day, "input", G_CALLBACK (spin_button_input), day_array); 
 
	/**  la méthode suivante vue dans gtk3-demo module spinbutton 
         * la synchro est automatique du spinbutton vers le label et c'est 
         * la fonction value_to_label qui réalise l'action à faire 
         * Gobject va collecter la valeur du champ value du adjustement_button_month pour 
         * la ""coller"" dans le champ "label" celle du label month_label**/
	g_object_bind_property_full (adjustement_button_month, "value",
                                 month_label, "label",
                                 G_BINDING_SYNC_CREATE,
                                 value_to_label,
                                 NULL,
                                 NULL, NULL);
	g_object_bind_property_full (adjustement_button_day, "value",
                                 day_label, "label",
                                 G_BINDING_SYNC_CREATE,
                                 value_to_label,
                                 NULL,
                                 NULL, NULL);
	/* Affichage de la fenetre principale */
	gtk_widget_show_all (window);
}
 
int main (int argc, char **argv)
{
    GtkApplication *app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
    g_signal_connect (app, "activate", G_CALLBACK (do_spinbutton), NULL);
    int status = g_application_run (G_APPLICATION (app), argc, argv);
    g_object_unref (app);
	return status;
}
je suis preneur de vos remarques car il est certain perfectible