Bon, j'ai fini (enfin :mrgreen:).
Finalement, affecter un curseur popup pour chacun des objets m'a grandement simplifié le code (ce qui ne veut pas forcement dire qu'il est devenu plus efficace ; cela multiplie le nombre d'objets gérés par la framework g_object).
Le code, donc.
Le fichier d'entêtes de l'objet ScaledValue :
Le fichier source de l'objet ScaledValue :Code:
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 /***********************************************/ /* Fichier d'entêtes de l'objet ScaledValue. */ /***********************************************/ /****************************************************************************************************/ /* Le widget ScaledValue prend à sa création via sa fonction scaled_value_new() une valeur initiale */ /* de type guint comprise entre 0 et 100. */ /* Il affiche cette valeur suivie du symbole "%" dans un rectangle entouré d'un frame. */ /* La valeur peut aussi être changée via la fonction scaled-value_set_value(). */ /* Lorsque le pointeur de la souris survole le widget, une fenêtre popup contenant un curseur */ /* apparaît à droite du widget. Elle disparaît un 10ème de seconde après que le pointeur ait quitté */ /* le widget (le frame contenant la valeur affichée ou le popup contenant le curseur). */ /* A l'aide du curseur, il est possible d'ajuster la valeur du widget. */ /* Chaque fois que la valeur a changé, le signal "value-changed" est émis. */ /* La nouvelle valeur peut être récupérée via la fonction scaled_value_get_value(). */ /* Le widget garde la valeur intacte jusqu'à ce qu'elle soit à nouveau changée à l'aide du curseur */ /* ou que le widget soit détruit. */ /****************************************************************************************************/ #ifndef __SCALED_VALUE_H__ #define __SCALED_VALUE_H__ #include <glib.h> #include <glib-object.h> #include <gtk/gtkframe.h> G_BEGIN_DECLS #define SCALED_VALUE_TYPE (scaled_value_get_type ()) #define SCALED_VALUE(any_widget) (G_TYPE_CHECK_INSTANCE_CAST ((any_widget), \ SCALED_VALUE_TYPE, ScaledValue)) #define SCALED_VALUE_CLASS(any_class) (G_TYPE_CHECK_CLASS_CAST ((any_class), \ SCALED_VALUE_TYPE, ScaledValueClass)) #define IS_SCALED_VALUE(any_widget) (G_TYPE_CHECK_INSTANCE_TYPE ((any_widget), \ SCALED_VALUE_TYPE)) #define IS_SCALED_VALUE_CLASS(any_class) (G_TYPE_CHECK_CLASS_TYPE ((any_class), \ SCALED_VALUE_TYPE)) typedef struct _ScaledValueClass ScaledValueClass; typedef struct _ScaledValue ScaledValue; typedef struct _ScaledValuePrivate ScaledValuePrivate; /* Déclaration de la structure de classe du widget ScaledValue. */ struct _ScaledValueClass { GtkFrameClass parent_class; /* Pas un pointeur !. */ /* Déclare le signal "value-changed", émis chaque fois que la valeur change. */ void (* value_changed) (ScaledValue *scaledvalue); }; /* déclaration de la structure du widget ScaledValue. */ struct _ScaledValue { /* Le widget "de peau" du widget ScaledValue est un GtkFrame. Donc, dérive de GtkFrame. */ GtkFrame frame; /* Pas un pointeur !. */ /* Pointeur sur les données privées du widget ScaledValue. */ ScaledValuePrivate *priv; }; /* Prototypes des fonctions. */ GType scaled_value_get_type (void) G_GNUC_CONST; GtkWidget * scaled_value_new (guint value); void scaled_value_set_value (ScaledValue *scaledvalue, guint value); guint scaled_value_get_value (ScaledValue *scaledvalue); G_END_DECLS #endif /* __SCALED_VALUE_H__ */
Et enfin, un petit programme d'exemple :Code:
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
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461 /*******************************************/ /* Fichier source de l'objet ScalableValue */ /*******************************************/ /****************************************************************************************************/ /* Le widget ScaledValue prend à sa création via sa fonction scaled_value_new() une valeur initiale */ /* de type guint comprise entre 0 et 100. */ /* Il affiche cette valeur suivie du symbole "%" dans un rectangle entouré d'un frame. */ /* La valeur peut aussi être changée via la fonction scaled-value_set_value(). */ /* Lorsque le pointeur de la souris survole le widget, une fenêtre popup contenant un curseur */ /* apparaît à droite du widget. Elle disparaît un 10ème de seconde après que le pointeur ait quitté */ /* le widget (le frame contenant la valeur affichée ou le popup contenant le curseur). */ /* A l'aide du curseur, il est possible d'ajuster la valeur du widget. */ /* Chaque fois que la valeur a changé, le signal "value-changed" est émis. */ /* La nouvelle valeur peut être récupérée via la fonction scaled_value_get_value(). */ /* Le widget garde la valeur intacte jusqu'à ce qu'elle soit à nouveau changée à l'aide du curseur */ /* ou que le widget soit détruit. */ /****************************************************************************************************/ #include <gtk/gtk.h> #include "scaledvalue.h" /****************************************************************************************************/ /* Structure des données privées du widget ScaledValue. */ /****************************************************************************************************/ struct _ScaledValuePrivate { /* Pointeurs sur les widgets permettant d'afficher la valeur et de recevoir les signaux. */ GtkLabel *label; GtkEventBox *eventbox; /* Pointeurs sur les widgets du popup. */ GtkScale *scale; GtkWindow *popup; /* Référence du timeout */ guint timeout_ref; }; /****************************************************************************************************/ /* Signaux qui peuvent être émis par le widget ScaledValue. */ /****************************************************************************************************/ enum { VALUE_CHANGED, LAST_SIGNAL }; static guint scaled_value_signals [LAST_SIGNAL] = { 0 }; /****************************************************************************************************/ /* Prototypes des fonctions du widget ScaledValue. */ /****************************************************************************************************/ /* Fonctions publiques (pour mémoire, définies dans scaledvalue.h) */ /* GType scaled_value_get_type (void) G_GNUC_CONST; */ /* GtkWidget * scaled_value_new (guint value); */ /* void scaled_value_set_value (ScaledValue *scaledvalue, guint value); */ /* guint scaled_value_get_value (ScaledValue *scaledvalue); */ /* Fonctions d'initialisation du widget ScaledValue */ static void scaled_value_class_init (ScaledValueClass *); static void scaled_value_init (ScaledValue *); /* Fonctions de callback */ static gboolean scaled_value_cb_enter_notify_eventbox (GtkEventBox *, GdkEvent *, ScaledValue *); static gboolean scaled_value_cb_leave_notify_eventbox (GtkEventBox *, GdkEvent *, ScaledValue *); static gboolean scaled_value_cb_enter_notify_popup (GtkWindow *, GdkEvent *, ScaledValue *); static gboolean scaled_value_cb_leave_notify_popup (GtkWindow *, GdkEvent *, ScaledValue *); static void scaled_value_cb_value_scale_changed (GtkRange *, ScaledValue *); static gboolean scaled_value_end_timeout (ScaledValue *); /****************************************************************************************************/ /* Code des fonctions. */ /****************************************************************************************************/ /* Fournit le type GType correspondant au widget ScaledValue. */ /* Le type est créé au premier appel de la fonction (lors de l'initialisation de la classe). */ /* De plus, cette fonction enregistre la structure des données privées de la classe. */ GType scaled_value_get_type (void) { /* Déclare le type "static" et le force à 0 pour le premier appel de la fonction */ static GType scaled_value_type = 0; /* Si le GType est égal à 0 */ if (!scaled_value_type) { GTypeInfo scaled_value_info = { sizeof (ScaledValueClass), NULL, NULL, (GClassInitFunc) scaled_value_class_init, NULL, NULL, sizeof (ScaledValue), 0, (GInstanceInitFunc) scaled_value_init, }; /* Enregistre le type ScaledValue. Précise notamment que le type ScaledValue dérive de GTK_TYPE_FRAME, */ /* ce qui permet d'utiliser toutes les propriétés de GtkFrame ainsi que celle des widgets dont dérive */ /* GtkFrame, en particulier GtkContainer pour attacher le GtkEventBox et le GtkLabel au widget ScaledValue. */ scaled_value_type = g_type_register_static (GTK_TYPE_FRAME, "ScaledValue", &scaled_value_info, 0); } return scaled_value_type; } /* Crée une nouvelle instance du widget ScaledValue et renvoie un pointeur sur ce widget. */ /* A la première instance, appelle la fonction scaled_value_class_Init (), puis à chaque */ /* nouvelle instance (dont la première), appelle la fonction scaled_value_init () */ GtkWidget* scaled_value_new (guint value) { /* Déclaration des variables locales à la fonction. */ ScaledValue *scaledvalue; ScaledValuePrivate *priv; /* Crée effectivement le widget ScaledValue. */ scaledvalue = SCALED_VALUE (g_object_new (scaled_value_get_type (), NULL)); /* A ce stade, la fonction scaled_value_init () a déjà été exécutée. */ /* Notamment, le pointeur scaledvalue->priv a déjà été initialisé. */ /* Récupère un pointeur sur la structure privée du widget ScaledValue. */ priv = scaledvalue->priv; /* Affecte la valeur du widget au curseur. */ gtk_range_set_value (GTK_RANGE (priv->scale), (gdouble) value); /* Initialise le label. */ gtk_label_set_text (priv->label, g_strdup_printf ("%d%%", value)); return GTK_WIDGET (scaledvalue); } /* Initialise la classe ScaledValueClass. */ static void scaled_value_class_init (ScaledValueClass *scaledvalueclass) { /* Ajoute ScaledValuePrivate comme structure de données privée de chaque widget ScaledValue. */ g_type_class_add_private (scaledvalueclass, sizeof (ScaledValuePrivate)); /* Enregistre le signal "value-changed" qui sera émis lorsque la valeur aura été changée. */ scaled_value_signals [VALUE_CHANGED] = g_signal_new ("value-changed", G_TYPE_FROM_CLASS (scaledvalueclass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (ScaledValueClass, value_changed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } /* Initialise le widget ScaledValue courant. Cette fonction est utilisée */ /* pour fixer la vue initiale du widget et les propriétés nécessaires. */ static void scaled_value_init (ScaledValue *scaledvalue) { /* Déclaration des variables locales à la fonction. */ ScaledValuePrivate *priv; /* Initialise un pointeur statique sur les données privées du widget ScaledValue. */ scaledvalue->priv = G_TYPE_INSTANCE_GET_PRIVATE (scaledvalue, SCALED_VALUE_TYPE, ScaledValuePrivate); priv = scaledvalue->priv; /* A ce moment, il n'y a aps de timeout actif. */ priv->timeout_ref = 0; /* Crée le Label et l'EventBox qui l'entoure. */ priv->label = GTK_LABEL (gtk_label_new (NULL)); priv->eventbox = GTK_EVENT_BOX (gtk_event_box_new ()); /* Paramètre les widgets. */ gtk_label_set_width_chars (priv->label, 5); gtk_misc_set_alignment (GTK_MISC (priv->label), 1, .5); /* Attache les objets les uns aux autres. */ gtk_container_add (GTK_CONTAINER (priv->eventbox), GTK_WIDGET (priv->label)); gtk_container_add (GTK_CONTAINER (scaledvalue), GTK_WIDGET (priv->eventbox)); /* Attache les fonctions de callback. */ g_signal_connect (G_OBJECT (priv->eventbox), "enter-notify-event", G_CALLBACK (scaled_value_cb_enter_notify_eventbox), scaledvalue); g_signal_connect (G_OBJECT (priv->eventbox), "leave-notify-event", G_CALLBACK (scaled_value_cb_leave_notify_eventbox), scaledvalue); /* Affiche les widgets internes du widget ScaledValue. */ gtk_widget_show (GTK_WIDGET (priv->label)); gtk_widget_show (GTK_WIDGET (priv->eventbox)); /* Crée la fenêtre popup et l'objet MyScale qu'elle contient, ainsi que l'ajustement associé. */ priv->scale = GTK_SCALE (gtk_scale_new_with_range (GTK_ORIENTATION_VERTICAL, 0, 100, 1)); priv->popup = GTK_WINDOW (gtk_window_new (GTK_WINDOW_POPUP)); /* Paramètre les widgets. */ gtk_scale_set_draw_value (GTK_SCALE (priv->scale), FALSE); gtk_range_set_inverted (GTK_RANGE (priv->scale), TRUE); gtk_window_set_decorated (priv->popup, FALSE); gtk_widget_set_size_request (GTK_WIDGET (priv->popup), 1, 140); gtk_window_set_resizable (priv->popup, FALSE); /* Attache les objets les uns aux autres. */ gtk_container_add (GTK_CONTAINER (priv->popup), GTK_WIDGET (priv->scale)); /* Rend le curseur visible dans la fenêtre popup. */ gtk_widget_show (GTK_WIDGET (priv->scale)); /* Attache les fonctions de callback. */ g_signal_connect (G_OBJECT (priv->popup), "enter-notify-event", G_CALLBACK (scaled_value_cb_enter_notify_popup), scaledvalue); g_signal_connect (G_OBJECT (priv->popup), "leave-notify-event", G_CALLBACK (scaled_value_cb_leave_notify_popup), scaledvalue); g_signal_connect (G_OBJECT (priv->scale), "value_changed", G_CALLBACK (scaled_value_cb_value_scale_changed), scaledvalue); } /* Fixe la valeur du widget ScaledValue. Si la valeur est différente de la valeur courante, le signal */ /* "value-changed" est émis par le widget GtkScale interne et la valeur affichée est mise à jour. */ void scaled_value_set_value (ScaledValue *scaledvalue, guint value) { /* Affecte la valeur du widget au curseur. */ gtk_range_set_value (GTK_RANGE (scaledvalue->priv->scale), (gdouble) value); } /* Rend la valeur du widget ScaledValue */ guint scaled_value_get_value (ScaledValue *scaledvalue) { return (guint) gtk_range_get_value (GTK_RANGE (scaledvalue->priv->scale)); } /****************************************************************************************************/ /* Fonctions de callback. */ /****************************************************************************************************/ /* Fonction appelée lorsque le pointeur entre au dessus de l'eventbox. */ static gboolean scaled_value_cb_enter_notify_eventbox (GtkEventBox *eventbox, GdkEvent *event, ScaledValue *scaledvalue) { /* Déclaration des variables locales à la fonction. */ ScaledValuePrivate * priv; GtkWindow *window; GtkAllocation allocation; /* Pas un pointeur ! */ gint window_x, window_y, widget_x, widget_y; /* Récupère un pointeur sur la structure privée du widget ScaledValue. */ priv = scaledvalue->priv; /* Si un timeout est en cours pour le widget ScaledValue courant, on le stoppe. */ if (priv->timeout_ref) { g_source_remove (priv->timeout_ref); /* Efface la référence du timeout. */ priv->timeout_ref = 0; } /* Calcule la position du coin en haut et à droite du frame. */ window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (scaledvalue))); gdk_window_get_position (gtk_widget_get_window (GTK_WIDGET (window)), &window_x, &window_y); gtk_widget_get_allocation (GTK_WIDGET (scaledvalue), &allocation); gtk_widget_translate_coordinates (GTK_WIDGET (scaledvalue), GTK_WIDGET (window), window_x + allocation.width, window_y, &widget_x, &widget_y); /* Déplace la fenêtre popup juste à droite du frame. */ gtk_window_move (priv->popup, widget_x, widget_y); /* affiche le popup. */ gtk_widget_show (GTK_WIDGET (priv->popup)); (void) event; return TRUE; /* Arrête la propagation de l'évènement. */ } /* Fonction appelée lorsque le pointeur quitte l'eventbox. */ static gboolean scaled_value_cb_leave_notify_eventbox (GtkEventBox *eventbox, GdkEvent *event, ScaledValue *scaledvalue) { /* Lance le timeout (1 dixième de seconde) et enregistre sa référence. */ scaledvalue->priv->timeout_ref = g_timeout_add (100, (GSourceFunc) scaled_value_end_timeout, scaledvalue); (void) eventbox; (void) event; return TRUE; /* Arrête la propagation de l'évènement. */ } /* Fonction appelée lorsque le pointeur entre au dessus du popup. */ static gboolean scaled_value_cb_enter_notify_popup (GtkWindow *popup, GdkEvent *event, ScaledValue *scaledvalue) { /* Déclaration des variables locales à la fonction. */ ScaledValuePrivate * priv; /* Récupère un pointeur sur la structure privée du widget ScaledValue. */ priv = scaledvalue->priv; /* Arrête le timeout. */ g_source_remove (priv->timeout_ref); /* Efface la référence du timeout. */ priv->timeout_ref = 0; (void) popup; (void) event; return TRUE; /* Arrête la propagation de l'évènement. */ } /* Fonction appelée lorsque le pointeur quitte le popup. */ static gboolean scaled_value_cb_leave_notify_popup (GtkWindow *popup, GdkEvent *event, ScaledValue *scaledvalue) { /* Lance le timeout (1 dixième de seconde) et enregistre sa référence. */ scaledvalue->priv->timeout_ref = g_timeout_add (100, (GSourceFunc) scaled_value_end_timeout, scaledvalue); (void) event; return TRUE; /* Arrête la propagation de l'évènement. */ } /* Fonction appelée lorsque la valeur contrôlée par le curseur change. */ static void scaled_value_cb_value_scale_changed (GtkRange *scale, ScaledValue *scaledvalue) { /* Déclaration des variables locales à la fonction. */ ScaledValuePrivate *priv; /* Récupère un pointeur sur la structure privée du widget ScaledValue. */ priv = scaledvalue->priv; /* Aligne la valeur du widget sur celle du curseur. */ gtk_label_set_text (priv->label, g_strdup_printf ("%d%%", (guint) gtk_range_get_value (scale))); /* Emet le signal "value-changed". */ g_signal_emit_by_name (scaledvalue, "value-changed"); } /* Fonction appelée à la fin du timeout. Masque le popup. */ static gboolean scaled_value_end_timeout (ScaledValue *scaledvalue) { /* Déclaration des variables locales à la fonction. */ ScaledValuePrivate *priv; /* Récupère un pointeur sur la structure privée du widget ScaledValue. */ priv = scaledvalue->priv; /* Masque la fenêtre popup */ gtk_widget_hide (GTK_WIDGET (priv->popup)); /* Efface la référence du timeout. */ priv->timeout_ref = 0; return FALSE; /* Empèche le timeout de se relancer. */ }
C'est Gérald3d qui va être content ; plus une seule variable globale.8OCode:
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 #include <stdlib.h> /* Décaration de "EXIT_SUCCESS" */ #include <gtk/gtk.h> #include "scaledvalue.h" /* Prototypes de la fonction de callback */ static void cb_value_changed (ScaledValue *); int main (int argc, char *argv[]) { /* Initialisation de GTK */ gtk_init (&argc, &argv); /* Déclaration des objets */ GtkWindow *window; GtkGrid *grid; ScaledValue *scaledvalue1, *scaledvalue2; /* Initialisation des objets */ window = GTK_WINDOW (gtk_window_new (GTK_WINDOW_TOPLEVEL)); grid = GTK_GRID (gtk_grid_new ()); scaledvalue1 = SCALED_VALUE (scaled_value_new (100)); scaledvalue2 = SCALED_VALUE (scaled_value_new (50)); /* Paramétrage des objets */ gtk_window_set_title (window, "Mon exemple 3"); gtk_container_set_border_width (GTK_CONTAINER (window), 10); gtk_widget_set_size_request (GTK_WIDGET (window), 300, 300); gtk_grid_set_row_spacing (grid, 5); gtk_grid_set_column_homogeneous (grid, FALSE); gtk_grid_set_row_homogeneous (grid, FALSE); gtk_widget_set_halign (GTK_WIDGET (grid), GTK_ALIGN_START); gtk_widget_set_valign (GTK_WIDGET (grid), GTK_ALIGN_START); /* Attachement des objets les uns dans les autres */ gtk_grid_attach (grid, GTK_WIDGET (scaledvalue1), 0 , 0, 1, 1); gtk_grid_attach_next_to (grid, GTK_WIDGET (scaledvalue2), GTK_WIDGET (scaledvalue1), GTK_POS_RIGHT, 1, 1); gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (grid)); /* Déclaration des fonctions de callback */ g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (gtk_main_quit), NULL); g_signal_connect (G_OBJECT (scaledvalue1), "value-changed", G_CALLBACK (cb_value_changed), NULL); g_signal_connect (G_OBJECT (scaledvalue2), "value-changed", G_CALLBACK (cb_value_changed), NULL); /* Affiche les widgets */ gtk_widget_show_all (GTK_WIDGET (window)); /* Et c'est parti ! */ gtk_main (); return EXIT_SUCCESS; } static void cb_value_changed (ScaledValue *scaledvalue) { guint value; /* récupère la valeur du curseur et la recopie dans le label */ value = scaled_value_get_value (scaledvalue); g_print ("La nouvelle valeur est : %d%%\n", value); }
Maintenant, je vais me faire le même (ou presque) pour régler un angle entre 0 et 360° avec un bouton rotatif.
Jean-Marie