Whole document tree
    

Whole document tree

GTK Tutorial: Widget non documentati Avanti Indietro Indice

12. Widget non documentati

Per questi sarebbe utile il contributo degli autori! :) Prendete in considerazione la possibilità di contribuire al nostro tutorial.

Se dovete usare uno di questi widget non documentati, vi suggeriamo caldamente di dare un'occhiata ai loro rispettivi file header nella distribuzione di GTK. I nomi delle funzioni di GTK sono molto descrittivi. Non appena si capisce come funzionano le cose, non è difficile dedurre il modo d'uso di un widget semplicemente guardando la dichiarazione di funzione associata ad esso. Aggiungendo a questo qualche spunto tratto dal codice di altri non dovrebbero esserci problemi.

Quando avrete raggiunto una comprensione globale di tutte le funzioni di un widget non documentato, considerate la possibilità di scrivere un tutorial su di esso, in modo che altri possano beneficiare del vostro lavoro.

12.1 Controlli di intervallo (Range Controls)

12.2 Anteprime

Le anteprime servono a un certo numero di cose in GIMP/GTK. La più importante è questa: a risoluzioni molto alte le immagini possono facilmente occupare diverse decine di megabyte di memoria; ogni operazione su immagini così grosse può richiedere molto tempo. Se per la scelta di una data modifica vi occorrono 5-10 tentativi (cioè 10-20 passi, poiché è necessario ripristinare l'originale se si è commesso un errore), possono volerci letteralmente delle ore per fare quella giusta - se non si rimane a corto di memoria prima! Coloro che hanno passato ore in camera oscura conoscono la sensazione. In questi casi le anteprime sono utilissime!

Ma la seccatura dell'attesa non è l'unico caso. Spesso è utile confrontare la versione precedente con la successiva affiancandole, o almeno alternandole. Se si sta lavorando con grandi immagini e ritardi di una decina di secondi un confronto efficace è quantomeno difficile da fare. Per immagini di 30 mega (4 pollici per 6 pollici, 600 punti per pollice, 24 bit) tale confronto risulta impraticabile per la maggior parte degli utenti. In questo caso le anteprime sono di grande aiuto!

Ma c'è di più. Con le anteprime è possibile scrivere plug-in per ottenere addirittura anteprime di anteprime (per esempio, la simulazione del pacchetto di filtri). Questi plug-in possono così fornire un certo numero di anticipazioni di quel che si otterrebbe applicando certe opzioni. Un simile approccio funziona come una tavolozza di anteprime, ed è molto efficace per piccoli cambiamenti!

Non è finita. Per alcuni plug-in può essere necessario un intervento umano in tempo reale specifico per ogni immagine. Nel plug-in SuperNova, ad esempio, vengono chieste le coordinate del centro della futura supernova. Il modo più semplice per fare questo è senza dubbio quello di mostrare un'anteprima all'utente chiedendogli di selezionare interattivamente il centro.

Infine, un paio di applicazioni tipiche. Le anteprime possono essere usate anche quando non si sta lavorando con grandi immagini. Per esempio, sono utili quando si stanno calcolando dei pattern complicati (date un'occhiata al venerabile plug in ``Diffraction'' e a molti altri!). Altro esempio: date un'occhiata al plug-in di rotazione della mappa dei colori (in allestimento). Le anteprime possono anche essere usate per visualizzare in un plug-in piccoli logo o, addirittura, l'immagine dell'Autore!

Quando non usare le anteprime

Le anteprime non vanno usate per grafici, disegni ecc., poiché per queste cose GDK è molto più veloce. Le anteprime vanno usate solo per immagini derivate da un'elaborazione!

Le anteprime possono essere inserite dappertutto. In un vbox, in un hbox, in una tabella, in un bottone, ecc. Sicuramente però hanno il loro look migliore se bordate con delle cornici (frame). Le anteprime non hanno bordi propri e appaiono piatte senza (naturalmente, se quel che si vuole è proprio un aspetto piatto...). I bordi possono essere creati con delle cornici.

[Image][Image]

Le anteprime sono per molti aspetti simili agli altri widget in GTK (con tutto ciò che questo implica), con l'eccezione di avere una caratteristica in più: è necessario che siano riempite con qualche tipo di immagine! Inizialmente parleremo solo dell'aspetto GTK delle anteprime e successivamente discuteremo di come riempirle.

Semplicemente:

                              /* Crea un widget di anteprima,
                                 inizializzane le dimensioni
                                 e visualizzalo */
GtkWidget *preview;
preview=gtk_preview_new(GTK_PREVIEW_COLOR)
                              /* Alternativamente:
                              GTK_PREVIEW_GRAYSCALE);*/
gtk_preview_size (GTK_PREVIEW (preview), WIDTH, HEIGHT);
gtk_widget_show(preview);
my_preview_rendering_function(preview);

Come già detto, le anteprime hanno un buon aspetto dentro le cornici, quindi:

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;
}

Questa è una semplice anteprima. Questa funzione restituisce la cornice ``madre'', in modo che sia possibile metterla in qualche altro posto nella vostra interfaccia. Naturalmente è possibile passare alla routine la cornice madre come parametro. In molte situazioni, comunque, il contenuto di un'anteprima viene aggiornato continuamente dall'applicazione; in questi casi potreste preferire passare alla funzione ``create_a_preview()'' un puntatore all'anteprima, ottenendone così il controllo dopo.

Un'avvertimento più importante che potrebbe un giorno risparmiarvi tanto tempo perso: a volte è preferibile etichettare le anteprime; ad esempio, è possibile etichettare l'anteprima contenente l'immagine originale come ``Originale'' e quella contenente l'immagine modificata come ``Modificata''. Potrebbe capitarvi di impacchettare in un vbox l'anteprima insieme con l'etichetta associata. L'insidia inattesa sta nel fatto che se l'etichetta è più ampia dell'anteprima (cosa che può accadere per una varietà di motivi da voi non prevedibili, come il fatto che la dimensione dell'anteprima viene decisa dinamicamente, o la dimensione del font), la cornice si espande e non risulta più perfettamente aderente all'anteprima. Questo stesso problema probabilmente può verificarsi anche in altre situazioni.

[Image]

La soluzione è quella di mettere l'anteprima e l'etichetta in una tabella 2x1 e di legarle insieme chiamando la funzione gtk_table_attach con i seguenti parametri (questa è una delle varianti possibili, naturalmente; l'importante è che non ci sia GTK_FILL nella seconda gtk_table_attach):

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);

Ed ecco il risultato:

[Image]

Altri suggerimenti

La maniera più semplice per rendere cliccabile un'anteprima è quella di metterla dentro un bottone. Questo ha anche l'effetto di aggiungere un bel bordo attorno all'anteprima, il che rende superfluo metterla in una cornice.

Questo è tutto per quel che riguarda GTK.

Completare un'anteprima

Per impratichirci con le basi del completamento delle anteprime, creiamo il seguente disegno (trovato per tentativi):

[Image]

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

  guchar *row=(guchar *) malloc(3*SIZE); /* 3 bits per dot */
  gint i, j;                             /* Coordinates    */
  double r, alpha, x, y;

  if (preview==NULL) return; /* Di solito aggiungo questo per  */  
                             /* evitare piantamenti stupidi.   */
                             /* Probabilmente bisognerebbe     */
                             /* assicurarsi che tutto sia stato*/
                             /* inizializzato con successo     */
  for (j=0; j < ABS(cos(2*alpha)) ) {    /* Siamo dentro la sagoma?   */
                                         /* glib.h contiene ABS(x).   */
        row[i*3+0] = sqrt(1-r)*255;      /* Definisce il Rosso        */
        row[i*3+1] = 128;                /* Definisce il Verde        */
        row[i*3+2] = 224;                /* Definisce il Blu          */
      }                                  /* "+0" è per allineamento   */
      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);
    /* Inserisce "row" in "preview" a partire del punto avente */
    /* coordinate (0,j) prima colonna, j-esima riga, per SIZE  */
    /* pixel verso destra */
  }

  free(row); /* libera un po' di memoria */
  gtk_widget_draw(preview,NULL); /* indovina cosa fa questo? */
  gdk_flush(); /* e questo? */
}
Coloro che non usano GIMP probabilmente hanno già visto abbastanza per fare molte cose. Per gli utenti GIMP c'è ancora qualcosa da aggiungere.

Anteprima dell'immagine

Probabilmente è opportuno tenere pronta una versione ridotta dell'immagine, grande quanto basta per riempire l'anteprima. Questo può essere fatto selezionando un pixel ogni n, dove n è il rapporto tra la dimensione dell'immagine e la dimensione dell'anteprima. Tutte le operazioni successive (compreso il riempimento dell'anteprima) sono fatte solo sul ridotto numero di pixel selezionati. Di seguito è riportata un'implementazione della riduzione dell'immagine (si tenga presente che ho preso solo lezioni basilari di C!).

(ATTENZIONE: CODICE NON VERIFICATO!!!)


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)
{
  /* Questa funzione riduce l'immagine alla dimens. scelta per l'anteprima */
  /* La dimensione dell'anteprima è determinata da LongerSize, cioè la più */
  /* grande delle dimensioni. Funziona solo per immagini RGB!              */
  gint RH, RW;          /* Altezza ridotta e larghezza ridotta             */
  gint width, height;   /* Larghezza e altezza dell'area da ridurre        */
  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; /* Assumiamo di trattare l'intera immagine    */                                         

  gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
  width  = x2-x1;
  height = y2-y1;
  /* Se c'è una SELEZIONE, ne abbiamo avuto gli estremi! */

  if (width != drawable->width && height != drawable->height)
    NoSelectionMade=FALSE;
  /* Controlliamo se l'utente ha una selezione attiva. Questo         */
  /* diventerà importante dopo, alla creazione di una maschera ridotta */

  /* Se si vuole l'anteprima dell'immagine intera, annulla quanto sopra */
  /* Naturalmente, in assenza di una selezione, questo non cambia nulla */
  if (Selection==ENTIRE_IMAGE) {
    x1=0;
    x2=drawable->width;
    y1=0;
    y2=drawable->height;
  }

  /* Se si vuole l'anteprima di una selezione con parte dell'area   */
  /* circostante bisogna espanderla un po'.                         */
  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);
  }

  /* Così si determinano larghezza e altezza dell'area da ridurre.   */
  width  = x2-x1;
  height = y2-y1;

  /* Le linee seguenti determinano quale dimensione deve essere  il  */
  /* lato più lungo. L'idea è presa dal plug-in supernova. Ritengo   */
  /* che avrei potuto pensarci da solo, ma la verità va detta.       */
  /* Brutta cosa il plagio!                                          */
  if (width>height) {
    RW=LongerSize;
    RH=(float) height * (float) LongerSize/ (float) width;
  }
  else {
    RH=LongerSize;
    RW=(float)width * (float) LongerSize/ (float) height;
  }

  /* L'intera immagine viene "stirata" in una stringa! */
  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);

  /* Prendine abbastanza da contenere una riga di immagine e una di maschera */
  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;

      /* Nessuna selezione = tutti i punti sono completamente selezionati */
      if (NoSelectionMade)
        tempmask[i*RW+j]=255;
      else
        tempmask[i*RW+j]=src_mask_row[whichcol];

      /* Aggiungi la riga alla lunga stringa che ora contiene l'immagine */
      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];

      /* Mantieni anche la trasparenza (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 seguente è una funzione di anteprima che usa lo stesso tipo ReducedImage! Si noti che usa una finta trasparenza - se ne è presente una, tramite fake_transparency che è definita come segue:

gint fake_transparency(gint i, gint j)
{
  if ( ((i%20)- 10) * ((j%20)- 10)>0   )
    return 64;
  else
    return 196;
}
E adesso la funzione per l'anteprima:
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();
}

Funzioni Applicabili

guint           gtk_preview_get_type           (void);
/* No idea */
void            gtk_preview_uninit             (void);
/* No idea */
GtkWidget*      gtk_preview_new                (GtkPreviewType   type);
/* Descritta precedentemente */
void            gtk_preview_size               (GtkPreview      *preview,
                                                gint             width,
                                                gint             height);
/* Permette di ridimensionare un'anteprima esistente */
/* Pare che un bug in GTK renda disordinato questo   */
/* processo. Un modo di rimettere le cose a posto    */
/* è quello di ridimensionare manualmente            */
/* la finestra contenente l'anteprima dopo aver      */
/* ridimensionato l'anteprima.                       */

void            gtk_preview_put                (GtkPreview      *preview,
                                                GdkWindow       *window,
                                                GdkGC           *gc,
                                                gint             srcx,
                                                gint             srcy,
                                                gint             destx,
                                                gint             desty,
                                                gint             width,
                                                gint             height);
/* No idea */

void            gtk_preview_put_row            (GtkPreview      *preview,
                                                guchar          *src,
                                                guchar          *dest,
                                                gint             x,
                                                gint             y,
                                                gint             w);
/* No idea */

void            gtk_preview_draw_row           (GtkPreview      *preview,
                                                guchar          *data,
                                                gint             x,
                                                gint             y,
                                                gint             w);
/* Descritta nel testo */

void            gtk_preview_set_expand         (GtkPreview      *preview,
                                                gint             expand);
/* No idea */

/* Nessun indizio per le seguenti, ma  dovrebbero  */
/* essere standard per la maggior parte dei widget */
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);

E' tutto!

12.3 Curve


Avanti Indietro Indice