Voir le flux RSS

gerald3d

[Gtk+] Accéder à un pointeur d'un widget en tout point d'un programme

Noter ce billet
par , 12/01/2018 à 13h00 (122 Affichages)
Je profite de la brèche ouverte par liberforce au sujet de Glade. Je me suis dit qu'il serait possible à moindre coût de trouver une solution intermédiaire pour celles et ceux qui désirent construire leur interface par le code source et accéder à tous les widgets en tout point de leur programme sans variables déclarées en global. Cette dernière phrase est un peu longue, j'en conviens . Je vais la disséquer.

Je reprends ici une partie du code de vergifac pour la création de la fenêtre principale. Tout d'abord quelques pointeurs sont déclarés en static et en dehors de toute fonction (déclaration en global).
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
static GtkWidget *boutsfac;
static GtkWidget *boutsfcli;
static GtkWidget *boutsprod;
static GtkWidget *boutliprod;
static GtkWidget *boutopen;
static GtkWidget *boutlicli;
static GtkWidget *labdb;
Ces pointeurs sont utilisés pour pouvoir dans deux fonctions rendre sensible ou insensible à la souris les widgets associées.
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
void deactiv_bmenu()
{
 gtk_widget_set_sensitive(boutsfac,FALSE);
 gtk_widget_set_sensitive(boutlicli,FALSE);
 gtk_widget_set_sensitive(boutsfcli,FALSE);
 gtk_widget_set_sensitive(boutsprod,FALSE);
 gtk_widget_set_sensitive(boutliprod,FALSE);
 gtk_widget_set_sensitive(cbparam,FALSE);
}

void activ_bmenu()
{
 gtk_widget_set_sensitive(boutsfac,TRUE);
 gtk_widget_set_sensitive(boutlicli,TRUE);
 gtk_widget_set_sensitive(boutsfcli,TRUE);
 gtk_widget_set_sensitive(boutsprod,TRUE);
 gtk_widget_set_sensitive(boutliprod,TRUE);
 gtk_widget_set_sensitive(cbparam,TRUE);
}
Est aussi déclarer en global le pointeur du widget principal : GtkWidget *winp;.

Pour le code exemple que je vais détaillé ci-après je vais conserver le pointeur principal winp en global. Il est bien entendu possible et même conseillé de ne pas agir ainsi. Mais je ne voudrais pas trop embrouiller le lecteur .

L'idée générale serait de se passer tant que faire ce peut des déclarations en global. Comment faire ? Deux solutions sont possibles :

  1. créér une structure dans laquelle on déclare toutes les variables nécessaires. On déclare ensuite une variable de ce type. On initialise tout ce petit monde et on la propage à toutes les fonctions qui en aurait besoin,
  2. le pointeur de la fenêtre principale pointe sur un container. il ne faut pas oublier qu'une IHM est construite sur le principe des poupées russes. Nous pouvons donc récupérer tous les pointeurs de tous les widgets de l'interface depuis le pointeur principal.

La première solution est très intéressante. Elle permet aux applications de se passer de toute (ou presque) déclaration en global. Il ne faut donc pas l'écarter. Elle devrait être utilisée ne serait-ce que pour déclarer le pointeur principal winp en local.

La deuxième solution est celle que je vais détailler. La méthode employée ici est une méthode intermédiaire. Je m'explique. Si vous utilisez Glade pour construire vos interfaces, vous passez par un GtkBuilder. Il vous permet d'accéder à tout pointeur de l'interface via le nom que vous lui avez donné. Si maintenant vous avez construit l'interface avec le code source il est aussi possible de faire la même chose mais sans passer par un GtkBuilder, inutilisable en l'état dans cette configuration. Il est nécessaire d'écrire quelques lignes de code que je vais vous livrer ici.

Accéder à tout widget d'une interface.

La première chose à savoir est que la plupart des widgets hérite avant tout d'un GtkContainer qui lui-même hérite d'un GtkWidget. Nous pouvons donc les caster en GtkContainer. Pour les autres (GtkButton, GtkLabel etc) ils ne contiennent rien à l'intérieur. Ils seront traités par les fonctions liées au GtkContainer. Cette première notion est fondamentale.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
GtkWidget
     GtkContainer
          GtkBin
          GtkBox
          GtkCList
          GtkFixed
          ...
La deuxième chose à savoir est que tout GtkWidget a un nom. Quand j'écris un nom, je parle bien d'un texte gchar* qui le décrit. Lorsque vous créez un GtkLabel par exemple son nom par défaut est "GtkLabel". Bon OK ca n'apporte pas grand chose pour l'instant . Ce qui est intéressant c'est qu'on peut modifier ce nom et bien entendu aussi le récupérer via ces deux fonctions :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
void
gtk_widget_set_name (GtkWidget *widget,
                     const gchar *name);
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
const gchar *
gtk_widget_get_name (GtkWidget *widget);
Nous avons maintenant la possibilité de trouver un pointeur depuis un texte. Il nous faut aussi avoir la possibilité d'extraire tous les pointeurs de l'interface pour nous permettre de comparer les noms. C'est ici que les fonctions liées aux GtkContainer vont nous sauver :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
void
gtk_container_foreach (GtkContainer *container,
                       GtkCallback callback,
                       gpointer callback_data);
Cette fonction permet d'envoyer à une fonction callback tous les widgets qui sont inclus dans le container transmis. On peut aussi envoyer à cette même fonction une donnée personnelle que nous utiliserons ici.
Il est important de comprendre que le callback sera appelé une seule fois si le premier widget de l'interface est un GtkBox par exemple. En effet cette fonction s'arrête à la première "poupée russe" rencontrée. Il faudra créer une boucle pour entrer dans le GtkBox et à nouveau lancer gtk_container_foreach();.

En résumé.

  • On peut nommer les GtkWidget et ainsi les retrouver grâce aux fonctions gtk_widget_set_name () et gtk_widget_get_name (),
  • on peut extraire tous les widgets d'une interface grâce à la fonction gtk_container_foreach ().


Je vous livre tout d'abord la fonction callback ainsi que la fonction à appeler pour extraire un pointeur. La fonction callback_find_widget () est déclarée en static puisque exclusivement utiliser par la fonction de recherche get_widget_from_window ().
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
static void
callback_find_widget (GtkWidget *widget, gpointer data)
{
  /* Création d'une structure interne pour pouvoir récupérer les différents
   * widgets de l'interface mais aussi le résultat de la comparaison des noms.
   */
  typedef struct
  {
    GtkWidget *widget;
    const gchar *name;
    gboolean resultcomp;
    GtkWidget *ptrfind;
  } Sresult;
  
  Sresult *result = (Sresult*)data;

  if (g_strcmp0 (gtk_widget_get_name (widget), result->name)==0)
  {
    result->resultcomp = TRUE;
    result->ptrfind = widget;
  }

  result->widget = widget;
}

GtkWidget *
get_widget_from_window (GtkWidget *winp, const gchar *name)
{
  /* Retourne NULL si le widget n'est pas valide */
  g_return_val_if_fail (GTK_IS_WIDGET (winp), NULL);
  /* Retourne NULL si name = NULL */
  g_return_val_if_fail (name!=NULL, NULL);

  /* Création d'uns structure interne pour pouvoir récupérer les différents
   * widgets de l'interface mais aussi le résultat de la comparaison des noms.
   */
  typedef struct
  {
    GtkWidget *widget;
    const gchar *name;
    gboolean resultcomp;
    GtkWidget *ptrfind;
  } Sresult;
  
  Sresult result;
  GtkWidget *widget = NULL;

  /* Initialisation de result */
  result.widget = winp;
  result.name = name;
  result.resultcomp = FALSE;
  widget = winp;
  while (1)
    {
       if (GTK_IS_CONTAINER (widget))
	gtk_container_foreach (GTK_CONTAINER (widget),(GtkCallback)callback_find_widget, &result);

      if (result.resultcomp == TRUE)
	return result.ptrfind;
      else
	{
	  /* Permet de sortir de la boucle lorsque tous les widgets ont été parcourus */
	  if (widget==result.widget)
	    break;
	  else
	    widget = result.widget;
	}
    }

  return NULL;
}
La fonction get_widget_from_window (GtkWidget *winp, const gchar *name); permet donc, depuis le GtkWidget *winp transmis d'extraire le pointeur d'un widget portant le nom name. Cette fonction vous renvoie soit un pointeur valide si la recherche est fructueuse, soit NULL dans le cas contraire ou si winp n'est pas valide ou name est NULL.

Pour pouvoir l'utiliser il ne reste plus qu'à nommer nos widgets. En reprenant le code exemple, lors de la création on peut écrire :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
...
boutsfac = gtk_button_new_with_label("Saisie Facture");
gtk_widget_set_size_request(boutsfac, 120, 25);
gtk_fixed_put(GTK_FIXED(frawp), boutsfac,20,py);
gtk_widget_set_name (boutsfac, "boutsfac");
...
Pour pouvoir retrouver le pointeur de ce widget il suffira d'écrire :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
  if (get_widget_from_window (winp, "boutsfac"))
    g_printerr ("widget trouvé\n");
  else
    g_printerr ("widget non trouvé !!!\n");
Ainsi en reprenant le code exemple du départ avec nos deux fonctions qui désactivent/activent des widgets on peut écrire ceci et se passer des déclarations en global de tous les pointeurs associés :
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
/* static GtkWidget *boutsfac; */
static GtkWidget *boutsfcli;
static GtkWidget *boutsprod;
static GtkWidget *boutliprod;
static GtkWidget *boutopen;
static GtkWidget *boutlicli;
static GtkWidget *labdb;

void deactiv_bmenu()
{
  gtk_widget_set_sensitive(get_widget_from_window (winp, "boutsfac"), FALSE);
  gtk_widget_set_sensitive(boutlicli,FALSE);
  gtk_widget_set_sensitive(boutsfcli,FALSE);
  gtk_widget_set_sensitive(boutsprod,FALSE);
  gtk_widget_set_sensitive(boutliprod,FALSE);
  gtk_widget_set_sensitive(cbparam,FALSE);
}

void activ_bmenu()
{
  gtk_widget_set_sensitive(get_widget_from_window (winp, "boutsfac"), TRUE);
  gtk_widget_set_sensitive(boutlicli,TRUE);
  gtk_widget_set_sensitive(boutsfcli,TRUE);
  gtk_widget_set_sensitive(boutsprod,TRUE);
  gtk_widget_set_sensitive(boutliprod,TRUE);
  gtk_widget_set_sensitive(cbparam,TRUE);
}
À vos claviers...et vos commentaires.

Envoyer le billet « [Gtk+] Accéder à un pointeur d'un widget en tout point d'un programme » dans le blog Viadeo Envoyer le billet « [Gtk+] Accéder à un pointeur d'un widget en tout point d'un programme » dans le blog Twitter Envoyer le billet « [Gtk+] Accéder à un pointeur d'un widget en tout point d'un programme » dans le blog Google Envoyer le billet « [Gtk+] Accéder à un pointeur d'un widget en tout point d'un programme » dans le blog Facebook Envoyer le billet « [Gtk+] Accéder à un pointeur d'un widget en tout point d'un programme » dans le blog Digg Envoyer le billet « [Gtk+] Accéder à un pointeur d'un widget en tout point d'un programme » dans le blog Delicious Envoyer le billet « [Gtk+] Accéder à un pointeur d'un widget en tout point d'un programme » dans le blog MySpace Envoyer le billet « [Gtk+] Accéder à un pointeur d'un widget en tout point d'un programme » dans le blog Yahoo

Catégories
Programmation , C

Commentaires