Didacticiel: Widgets non documentés

Page suivante Page précédente Table des matières

## 12. Widgets non documentés

On a besoin de leurs auteurs! :). Participez à notre didacticiel.

Si vous devez utiliser un de ces widgets non documentés, je vous recommande fortement de consulter leurs fichiers en-têtes respectifs dans la distribution GTK. Les noms de fonctions du GTK sont très parlantes. Lorsque vous avez compris comment les choses fonctionnent, il n'est pas difficile de savoir comment utiliser un widget à partir des déclarations de ses fonctions. Cela, avec quelques exemples de codes pris ailleurs, devrait ne pas poser de problème.

Lorsque vous avez compris toutes les fonctions d'un nouveau widget non documenté, pensez à écrire un didacticiel pour que les autres puissent bénéficier du temps que vous y avez passé.

## 12.1 Entrées de texte

## 12.2 Sélections de couleurs

## 12.3 Contrôle d'intervalle

## 12.4 Règles

## 12.5 Boîtes de texte

## 12.6 Prévisualisation

(Ceci peut devoir être réécrit pour suivre le style du reste de ce didacticiel).

> `

>     Les prévisualisateurs servent à plusieurs choses dans GIMP/GTK. La
>     plus importante est celle-ci : les images de haute qualité peuvent
>     occuper des dizaines de mega-octets en mémoire - facilement ! Toute
>     opération sur une image aussi grosse implique un temps de traitement
>     élevé. Si cela vous prend 5 à 10 essais (i.e. 10 à 20 étapes puisque
>     vous devez recommencer lorsque vous avez fait une erreur) pour choisir
>     la bonne modification, cela prendra littéralement des heures pour
>     produire la bonne image - pour peu que vous ne manquiez pas de mémoire
>     avant. Ceux qui on passé des heures dans les chambres noires de
>     développement couleur connaissent cette sensation. Les
>     prévisualisations sont notre planche de salut !

>     L'aspect pénible de l'attente n'est pas le seul problème. souvent, il
>     est utile de comparer les versions « Avant » et « Après » côte à côte
>     ou, au pire l'une après l'autre. Si vous travaillez avec de grosses
>     images et des attentes de 10 secondes, l'obtention des versions «
>     Avant » et « Après » est, pour le moins, difficile. Pour des images de
>     30Mo (4"x6", 600dpi, 24 bits), la comparaison côte à côte est
>     impossible pour la plupart des gens, et la comparaison séquentielle
>     n'est guère mieux. Les prévisualisations sont notre planche de salut !

>     Mais il y a plus. Les prévisualisations permettent les
>     pré-prévisualisations côte à côte. En d'autres termes, vous écrivez un
>     plug-in (par exemple la simulation filterpack) qui aurait plusieurs
>     prévisualisations de ce-que-ce-serait-si-vous-faisiez-ceci. Une approche
>     comme celle ci agit comme une sorte de palette de prévisualisation et
>     est très pratique pour les petites modifications. Utilisons les
>     prévisualisations !

>     Encore plus : pour certains plug-ins une intervention humaine en
>     temps réel, spécifique aux images, peut s'avérer nécessaire. Dans le
>     plug-in SuperNova, par exemple, on demande à l'utilisateur d'entrer
>     les coordonnées du centre de la future supernova. La façon la plus
>     simple de faire cela, vraiment, est de présenter une prévisualisation
>     à l'utilisateur et de lui demander de choisir interactivement le
>     point. Utilisons les prévisualisations !

>     Enfin, quelques utilisations diverses : on peut utiliser les
>     prévisualisations, même lorsqu'on ne travaille pas avec de grosses
>     images. Elles sont utiles, par exemple, lorsqu'on veut avoir un rendu
>     de motifs complexes. (Testez le vénérable plug-in Diffraction et
>     d'autres !). Comme autre exemple, regardez le plug-in de rotation de
>     couleur (travail en cours). Vous pouvez aussi utiliser les
>     prévisualisations pour des petits logos dans vos plug-ins et même pour
>     une photo de vous, l'Auteur. Utilisons les prévisualisations !

>     Quand ne pas utiliser les prévisualisations

>     N'utilisez pas les prévisualisations pour les graphes, les tracés,
>     etc. GDK est bien plus rapide pour ça. N'utilisez les que pour les
>     images !

>     Utilisons les prévisualisations !

>     Vous pouvez mettre une prévisualisation dans à peu près n'importe
>     quoi. Dans une vbox, une hbox, un bouton, etc. Mais elles donnent leur
>     meilleur d'elles-mêmes dans des cadres resserrés autour d'elles. Les
>     prévisualisations n'ont, par elles-mêmes, aucun contour et semblent
>     plates sans eux. (Bien sûr, si c'est cet aspect que vous
>     voulez...). Les cadres serrés fournissent les bordures nécessaires.

>                                    [Image][Image]

>     Les prévisualisations sont, à bien des égards, comme tous les autres
>     widgets de GTK (avec tout ce que cela implique) sauf qu'il disposent
>     d'une fonctionnalité supplémentaire : ils doivent être remplis
>     avec une image ! Nous traiterons d'abord exclusivement de l'aspect GTK
>     des prévisualisations, puis nous verrons comment les remplir.

>                                   /* Création d'un widget prévisualisation,
>                                    * configuration de sa taille et affichage */
>     GtkWidget *preview;
>     preview=gtk_preview_new(GTK_PREVIEW_COLOR)
>                                   /* Autre option :
>                                   GTK_PREVIEW_GRAYSCALE);*/

>     gtk_preview_size (GTK_PREVIEW (preview), WIDTH, HEIGHT);
>     gtk_widget_show(preview);
>     my_preview_rendering_function(preview);

>     Ah oui, comme je le disais, les prévisualisations rendent mieux dans
>     des cadres :

>     GtkWidget *create_a_preview(int        Width,
>                                 int        Height,
>                                 int        Colorfulness)
>     {
>       GtkWidget *preview;
>       GtkWidget *frame;

>       frame = gtk_frame_new(NULL);
>       gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
>       gtk_container_border_width (GTK_CONTAINER(frame),0);
>       gtk_widget_show(frame);

>       preview=gtk_preview_new (Colorfulness?GTK_PREVIEW_COLOR
>                                            :GTK_PREVIEW_GRAYSCALE);
>       gtk_preview_size (GTK_PREVIEW (preview), Width, Height);
>       gtk_container_add(GTK_CONTAINER(frame),preview);
>       gtk_widget_show(preview);

>       my_preview_rendering_function(preview);
>       return frame;
>     }

>     Ceci est ma prévisualisation de base. Cette fonction retourne le cadre
>     « père », on peut ainsi le placer ailleurs dans notre interface. Bien
>     sûr, on peut passer le cadre « père » en paramètre à cette
>     fonction. Dans de nombreuses situations, toutefois, le contenu de la
>     prévisualisation est changée continuellement par notre application. En
>     ce cas, on peut passer un pointeur vers une prévisualisation à la
>     fonction <em/create_a_preview()/ et avoir ainsi un contrôle sur elle
>     plus tard.

>     Un point plus important qui pourra un jour vous faire économiser
>     beaucoup de temps. Quelques fois, il est souhaitable de mettre un
>     label à votre prévisualisation. Par exemple, on peut nommer la
>     prévisualisation contenant l'image originale « Original » et celle
>     contenant l'image modifiée « Moins Originale ». Il peut vous arriver
>     de placer la prévisualisation avec le label approprié dans une
>     vbox. L'effet inattendu est que si le label est plus large que la
>     prévisualisation (taille de cette dernière, taille de la fonte du
>     label, etc), le cadre s'élargit et ne convient plus à la
>     prévisualisation. Le même problème se passera probablement dans
>     d'autres situations aussi.

>                                        [Image]

>     La solution consiste à placer la prévisualisation et le label dans une
>     table de 2x2 en les attachant avec les paramètres suivants (c'est
>     l'une des possibilités, bien sûr. La clé consiste à ne pas mettre
>     GTK_FILL dans le second attachement)«nbsp;:

>     gtk_table_attach(GTK_TABLE(table),label,0,1,0,1,
>                      0,
>                      GTK_EXPAND|GTK_FILL,
>                      0,0);
>     gtk_table_attach(GTK_TABLE(table),frame,0,1,1,2,
>                      GTK_EXPAND,
>                      GTK_EXPAND,
>                      0,0);

>     Et voici le résultat :

>                                        [Image]

>     Divers

>     Rendre une prévisualisation cliquable se fait très facilement en la plaçant dans un bouton. Cela ajoute aussi une bordure agréable autour de la prévisualisation et vous n'avez même pas besoin de la mettre dans un cadre.  Voir le plug-in Filter Pack Simulation comme exemple.

>     Remplir une prévisualisation

>     Afin de nous familiariser avec les bases de ce remplissage, créons le motif suivant :

>                                        [Image]

>     void
>     my_preview_rendering_function(GtkWidget     *preview)
>     {
>     #define SIZE 100
>     #define HALF (SIZE/2)

>       guchar *row=(guchar *) malloc(3*SIZE); /* 3 bits par point */
>       gint i, j;                             /* Coordonnées      */
>       double r, alpha, x, y;

>       if (preview==NULL) return; /* J'ajoute généralement ceci quand je */
>                                  /* veux éviter des plantages stupides  */
>                                  /* Vous devez vous assurer que tout a  */
>                                  /* été correctement initialisé !       */

>       for (j=0; j < ABS(cos(2*alpha)) ) { /* Sommes-nous dans la forme ? */
>                                              /* glib.h contient ABS(x).   */
>             row[i*3+0] = sqrt(1-r)*255;      /* Definit rouge             */
>             row[i*3+1] = 128;                /* Definit vert              */
>             row[i*3+2] = 224;                /* Definit bleu              */
>           }                                  /* "+0" est pour l'alignement ! */
>           else {
>             row[i*3+0] = r*255;
>             row[i*3+1] = ABS(sin((float)i/SIZE*2*PI))*255;
>             row[i*3+2] = ABS(sin((float)j/SIZE*2*PI))*255;
>           }
>         }
>         gtk_preview_draw_row( GTK_PREVIEW(preview),row,0,j,SIZE);

>         /* Insère "row" dans "preview" en partant du point de */
>         /* coordonnées (0,j) première colonne, j_ième ligne allant de SIZE */
>         /* pixels vers la droite */
>       }

>       free(row); /* on récupère un peu d'espace */
>       gtk_widget_draw(preview,NULL); /* qu'est-ce que ça fait ? */
>       gdk_flush(); /* et ça ?  */
>     }

>     Ceux qui n'utilisent pas GIMP en ont suffisamment vu pour faire
>     déjà beaucoup de choses. Pour ceux qui l'utilisent, j'ai quelques
>     précisions à ajouter.

>     Prévisualisation d'image

>     Il est pratique de conserver une version réduite de l'image ayant
>     juste assez de pixels pour remplir la prévisualisation. Ceci est
>     possible en choisissant chaque énième pixel où n est le ratio de la
>     taille de l'image par rapport à la taille de la visualisation. Toutes
>     les opérations suivantes (y compris le remplissage des
>     prévisualisations) sont alors réalisées seulement sur le nombre réduit
>     de pixels. Ce qui suit est mon implantation de la réduction d'image
>     (Gardez à l'esprit que je n'ai que quelques notions de base en C !).

>     (ATTENTION : CODE NON TESTÉ !!!)

>     typedef struct {
>       gint      width;
>       gint      height;
>       gint      bbp;
>       guchar    *rgb;
>       guchar    *mask;
>     } ReducedImage;

>     enum {
>       SELECTION_ONLY,
>       SELCTION_IN_CONTEXT,
>       ENTIRE_IMAGE
>     };

>     ReducedImage *Reduce_The_Image(GDrawable *drawable,
>                                    GDrawable *mask,
>                                    gint LongerSize,
>                                    gint Selection)
>     {
>       /* Cette fonction réduit l'image à la taille de prévisualisation choisie */
>       /* La taille de la prévisualisation est déterminée par LongerSize, i.e.  */
>       /* la plus grande des deux dimensions. Ne fonctionne qu'avec des images  */
>       /* RGB !                                                                 */

>       gint RH, RW;          /* Hauteur et Largeur réduites                     */
>       gint width, height;   /* Largeur et Hauteur de la surface à réduire      */
>       gint bytes=drawable->bpp;
>       ReducedImage *temp=(ReducedImage *)malloc(sizeof(ReducedImage));

>       guchar *tempRGB, *src_row, *tempmask, *src_mask_row,R,G,B;
>       gint i, j, whichcol, whichrow, x1, x2, y1, y2;
>       GPixelRgn srcPR, srcMask;
>       gint NoSelectionMade=TRUE; /* Suppose que l'on traite l'image entière    */

>       gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
>       width  = x2-x1;
>       height = y2-y1;
>       /* S'il y a une SELECTION, on récupère ses frontières ! */

>       if (width != drawable->width && height != drawable->height)
>         NoSelectionMade=FALSE;
>       /* On vérifie si l'utilisateur a rendu une sélection active         */
>       /* Ceci sera important plus tard, lorsqu'on créera un masque réduit */

>       /* Si on veut prévisualiser l'image entière, supprimer ce qui suit ! */
>       /* Bien sûr, s'il n'y a pas de sélection, cela n'a aucun effet !     */
>       if (Selection==ENTIRE_IMAGE) {
>         x1=0;
>         x2=drawable->width;
>         y1=0;
>         y2=drawable->height;
>       }

>       /* Si on veut prévisualiser une sélection avec une surface qui l'entoure, */
>       /* on doit l'agrandir un petit peu. Considérez ça comme une devinette.   */

>       if (Selection==SELECTION_IN_CONTEXT) {
>         x1=MAX(0,                x1-width/2.0);
>         x2=MIN(drawable->width,  x2+width/2.0);
>         y1=MAX(0,                y1-height/2.0);
>         y2=MIN(drawable->height, y2+height/2.0);
>       }

>       /* Calcul de la largeur et de la hauteur de la surface à réduire. */

>       width  = x2-x1;
>       height = y2-y1;

>       /* Les lignes ci-dessous déterminent la dimension qui sera le coté */
>       /* le plus long. Cette idée est empruntée au plug-in Supernova.    */
>       /* Je soupçonne que j'aurais pu y penser moi-même, mais la vérité  */
>       /* doit être dite. Le plagiat pue !                                */

>       if (width>height) {
>         RW=LongerSize;
>         RH=(float) height * (float) LongerSize/ (float) width;
>       }
>       else {
>         RH=LongerSize;
>         RW=(float)width * (float) LongerSize/ (float) height;
>       }

>       /* L'image entière est réduite dans une chaîne ! */

>       tempRGB   = (guchar *) malloc(RW*RH*bytes);
>       tempmask  = (guchar *) malloc(RW*RH);

>       gimp_pixel_rgn_init (&srcPR, drawable, x1, y1, width, height, FALSE, FALSE);
>       gimp_pixel_rgn_init (&srcMask, mask, x1, y1, width, height, FALSE, FALSE);

>       /* Réservation pour sauver une ligne d'image et une ligne du masque */
>       src_row       = (guchar *) malloc (width*bytes);
>       src_mask_row  = (guchar *) malloc (width);

>       for (i=0; i < RH; i++) {
>         whichrow=(float)i*(float)height/(float)RH;
>         gimp_pixel_rgn_get_row (&srcPR, src_row, x1, y1+whichrow, width);
>         gimp_pixel_rgn_get_row (&srcMask, src_mask_row, x1, y1+whichrow, width);

>         for (j=0; j < RW; j++) {
>           whichcol=(float)j*(float)width/(float)RW;

>           /* Pas de sélection = chaque point est complètement sélectionné ! */

>           if (NoSelectionMade)
>             tempmask[i*RW+j]=255;
>           else
>             tempmask[i*RW+j]=src_mask_row[whichcol];

>           /* Ajout de la ligne à la longue chaîne qui contient maintenant */
>           /* l'image !                                                    */

>           tempRGB[i*RW*bytes+j*bytes+0]=src_row[whichcol*bytes+0];
>           tempRGB[i*RW*bytes+j*bytes+1]=src_row[whichcol*bytes+1];
>           tempRGB[i*RW*bytes+j*bytes+2]=src_row[whichcol*bytes+2];

>           /* On s'accroche aussi à l'alpha */
>           if (bytes==4)
>             tempRGB[i*RW*bytes+j*bytes+3]=src_row[whichcol*bytes+3];
>         }
>       }
>       temp->bpp=bytes;
>       temp->width=RW;
>       temp->height=RH;
>       temp->rgb=tempRGB;
>       temp->mask=tempmask;
>       return temp;
>     }

>     La suite est une fonction de prévisualisation qui utilise le même type
>     <em/ReducedImage/ !  On remarque qu'elle utilise une fausse
>     transparence (au moyen de <em/fake_transparancy/ qui est défini comme
>     suit :

>     gint fake_transparency(gint i, gint j)
>     {
>       if ( ((i%20)- 10) * ((j%20)- 10)>0   )
>         return 64;
>       else
>         return 196;
>     }

>     Voici maintenant la fonction de prévisualisation«nbsp;:

>     void
>     my_preview_render_function(GtkWidget     *preview,
>                                gint          changewhat,
>                                gint          changewhich)
>     {
>       gint Inten, bytes=drawable->bpp;
>       gint i, j, k;
>       float partial;
>       gint RW=reduced->width;
>       gint RH=reduced->height;
>       guchar *row=malloc(bytes*RW);;

>       for (i=0; i < RH; i++) {
>         for (j=0; j < RW; j++) {

>           row[j*3+0] = reduced->rgb[i*RW*bytes + j*bytes + 0];
>           row[j*3+1] = reduced->rgb[i*RW*bytes + j*bytes + 1];
>           row[j*3+2] = reduced->rgb[i*RW*bytes + j*bytes + 2];

>           if (bytes==4)
>             for (k=0; k<3; k++) {
>               float transp=reduced->rgb[i*RW*bytes+j*bytes+3]/255.0;
>               row[3*j+k]=transp*a[3*j+k]+(1-transp)*fake_transparency(i,j);
>             }
>         }
>         gtk_preview_draw_row( GTK_PREVIEW(preview),row,0,i,RW);
>       }

>       free(a);
>       gtk_widget_draw(preview,NULL);
>       gdk_flush();
>     }

>     Fonctions applicables

>     guint           gtk_preview_get_type           (void);
>     /* Aucune idée */
>     void            gtk_preview_uninit             (void);
>     /* Aucune idée */
>     GtkWidget*      gtk_preview_new                (GtkPreviewType   type);
>     /* Décrite ci-dessous */
>     void            gtk_preview_size               (GtkPreview      *preview,
>                                                     gint             width,
>                                                     gint             height);
>     /* Permet de changer la taille d'une prévisualisation existante */
>     /* Apparamment, il y a un bug dans GTK qui rend ce traitement   */
>     /* hasardeux. Une méthode pour corriger ce problème consiste à  */
>     /* changer manuellement la taille de la fenêtre contenant la    */
>     /* prévisualisation après avoir changé la taille de la          */
>     /* prévisualisation.                                            */

>     void            gtk_preview_put                (GtkPreview      *preview,
>                                                     GdkWindow       *window,
>                                                     GdkGC           *gc,
>                                                     gint             srcx,
>                                                     gint             srcy,
>                                                     gint             destx,
>                                                     gint             desty,
>                                                     gint             width,
>                                                     gint             height);
>     /* Aucune idée */

>     void            gtk_preview_put_row            (GtkPreview      *preview,
>                                                     guchar          *src,
>                                                     guchar          *dest,
>                                                     gint             x,
>                                                     gint             y,
>                                                     gint             w);
>     /* Aucune idée */

>     void            gtk_preview_draw_row           (GtkPreview      *preview,
>                                                     guchar          *data,
>                                                     gint             x,
>                                                     gint             y,
>                                                     gint             w);
>     /* Décrite dans le texte */

>     void            gtk_preview_set_expand         (GtkPreview      *preview,
>                                                     gint             expand);
>     /* Aucune idée */

>     /* Aucune piste pour celles qui suivent mais devrait être */
>     /* un standard pour la plupart des widgets.               */

>     void            gtk_preview_set_gamma          (double           gamma);
>     void            gtk_preview_set_color_cube     (guint            nred_shades,
>                                                     guint            ngreen_shades,
>                                                     guint            nblue_shades,
>                                                     guint            ngray_shades);
>     void            gtk_preview_set_install_cmap   (gint             install_cmap);
>     void            gtk_preview_set_reserved       (gint             nreserved);
>     GdkVisual*      gtk_preview_get_visual         (void);
>     GdkColormap*    gtk_preview_get_cmap           (void);
>     GtkPreviewInfo* gtk_preview_get_info           (void);

>     That's all, folks!

> `

## 12.7 Courbes

Page suivante Page précédente Table des matières