# Image viewer (C)

Un peu plus qu'une simple application Gtk « Hello world ».
Projet de Documentation GNOME
gnome-doc-list@gnome.org
Johannes Schmid
jhs@gnome.org
Marta Maria Casetti
mmcasetti@gmail.com 2013
Luc Rebert,
traduc@rebert.name 2011
Alain Lojewski,
allomervan@gmail.com 2011-2012
Luc Pionchon
pionchon.luc@gmail.com 2011
Bruno Brouard
annoa.b@gmail.com 2011-12
Luis Menina
liberforce@freeside.fr 2014

# Image viewer

      Dans ce tutoriel, vous allez apprendre :

        quelques concepts de base de la programmation en C/GObject,
        comment écrire une application GTK en C.

# Création d'un projet dans Anjuta

Avant de commencer à programmer, vous devez ouvrir un nouveau projet dans Anjuta. Ceci crée tous les fichiers qui vous sont nécessaires pour construire et exécuter votre programme plus tard. C'est aussi utile pour tout regrouper en un seul endroit.

Lancez Anjuta et cliquez sur FichierNouveauProjet pour ouvrir l'assistant de création de projet.

Choose GTK+ (Simple) from the C tab, click Continue, and fill out your details on the next few pages. Use image-viewer as project name and directory.

Assurez-vous d'avoir désactivé Utiliser GtkBuilder pour l'interface utilisateur car nous allons créer l'interface utilisateur manuellement dans cet exemple. Consultez le tutoriel Guitar-Tuner pour un exemple d'utilisation du constructeur d'interfaces.

Cliquez sur Appliquer et votre projet est créé. Ouvrez src/main.c depuis l'onglet Projet ou l'onglet Fichiers. Vous devez voir apparaître du code commençant par les lignes :

#include <config.h> #include <gtk/gtk.h>

# Première construction du programme

« C » est un langage plutôt verbeux, donc ne soyez pas surpris par la quantité de code que contient le fichier. La plupart du code est générique. Il charge une fenêtre (vide) et l'affiche. Vous trouverez plus de détails ci-dessous ; passez cette liste si vous comprenez les bases :

Les trois lignes `#include` du haut incorporent les bibliothèques `config` (définitions utiles pour la construction autoconf), `gtk` (interface utilisateur) et `gi18n` (pour internationalisation). Les fonctions de ces bibliothèques seront utilisées dans le reste du programme.

La fonction `create_window` crée une nouvelle fenêtre (vide) et connecte un signal pour quitter l'application quand la fenêtre est fermée.

Connecter des signaux, c'est décider de ce qui doit se passer quand on appuie sur un bouton ou quand quelque chose d'autre se produit. Ici, la fonction `destroy` est appelée (et quitte l'application) quand la fenêtre est fermée.

La fonction `main` est exécutée par défaut quand vous lancez une application C. Elle appelle d'autres fonctions qui configurent puis lancent l'application. La fonction `gtk_main` démarre la boucle principale de GTK, qui lance l'interface utilisateur et commence à écouter les événements (comme des clics de souris ou des appuis sur des touches).

La définition conditionnelle `ENABLE_NLS` configure `gettext` qui est un environnement pour la traduction des applications. Ces fonctions définissent la façon dont les objets de traduction doivent prendre en charge votre application quand vous l'exécutez.

Le programme est prêt à être utilisé, donc vous pouvez le compiler en cliquant sur ConstruireConstruire le projet ou en appuyant sur MajF7.

Cliquez sur Exécuter dans la fenêtre suivante pour configurer une construction avec débogage. Vous ne devez le faire qu'une seule fois, lors de la première exécution.

# Création de l'interface utilisateur

Nous allons maintenant donner vie à la fenêtre vide. GTK organise l'interface utilisateur avec des `GtkContainer` qui peuvent contenir d'autres éléments graphiques et même d'autres conteneurs. Ici, nous allons utiliser le plus simple conteneur disponible, une boîte `GtkBox` :

static GtkWidget* create_window (void) { GtkWidget *window; GtkWidget *button; GtkWidget *image; GtkWidget *box;

/* Set up the UI */ window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_title (GTK_WINDOW (window), "image-viewer-c");

box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5); button = gtk_button_new_with_label (_("Open image")); image = gtk_image_new ();

gtk_box_pack_start (GTK_BOX (box), image, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);

gtk_container_add (GTK_CONTAINER (window), box);

/* Connect signals */

/* Show open dialog when opening a file */ g_signal_connect (button, "clicked", G_CALLBACK (on_open_image), image);

/* Exit when the window is closed */ g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);

return window; }

Les premières lignes créent les éléments graphiques que nous voulons utiliser : un bouton pour pouvoir ouvrir une image, l'élément graphique image lui-même et la boîte qui nous sert de conteneur. Nous utilisons des macros comme `GTK_BOX` pour contrôler et convertir de manière dynamique les types d'objet ce qui est nécessaire car le C n'est pas prévu nativement pour prendre en charge la programmation orientée objets.

Les appels à `gtk_box_pack_start` ajoutent les deux éléments graphiques à la boîte et définissent leur comportement. L'image va s'étendre pour occuper tout l'espace disponible alors que le bouton va prendre juste la taille nécessaire. Notez que nous ne définissons pas explicitement les dimensions des éléments graphiques. Avec GTK, ce n'est habituellement pas nécessaire car cela facilite grandement l'obtention d'une bonne disposition pour différentes tailles de fenêtre. La boîte est ensuite ajoutée dans la fenêtre.

Il nous faut définir ce qui se passe quand l'utilisateur clique sur le bouton. GTK utilise le concept de signaux. Quand le bouton est cliqué, il envoie le signal clicked qu'il est possible de relier à une action. Cela a été réalisé avec la méthode `g_signal_connect` qui commande à GTK d'appeler la fonction `on_open_image` quand le bouton est cliqué et de transmettre l'image à cette fonction comme argument additionnel. Nous définirons la fonction de rappel à la section suivante.

Le dernier signal `g_signal_connect()` assure que l'application quitte quand la fenêtre est fermée.

Pour finir, assurez-vous de remplacer l'appel `gtk_widget_show` de la fonction principale `main()` par `gtk_widget_show_all()` pour afficher la fenêtre et tous les éléments graphiques qu'elle contient.

# Affichage de l'image

Nous allons maintenant définir le gestionnaire de signal pour le signal clicked du bouton ci-dessus. Ajoutez ce code avant la méthode `create_window()`.

static void on_open_image (GtkButton* button, gpointer user_data) { GtkWidget *image = GTK_WIDGET (user_data); GtkWidget *toplevel = gtk_widget_get_toplevel (image); GtkFileFilter *filter = gtk_file_filter_new (); GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Open image"), GTK_WINDOW (toplevel), GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);

gtk_file_filter_add_pixbuf_formats (filter); gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);

switch (gtk_dialog_run (GTK_DIALOG (dialog))) { case GTK_RESPONSE_ACCEPT: { gchar *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); gtk_image_set_from_file (GTK_IMAGE (image), filename); break; } default: break; } gtk_widget_destroy (dialog); }

C'est un peu plus compliqué que tout ce que nous avons essayé jusqu'à présent, donc décortiquons cette partie étape par étape :

Le premier argument du signal est toujours l'élément graphique qui envoie le signal. Il y a parfois d'autres arguments attachés au signal, mais clicked n'en a aucun. Ensuite vient l'argument `user_data` qui est un pointeur vers les données transmises en connectant le signal. Ici, c'est notre objet `GtkImage`.

La prochaine ligne intéressante est celle où est créée la boîte de dialogue pour choisir le fichier avec `gtk_file_chooser_dialog_new`. La fonction prend comme argument le titre et la fenêtre parente de la boîte de dialogue et plusieurs options telles que le numéro des boutons et leurs valeurs correspondantes.

Notez que nous utilisons les noms de bouton de la collection (stock) Gtk au lieu de saisir manuellement « Cancel » ou « Open ». L'avantage d'utiliser les noms de la collection est que les étiquettes des boutons seront déjà traduites dans la langue de l'utilisateur.

Les deux lignes suivantes limitent la boîte de dialogue Open à l'affichage des seuls fichiers pouvant être ouverts par `GtkImage`. Un objet filtre est d'abord créé ; ensuite nous ajoutons tous les types de fichier pris en charge par `GdkPixbuf` (ce qui inclut la plupart des formats d'image comme PNG ou JPEG) au filtre. Enfin, nous appliquons ce filtre à la boîte de dialogue Open.

`gtk_dialog_run` affiche la boîte de dialogue Open. La boîte de dialogue attend que l'utilisateur choisisse une image ; quand c'est fait, `gtk_dialog_run` retourne la valeur `GTK_RESPONSE_ACCEPT` (il retourne la valeur `GTK_RESPONSE_CANCEL` si l'utilisateur clique sur Cancel). L'instruction `switch` teste cette réponse.

Supposons que l'utilisateur a cliqué sur le bouton Ouvrir, la ligne suivante attribue le nom du fichier image choisi par l'utilisateur aux propriétés `file` du GtkImage. Ensuite, GtkImage charge et affiche l'image choisie.

Dans la dernière ligne de cette méthode, nous détruisons la boîte de dialogue Open car on n'en a plus besoin. La destruction masque automatiquement la boîte de dialogue.

# Construction et lancement de l'application

À ce stade, tout le programme est fonctionnel. Cliquez sur ConstruireConstruire le projet pour tout reconstruire et faites ExécuterExécuter pour lancer l'application.

Si ce n'est pas encore fait, sélectionnez l'application Debug/src/image-viewer dans la boîte de dialogue qui s'affiche et cliquez sur Exécuter. Amusez-vous bien !

# Implémentation de référence

Si vous rencontrez des difficultés avec ce tutoriel, comparez votre programme à ce programme de référence.

# Les étapes suivantes

Voici quelques idées sur la manière d'étendre ce simple exemple :

Faire que l'utilisateur puisse sélectionner un dossier plutôt qu'un fichier et fournir les contrôles pour naviguer parmi toutes les images d'un dossier.

Appliquer au hasard des filtres et des effets à l'image quand elle est chargée et permettre à l'utilisateur d'enregistrer l'image modifiée.

GEGL fournit de puissantes possibilités de manipulation d'image.

Permettre à l'utilisateur de charger des images depuis des sites de partage, des scanners ou d'autres sources plus sophistiquées.

You can use GIO to handle network file transfers and the like, and GNOME Scan to handle scanning.