Free online content available in this course.

Got it!

Last updated on 1/8/18

Notions de base et fenêtres

Log in or subscribe for free to enjoy all this course has to offer!

Vous voici donc dans le premier chapitre intéressant ! ^^

Dans un premier temps nous allons faire un peu de théorie pour comprendre comment GTK fonctionne, puis nous allons découvrir le code de base d'une application GTK.
Ensuite, nous allons voir comment créer une fenêtre, notamment la fenêtre principale de votre programme puis comment la personnaliser et la caractériser. En ce qui concerne les boîtes de dialogue (erreurs, infos, questions, etc), nous les verrons dans la partie II quand vous en saurez un peu plus. ;)

On a du pain sur la planche ! Bon courage et bonne lecture !

Widgets et héritage

Les Widgets

Avant de passer à la pratique, il va falloir que je vous explique certaines choses sur le fonctionnement des objets GTK (boutons, menus, etc...).

Lorsque vous développerez des applications avec GTK, les objets que vous mettrez dans vos fenêtres (je ne sais pas trop comment les appeler autrement que par leur vrai nom) sont appelés WIDGETS. Il n'existe pas de sens exact pour ce mot (sens général, bien sûr), mais en cherchant un peu, vous trouverez différentes définitions, voici quelques explications :

  • De l'anglais : machin, chose, gadget.

  • Gadget de Dashboard sous mac OSX Tiger.

  • Plus interéssant : WInDow gadGET ou élément d'une interface graphique.

Je pense que vous commencez à comprendre de quoi il s'agit !!! :) En gros,les widgets sont les boutons, les zones de texte, les menus, enfin.. à peu près tout ce qui constitue une interface !

Quelques bases
  • Il existe des types différents (ce sont des structures) pour chaque type de widget. Cependant, on ne les utilise que dans des cas particuliers et on utilisera la plupart du temps le type "widget" pour stocker les objets.

  • Les noms des fonctions ont une syntaxe particulière, ils sont de la forme gtk_widget_action(...). C'est cela dans la plupart des cas avec GTK.
    On remplace :

    • widget par le type de widget sur lequel on travaille (ex : window pour une fenêtre).

    • action par ce que la fonction est sensée effectuer (ex : set_title pour définir un titre)

C'est très facile à comprendre, vous ne devriez pas avoir de problème avec ça ! ^^

L'héritage

Après les Widgets, il y a une autre notion à connaitre avant de commencer, l'héritage ! Si vous faites du C++, vous devriez savoir ce que c'est car c'est le but du C++ : l'héritage et l'orienté objet ! Mais nous travaillerons en C et GTK introduit une notion d'héritage.

Il faut que vous sachiez qu'il existe une hiérarchie entre les différents Widgets : il y a des groupes qui contiennent plusieurs widgets, la possibilité qu'un widget en contienne un autre, etc. L'héritage de GTK y est directement lié !

Un exemple concret

Je vais vous expliquer l'héritage avec un exemple : Une fenêtre.

Je vous ai expliqué que pour créer un objet, on déclare un widget puis on y stocke un objet quelconque.
Une fenêtre est donc avant tout un Widget.

Citation : Hiérarchie des objets

Widget -> fenêtre

Maintenant, imaginons que je veuille créer une boîte de dialogue. Une boîte de dialogue c'est quoi ? En y réfléchissant un peu, on s'aperçoit que c'est aussi une fenêtre ! On y a apporté des modifications mais c'est une fenêtre. On va donc descendre d'un cran pour dire qu'elle est particulière :

Citation : Hiérarchie des objets

Widget -> Fenêtre -> Boîte_de_dialogue

Petit point de vocabulaire : On dit que BOITE_DE_DIALOGUE dérive de FENETRE (retenez bien le terme de dériver). Dans la réalité, l'architecture est bien plus compliquée, mais d'un point de vue théorique, c'est exactement ça !

A quoi ça sert ?

L'intérêt de tout ça ? Il est simple.

Un premier exemple : Pour créer une boite de dialogue, les développeurs de GTK se sont dit : "On a créé une fenêtre, y'a plus qu'à la modifier !". La fonction qui crée une boite de dialogues appelle la fonction qui crée une fenêtre, puis modifie certains paramètres pour en faire une boite de dialogues.
Un peu de pseudo-code C :

Widget* nouvelle_boite_dialogue(void)
{
Widget * fenetre = nouvelle_fenetre();
/* On change la taille, on ajoute du texte et des boutons, ..
pour faire une boite de dialogue */
return fenetre;
}

Un deuxième exemple : Les developpeurs de GTK ont programmé une fonction pour définir le titre d'une fenêtre. Mais pour une boite de dialogue ?
Se sont-ils ennuyés à refaire la même fonction (car une boîte de dialogue a la même structure qu'une fenêtre) pour une boîte de dialogue ? Et donc avoir deux fonctions identiques pour deux objets similaires ? ... NON !

Tout simplement, vous utiliserez sur BOITE_DE_DIALOGUE une fonction faite pour FENETRE, ce qui ne pose aucun problème puisque BOITE_DE_DIALOGUE dérive de FENETRE.
Concluez que vous pouvez utiliser sur un widget les fonctions des widgets parents. :) (dans la plupart des cas)
_ _ _ _ _ _ _ _ _ _ _ _ _

Voilà à quoi sert l'héritage, à minimiser le nombre de fonctions pour des choses identiques. Si, ça reste assez flou pour vous, méditez bien là-dessus jusqu'à ce que vous ayez bien compris, c'est très important ! :)

Code de base et création d'une fenêtre

Code de base GTK

Dans le chapitre précédent, vous avez installé la bibliothèque GTK, les runtimes nécessaires pour que vos programmes puissent être exécutés, et même d'autres librairies permettant à GTK de faire des choses beaucoup plus avancées ! (Car la librairie GTK toute seule ne sert pas à grand chose :p )

Après (peut-être) quelques difficultés, vous êtes arrivé à la fin de ce chapitre où vous avez trouvé le code de base d'une application GTK, afin de tester si tout marchait bien ; je vous le remet ici :

#include <stdlib.h>
#include <gtk/gtk.h>
int main(int argc, char **argv)
{
/* Initialisation de GTK+ */
gtk_init(&argc, &argv);
return EXIT_SUCCESS;
}

Citation : M@teo

Un grand nombre de librairies écrites en C nécessitent d'être initialisées et fermées par des appels à des fonctions. La SDL n'échappe pas à la règle.

GTK+ non plus ! :p En fait, GTK+ a juste besoin d'être initialisé, pas besoin de la quitter.

Vous remarquez donc les nouveautés suivantes :

  • int main(int argc, char **argv) : En fait, rien de nouveau mais n'oubliez pas que le main doit avoir ce prototype !

  • #include <gtk/gtk.h> : L'include de la librairie GTK+, charge la librairie et permet l'utilisation des fonctions GTK & co.

  • gtk_init(&argc, &argv) : Première fonction, elle initialise GTK et charge certaines choses en mémoire. On lui passe l'adresse des arguments du main.

  • return EXIT_SUCCESS : EXIT_SUCCESS est, disons, mieux que 0. C'est donc cela que nous utiliserons. :)

Si vous compilez, vous remarquerez que rien n'apparaît à l'écran, normal, tout comme la SDL, initialiser la librairie ne suffit pas, il faut créer une fenêtre. :p (ou autre chose, mais on va commencer par ça !)

Création d'une fenêtre

Faites bien attention, la méthode que je vais utiliser pour vous expliquer les fenêtres sera la même que celle que j'utiliserai tout au long du cours.
Cela se passe en trois étapes :

  • Explication du widget, hiérarchie et fonctionnement.

  • Fonctions relatives à ce/ces Widget(s).

  • Code d'exemple complet.

Le nom du widget fenêtre est GtkWindow. Voici sa hiérarchie :

Citation : Hiérarchie de GtkWindow

GObject -> GtkObject -> GtkWidget -> GtkContainer -> GtkBin -> GtkWindow

Vous allez donc déclarer un pointeur sur GtkWidget, vous pouvez le déclarer au début avant l'initialisation. Donnez lui un nom simple et clair, ce sera votre fenêtre principale ! :

GtkWidget * MainWindow = NULL;

Ensuite, vous allez assigner une nouvelle fenêtre à ce pointeur avec la fonction gtk_window_new qui prend en paramètre le type de fenêtre à créer.

GtkWidget* gtk_window_new(GtkWindowType type);

Les deux types possibles sont :

  • GTK_WINDOW_TOPLEVEL : Une fenêtre normale.

  • GTK_WINDOW_POPUP : Une fenêtre sans bordure (ne pas utiliser, sauf cas particuliers).

Vous allez donc faire :

MainWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);

On dit que gtk_window_new est le constructeur de GtkWindow.

Afficher la fenêtre

Pour afficher un widget, on utilise la fonction gtk_widget_show, quel que soit le widget !

void gtk_widget_show(GtkWidget *widget);

Plus tard vous pourrez aussi utiliser gtk_widget_show_all, cette fonction permet de montrer tous les widgets qui on été insérés dans un GtkContainer (GtkWindow dérive de GtkContainer) :

void gtk_widget_show_all(GtkWidget *widget);
Evènements

Un cours sera dédié à la programmation événementielle mais il va bien falloir que je vous donne un bonus en attendant. Vous allez donc me faire confiance et rajouter cette ligne après la création de la fenêtre :

g_signal_connect(G_OBJECT(MainWindow), "delete-event", G_CALLBACK(gtk_main_quit), NULL);

Et juste avant le return, vous rajoutez cet appel de fonction :

gtk_main();

Bon, vous l'avez mérité, voilà le code complet. Essayer d'apprendre le nom des fonctions et de ne pas faire trop de copier/coller ! :)

#include <stdlib.h>
#include <gtk/gtk.h>
int main(int argc, char **argv)
{
/* Variables */
GtkWidget * MainWindow = NULL;
/* Initialisation de GTK+ */
gtk_init(&argc, &argv);
/* Création de la fenêtre */
MainWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(G_OBJECT(MainWindow), "delete-event", G_CALLBACK(gtk_main_quit), NULL);
/* Affichage et boucle évènementielle */
gtk_widget_show(MainWindow);
gtk_main();
/* On quitte.. */
return EXIT_SUCCESS;
}
Fenetre GTK

Compilez et exécutez !!! Voilà votre première fenêtre qui s'ouvre sous vos yeux, quelle émotion, ..., non ? ^^
Bon; c'est pas super beau, c'est vide, mais bon c'est une fenêtre ! C'est tout ce qu'on a demandé !

Nous allons maintenant l'améliorer, lui donner un titre, une taille, et la mettre ou on veut ! :)

Personnaliser notre fenêtre

La partie que vous attendiez, on va pouvoir s'amuser ! :p
Je vais vous présenter pleins de fonctions intéressantes pour vos fenêtres. Certaines ne vous serviront pas pour le moment mais si vous en avez besoin, vous savez qu'elles sont là !

La quasi-totalité de ces fonctions vous demanderont un GtkWindow à la place d'un GtkWidget. La solution consiste à utiliser la macro GTK_NOM_DU_WIDGET, autrement dit, GTK_WINDOW(). N'oubliez pas, car sinon, vous aurez des erreurs dans la console (c'est pas beau, et ça peut créer des super bugs incompréhensibles).

Le titre

Fonction pour définir le titre : on donne la fenêtre et une chaîne de caractères :

void gtk_window_set_title(GtkWindow *window, const gchar *title);

Il faut donc faire ainsi :

gtk_window_set_title(GTK_WINDOW(MainWindow), "Fenetre de zer0");

Fonction pour récupérer le titre : La fonction retourne un gchar*. C'est le char de la GLib, je vous conseille ce type pour utiliser Gtk, sinon vous aurez toujours pleins de Warnings ! ;)

const gchar* gtk_window_get_title(GtkWindow *window);

Attention, vous devez bien stocker le retour de cette fonction dans un const gchar, car le pointeur retourné pointe directement là ou GTK+ stocke le titre de la fenêtre : vous ne devez par conséquent pas libérer cette chaine.

La taille (par défaut)

Définir la taille par défaut : On donne la fenêtre (avec la macro) et les dimensions voulues.

void gtk_window_set_default_size(GtkWindow *window, gint width, gint height);

Recupérer la taille par défaut : On donne deux pointeurs gint (int de la Glib, comme le gchar !) et Gtk les modifie pour vous donner la taille. :)

void gtk_window_get_default_size (GtkWindow *window, gint *width, gint *height);

La taille par défaut sera utilisée si elle est assez grande pour contenir tous les widgets, sinon, elle sera agrandie automatiquement.

La taille (actuelle)

Définir la taille actuelle :

void gtk_window_resize (GtkWindow *window, gint width, gint height);

Récupérer la taille actuelle :

void gtk_window_get_size (GtkWindow *window, gint *width, gint *height);

Les paramètres sont les mêmes que pour la taille par défaut.

Positionnement

Fonction pour positionner la fenêtre : On passe un GtkWindowPosition. Il y a les possibilités suivantes :

  • GTK_WIN_POS_NONE : Position aléatoire.

  • GTK_WIN_POS_MOUSE : Position de la souris au moment de l'appel.

  • GTK_WIN_POS_CENTER : Fenêtre centrée.

  • GTK_WIN_POS_CENTER_ALWAYS : Toujours centrée, non déplaçable.

  • GTK_WIN_POS_CENTER_ON_PARENT : Centrée par rapport à la fenêtre parente.

void gtk_window_set_position(GtkWindow *window, GtkWindowPosition position);

Ou avec la position en x et y :

void gtk_window_move(GtkWindow *window, gint x, gint y);

Fonction pour récupérer la position : Voir la fonction pour la taille.

void gtk_window_get_position(GtkWindow *window, gint *root_x, gint *root_y);

L'icône

Définir l'icône : On donne la fenêtre, le nom du fichier, et un pointeur sur GError pour gérer les erreurs (ou NULL). La fonction renvoie TRUE si l'icône a pu être définie à la fenêtre.

gboolean gtk_window_set_icon_from_file (GtkWindow *window, const gchar *filename, GError **err);

Vous pouvez aussi définir l'icône à partir du GdkPixbuf, avec la fonction gtk_window_set_icon.

Récupérer l'icône : donnez juste votre fenêtre, la fonction renvoie un GdkPixbuf.

GdkPixbuf * gtk_window_get_icon (GtkWindow *window);

Comme son nom l'indique, le GdkPixbuf est un buffer de pixel, on l'utilise pour manipuler les images avec GTK+. Nous apprendrons à nous en servir plus tard.

Iconifier

Iconifier :

void gtk_window_iconify (GtkWindow *window);

Restaurer :

void gtk_window_deiconify (GtkWindow *window);

Maximiser

Maximiser :

void gtk_window_maximize (GtkWindow *window);

Restaurer :

void gtk_window_unmaximize (GtkWindow *window);

Bouton "fermer"

Définir l'état du bouton fermer : Donnez TRUE pour activer le bouton, FALSE pour le désactiver.

void gtk_window_set_deletable (GtkWindow *window, gboolean setting);

Connaître l'état : La fonction renvoie TRUE si le bouton est actif, FALSE sinon.

gboolean gtk_window_get_deletable (GtkWindow *window);

Les bordures

Définir l'épaisseur des bordures : Donnez la fenêtre et l'épaisseur des bordures en pixels, dans l'ordre gauche, haut, droite, bas.

void gtk_window_set_frame_dimensions (GtkWindow *window, gint left, gint top, gint right, gint bottom);

Connaître l'épaisseur : Mêmes paramètres, mais en pointeurs pour que vous puissiez y avoir accès.

void gtk_window_get_frame_dimensions (GtkWindow *window, gint *left, gint *top, gint *right, gint *bottom);

Exemple

Pour que vous compreniez bien l'utilisation de ces fonctions, voici un exemple de code personnalisant une Fenêtre, il utilise plusieurs fonctions vue ci-dessus : Télécharger le code exemple.

Vous savez maintenant créer des fenêtres GTK+ et les personnaliser ! :)

Dans le prochain chapitre, vous allez apprendre à utiliser les labels pour pouvoir afficher du texte dans vos fenêtres ! ;)
Enjoy !

Example of certificate of achievement
Example of certificate of achievement