<ÖONT SIZÅ=1 FACE½arial COLOR=black> Copyòight &cïpy; 199u-1999 Aðogeo srl, Milan Š
vai alla Homepage di Apogeonline

 



Cos'è OpenPress
Glossario
Linux-FAQ
Documenti:

Open Source Definition

GNU General Public License

La cattedrale e il bazaar

Colonizzare la noosfera

Il calderone magico


Libri:

Italian crackdown

Open Sources

MediaMorfosi

GTK+/GNOME
sviluppo applicazioni


Telematica per la pace

Linux HOWTO: Installazione e configurazione

Linux HOWTO: Networking


Risorse
Feedback
vai alla Homepage di Apogeo Editore
<ôD WIDTH="25%" AÌIGN="riçht"> Ava~ti Ê

Vai alla homepage di OpenPress

GTK+ / Gnome Sviluppo di Applicazioni


Utilizzare il GnomeCanvas

GnomeCanvas è molto semplice da utilizzare. Questà è una virtù paragonata all'approccio di basso livello fornito da GtkDrawingArea e altri. Questa sezione mostra come creare un canvas, e operare con i suoi oggetti, terminando con esempi pratici di programmazione.

Preparazione del widget GnomeCanvas

La prima decisione da prendere è se utilizzare il canvas in modalità GDK oppure antialiased. Quando create un widget canvas, è necessario specificarne la modalità. Non esiste un modo per cambiarla in un secondo momento. gnome_canvas_new() crea un canvas in modalità GDK, mentre gnome_canvas_new_aa() in modalità antialiased. Queste funzioni sono mostrate in Figura 5 .

Talvolta è importante sapere quale visual e colormap verrano utilizzati dal canvas. In particolare:

  • In modalità GDK, se desiderate utilizzare gli oggetti GnomeCanvasImage per visualizzare immagini, è necessario utilizzare le visual e colormap di Imlib. GnomeCanvasImage utilizza Imlib per la visualizzazione delle immagini.

  • In modalità antialiased vengono utilizzate le funzioni per il rendering dei buffer RGB forniti da GDK ( la sezione Buffer RGB nel il capitolo I fondamenti di GDK ) per visualizzare questi sullo schermo. È necessario quindi utilizzare le visual e colormap del modulo RGB di GDK.

Per creare un widget con una visual e una colormap non di default vengono utilizzate le funzioni gtk_widget_push_visual() e gtk_widget_push_colormap(). Ecco il codice per creare un canvas in modalità GDK che supporti le immagini:


  GtkWidget* canvas;
  gtk_widget_push_visual(gdk_imlib_get_visual());
  gtk_widget_push_colormap(gdk_imlib_get_colormap());
  canvas = gnome_canvas_new();
  gtk_widget_pop_visual();
  gtk_widget_pop_colormap();

Invece, per un canvas in modalità antialiased:


  GtkWidget* canvas;
  gtk_widget_push_visual(gdk_rgb_get_visual());
  gtk_widget_push_colormap(gdk_rgb_get_cmap());
  canvas = gnome_canvas_new_aa();
  gtk_widget_pop_colormap();
  gtk_widget_pop_visual();

#include <libgnomeui/gnome-canvas.h>

GtkWidget* gnome_canvas_new(void);

GtkWidget* gnome_canvas_new_aa(void);

Figura 5. Costruttori del canvas

Regioni di scorrimento

Il canvas è praticamente di dimensioni infinite dal punto di vista dello sviluppatore. Tuttavia in realtà la vostra applicazione ne utilizzerà una piccola porzione. Quando utilizzate il canvas è necessario specificare quale regione interessa all'utente con gnome_canvas_set_scroll_region() ( Figura 6 ). La regione di scorrimento viene descritta attraverso coordinate globali. È inoltre possibile interrogare la regione di scorrimento con gnome_canvas_get_scroll_region().

Per aggiungere delle barre di scorrimento al canvas è sufficiente creare un widget GtkScrolledWindow e inserire il canvas al suo interno:


  GtkWidget* sw;
  sw = gtk_scrolled_window_new(NULL, NULL);
  gtk_container_add(GTK_CONTAINER(sw), canvas);

Se è vostro desiderio implementare lo scorrimento in un altro meccanismo che non siano le barre, dovete leggere e scrivere i riferimenti dello scorrimento. Questi sono espressi in coordinate canvas, e specificano quale pixel è visibile nell'angolo in alto a sinista. Ricordate che le coordinate nel sistema canvas sono relative alla regione di scorrimento.

  #include <libgnomeui/gnome-canvas.h>

void gnome_canvas_set_scroll_region(GnomeCanvas* canvas, double x1, double y1, double x2, double y2);

void gnome_canvas_get_scroll_region(GnomeCanvas* canvas, double* x1, double* y1, double* x2, double* y2);

void gnome_canvas_scroll_to(GnomeCanvas* canvas, gint cx, gint cy);

void gnome_canvas_get_scroll_offsets(GnomeCanvas* canvas, gint* cx, gint* cy);

Figura 6. Scorrimento del canvas

Zoom

Il canvas fornisce funzioni di zoom, incluse nelle conversioni fra sistemi di coordinate globali verso coordinate canvas e viceversa. È possibile impostare il fattore di ingrandimento utilizzando gnome_canvas_set_pixels_per_unit() ( Figura 7 ). Per default, il rapporto fra pixel e unità canvas è uguale a 1.0, ovvero nessun ingrandimento. Specificando un valore inferiore a 1.0 si indica la riduzione, maggiori invece, un vero e proprio ingrandimento.

In modalità antialiased potete ottenere lo stesso effetto applicando una trasformazione affine di trasformazione scalare al gruppo root del canvas stesso. Il membro pixels_per_unit della struttura GnomeCanvas è implementato per questo scopo. In più, gnome_canvas_set_pixels_per_unit() è più conveniente che l'uso di una trasformazione affine, e funziona in modalità GDK. (Poiché GDK utiliza le primitive di Xlib, non è banale implementare una trasformazione affine arbitraria. Probabilmente future versioni di Gnome implementeranno questo tipo di funzioni.)

  #include <libgnomeui/gnome-canvas.h>

void gnome_canvas_set_pixels_per_unit(GnomeCanvas* canvas, double ppu);

Figura 7. Zoom del canvas

Oggetti del canvas

La maggior parte delle volte vi ritroverete interessati a lavorare su oggetti del canvas piuttosto che sul canvas stesso. Questi sono solitamente molto semplici da utilizzare, paragonati ai widget. Nessuno degli oggetti standard ha un segnale esclusivo, dato che non sono interattivi (dato che GnomeCanvasItem è una sottoclasse di GtkObject, potrete, comunque, avere un oggetto che emetta dei segnali). La classe di base GnomeCanvasItem emette un unico segnale, "event", che viene utilizzato per gestire qualunque tipo di evento. Il segnale "event" non ha un gestore per default. Gli oggetti del canvas non rispondono agli eventi a meno che non venga associato un gestore al segnale. Figura 8 elenca tutte le funzioni di utilità per manipolare una classe di base GnomeCanvasItem.

Per creare un oggetto del canvas utilizzate la funzione generica gnome_canvas_item_new() (oppure gnome_canvas_item_newv() ). Questa funzione accetta il gruppo di appartenenza del nuovo oggetto, il GtkType della sottoclasse di GnomeCanvasItem da creare, e infine una lista di argomenti, terminata da NULL. La lista degli argomenti è utilizzata unicamente per convenienza, in modo da non dover chiamare gnome_canvas_item_set() immediatamente. gnome_canvas_item_new() crea una nuova istanza del tipo utilizzando gtk_type_new(), aggiunge l'oggetto al proprio GnomeCanvasGroup, e lo inserisce nella lista degli oggetti da ridisegnare.

Per distruggere un oggetto e rimuoverlo dal canvas è sufficiente chiamare gtk_object_destroy(). È inoltre possibile utilizzare i contatori di riferimenti standard con questo tipo di oggetti.

È possibile associare una trasformazione affine a un oggetto utilizzando gnome_canvas_item_affine_absolute(), oppure crearne una nuova utilizzando gnome_canvas_item_affine_relative(). Queste funzioni possono essere utilizzare per traslare, ruotare o scalare un oggetto del canvas (tuttavia, la scalatura e la rotazione funzionano unicamente in modalità antialiased).

Gli oggetti di un gruppo vengono normalmente inseriti nello stack nell'ordine in cui vengono creati, ponendo l'oggetto più recente in testa allo stack. È possibile manipolare questo ordine utilizzando gnome_canvas_item_raise() e gnome_canvas_item_lower(). Queste spostano un oggetto all'interno dello stack di un dato numero di posizioni. Passare valori molti alti come argomento positions è sicuro. L'oggetto verrà spostato il più possibile, e non oltre. È inoltre possibile spostare l'oggetto da una estremità all'altra dello stack, utilizzando gnome_canvas_item_raise_to_top() e gnome_canvas_item_lower_to_bottom().

Gli oggetti possono essere mostrati oppure nascosti. Questi ultimi non sono visualizzati dal canvas e non ricevono eventi. Tutti gli oggetti sono visibili per default. Le funzioni utilizzate sono gnome_canvas_item_show() e gnome_canvas_item_hide().

Cambiare il gruppo ad un oggetto è relativamente semplice, l'unica regola da seguire è che il nuovo gruppo deve appartenere allo stesso canvas di quello originale.

gnome_canvas_item_grab_focus() è una funzione analoga a gtk_widget_grab_focus(). Questa invia tutti gli eventi da tastiera all'oggetto in questione.

Gli oggetti del canvas possono inoltre catturare il puntatore del mouse esattamente come una GdkWindow. Gli argomenti per gnome_canvas_item_grab() sono analoghi a quelli per gdk_pointer_grab() ( il capitolo I fondamenti di GDK ). Mentre questo cattura il puntatore, nessun altro oggetto può ricevere eventi. Dietro le quinte, GnomeCanvas utilizza gdk_pointer_grab() per implementare gnome_canvas_item_grab().

Le proprietà visuali degli oggetti del canvas vengono interamente manipolate attraverso gli argomenti degli oggetti. Se avete saltato il il capitolo I tipi e gli oggetti in GTK+ , tornate indietro e leggete adesso la sezione sugli argomenti. Vengono utilizzate due funzioni per impostare la proprietà degli oggetti del canvas: gnome_canvas_item_set() e gnome_canvas_item_setv(). Queste sono quasi equivalenti a gtk_object_set() e gtk_object_setv(): impostano gli argomenti nello stesso identico modo, ma inoltre marcano gli oggetti da ridisegnare. Quindi è consigliato utilizzare queste anziché le varianti per GtkObject.

gnome_canvas_item_request_update() marca gli oggetti come "sporchi" e li inserisce nella coda di ridisegno. Il canvas attende che non siano presenti eventi di GTK+ in attesa, quindi ridisegna sé stesso in una singola operazione. Questo accade a causa dell'utilizzo di gtk_idle_add() e della sua rimozione dopo che viene eseguita. Per questo chiamare più volte gnome_canvas_item_request_update() può compromettere le prestazioni del canavs.

#include <libgnomeui/gnome-canvas.h>

GnomeCanvasItem* gnome_canvas_item_new(GnomeCanvasGroup* parent, GtkType type, const gchar* first_arg_name, ...);

GnomeCanvasItem* gnome_canvas_item_newv(GnomeCanvasGroup* parent, GtkType type, guint nargs, GtkArg* args);

void gnome_canvas_item_set(GnomeCanvasItem* item, const gchar* first_arg_name, ...);

void gnome_canvas_item_setv(GnomeCanvasItem* item, guint nargs, GtkArg* args);

void gnome_canvas_item_affine_relative(GnomeCanvasItem* item, const double affine[6]);

void gnome_canvas_item_affine_absolute(GnomeCanvasItem* item, const double affine[6]);

void gnome_canvas_item_raise(GnomeCanvasItem* item, int positions);

void gnome_canvas_item_lower(GnomeCanvasItem* item, int positions);

void gnome_canvas_item_raise_to_top(GnomeCanvasItem* item);

void gnome_canvas_item_lower_to_bottom(GnomeCanvasItem* item);

void gnome_canvas_item_show(GnomeCanvasItem* item);

void gnome_canvas_item_hide(GnomeCanvasItem* item);

void gnome_canvas_item_reparent(GnomeCanvasItem* item, GnomeCanvasGroup* new_group);

void gnome_canvas_item_grab_focus(GnomeCanvasItem* item);

int gnome_canvas_item_grab(GnomeCanvasItem* item, unsigned int event_mask, GdkCursor* cursor, guint32 etime);

void gnome_canvas_item_ungrab(GnomeCanvasItem* item, guint32 etime);

void gnome_canvas_item_get_bounds(GnomeCanvasItem* item, double* x1, double* y1, double* x2, double* y2);

void gnome_canvas_item_request_update(GnomeCanvasItem* item);

Figura 8. Utilizzare il GnomeCanvasItem

Oggetti ed eventi del canvas

Gli oggetti standard del GnomeCanvas emettono un unico segnale, "event", che viene emesso per tutti i tipi di evento. Il widget GnomeCanvas preprocessa tutti gli eventi GDK che riceve, e li trasmette ai suoi oggetti. Sintetizza inoltre alcuni eventi particolari. Ricordate che X invia gli eventi unicamente a finestre X (GdkWindow), e gli oggetti del canvas non hanno una GdkWindow a loro associata. Per questo motivo il widget GnomeCanvas deve svolgere il ruolo di intermediario. Ecco alcune delle operazioni svolte:

  • Le coordinate vengono automaticamente convertite a coordinate globali. Ad esempio, se un oggetto canvas riceve un evento del tipo GDK_BUTTON_PRESS, i campi x e y dell'evento saranno forniti in coordinate globali. (L'evento grezzo viene ricevuto dalla GdkWindow del canvas e per questo utilizzava coordinate relative.)

  • Gli eventi di entrata/uscita vengono sintetizzati per gli oggetti del canvas come il puntatore del mouse che si sposta attraverso il canvas.

  • Gli eventi vengono propagati all'interno della gerarchia degli oggetti del canvas, finché un il gestore del segnale "event" di un oggetto restituisce un valore uguale a TRUE. Questo meccanismo è molto simile a quello utilizzato per GtkWidget.

  • Agli oggetti del canvas vengono inviati unicamente gli eventi generati dagli utenti. La maggior parte degli eventi che vi aspettate che una GdkWindow possa ricevere, come expose e configure, non vengono inviati agli oggetti.

Il canvas svolge queste operazioni dietro le quinte, in modo che gli eventi degli oggetti siano utilizzabili in modo intuitivo e simile agli eventi GDK.

Una callback per un evento di un oggetto del canvas appare così:


static gint
item_event_callback(GnomeCanvasItem* item,
                    GdkEvent* event,
                    gpointer data)
{
  switch (event->type) {
    case GDK_BUTTON_PRESS:
      break;

    case GDK_MOTION_NOTIFY:
      break;

    case GDK_BUTTON_RELEASE:
      break;

    default:
      break;
  }

  /* Ritornare FALSE causa la propagazione dell'evento agli oggetti padri;
   * ritornare TRUE causa il termine della propagazione.
   */
  return FALSE;
}

Ovviamente, una callback reale esaminerà il contenuto dell'evento ed eseguirà istruzioni correlate.

Un esempio di GnomeCanvas

Questa sezione mostra un piccolo programma di esempio, a dimostrazione dell'utilizzo del canvas. Non viene descritta nel particolare la creazione degli oggetti. la sezione Reference per i GnomeCanvasItem standard fornisce maggiori dettagli al riguardo. La Figura 9 mostra il programma di esempio in esecuzione. È possibile spostare gli oggetti del canvas con il tasto sinistro del mouse. Premere il tasto Shift durante il clic su un oggetto ne causa la sua distruzione.

Figura 9. Un semplice programma per GnomeCanvas

Ecco il codice per creare un semplice canvas in modalità antialiased. Notare la chiamata a gdk_rgb_init(). Notare inoltre che lo scorrimento del canvas viene impostato. Infine, notare che la colormap e il visual di GdkRGB vengono spinti durante la creazione del canvas.


#include <gnome.h>

static gint delete_event_cb(GtkWidget* window, GdkEventAny* e, gpointer data);
static void create_canvas_items(GtkWidget* canvas);

int
main(int argc, char* argv[])
{
  GtkWidget* window;
  GtkWidget* sw;
  GtkWidget* canvas;

  gnome_init("esempio-canvas", "0.0", argc, argv);

  gdk_rgb_init();

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

  gtk_window_set_title(GTK_WINDOW(window), "Esempio del Canvas");

  gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, TRUE);

  gtk_signal_connect(GTK_OBJECT(window),
                     "delete_event",
                     GTK_SIGNAL_FUNC(delete_event_cb),
                     NULL);

  sw = gtk_scrolled_window_new(NULL, NULL);

  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
                                 GTK_POLICY_AUTOMATIC,
                                 GTK_POLICY_AUTOMATIC);


  gtk_widget_push_visual(gdk_rgb_get_visual());
  gtk_widget_push_colormap(gdk_rgb_get_cmap());
  canvas = gnome_canvas_new_aa();
  gtk_widget_pop_colormap();
  gtk_widget_pop_visual();

  gnome_canvas_set_scroll_region(GNOME_CANVAS(canvas), 0, 0, 600, 450);

  create_canvas_items(canvas);

  gtk_container_add(GTK_CONTAINER(sw), canvas);
  gtk_container_add(GTK_CONTAINER(window), sw);

  gtk_window_set_default_size(GTK_WINDOW(window), 300, 300);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}

static gint
delete_event_cb(GtkWidget* window, GdkEventAny* e, gpointer data)
{
  gtk_main_quit();
  return FALSE;
}

Una volta creato il canvas il programma aggiunge alcuni oggetti e collega una semplice callback al segnale "event" dell'oggetto. Ecco il codice:


static gint
item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data)
{
  static double x, y;
  double new_x, new_y;
  GdkCursor *fleur;
  static int dragging;
  double item_x, item_y;

  item_x = event->button.x;
  item_y = event->button.y;
  gnome_canvas_item_w2i(item->parent, &item_x, &item_y);

  switch (event->type)
    {
    case GDK_BUTTON_PRESS:
      switch(event->button.button)
        {
        case 1:
          if (event->button.state & GDK_SHIFT_MASK)
            {
              gtk_object_destroy(GTK_OBJECT(item));
            }
          else
            {
              x = item_x;
              y = item_y;

              fleur = gdk_cursor_new(GDK_FLEUR);
              gnome_canvas_item_grab(item,
                                     GDK_POINTER_MOTION_MASK |
                                     GDK_BUTTON_RELEASE_MASK,
                                     fleur,
                                     event->button.time);
              gdk_cursor_destroy(fleur);
              dragging = TRUE;
            }
          break;

        default:
          break;
        }
      break;

    case GDK_MOTION_NOTIFY:
      if (dragging && (event->motion.state & GDK_BUTTON1_MASK))
        {
          new_x = item_x;
          new_y = item_y;

          gnome_canvas_item_move(item, new_x - x, new_y - y);
          x = new_x;
          y = new_y;
        }
      break;

    case GDK_BUTTON_RELEASE:
      gnome_canvas_item_ungrab(item, event->button.time);
      dragging = FALSE;
      break;

    default:
      break;
    }

  return FALSE;
}

static void
setup_item(GnomeCanvasItem *item)
{
  gtk_signal_connect(GTK_OBJECT(item), "event",
                     (GtkSignalFunc) item_event,
                     NULL);
}

static void
create_canvas_items(GtkWidget* canvas)
{
  GnomeCanvasPoints* points;
  GnomeCanvasGroup* group;
  GnomeCanvasItem* item;
  double affine[6];

  group = gnome_canvas_root(GNOME_CANVAS(canvas));

  /* Un poligono */
  points = gnome_canvas_points_new(14);

  points->coords[0] = 270.0;
  points->coords[1] = 330.0;
  points->coords[2] = 270.0;
  points->coords[3] = 430.0;
  points->coords[4] = 390.0;
  points->coords[5] = 430.0;
  points->coords[6] = 390.0;
  points->coords[7] = 330.0;
  points->coords[8] = 310.0;
  points->coords[9] = 330.0;
  points->coords[10] = 310.0;
  points->coords[11] = 390.0;
  points->coords[12] = 350.0;
  points->coords[13] = 390.0;
  points->coords[14] = 350.0;
  points->coords[15] = 370.0;
  points->coords[16] = 330.0;
  points->coords[17] = 370.0;
  points->coords[18] = 330.0;
  points->coords[19] = 350.0;
  points->coords[20] = 370.0;
  points->coords[21] = 350.0;
  points->coords[22] = 370.0;
  points->coords[23] = 410.0;
  points->coords[24] = 290.0;
  points->coords[25] = 410.0;
  points->coords[26] = 290.0;
  points->coords[27] = 330.0;

  item = gnome_canvas_item_new(group,
                               gnome_canvas_polygon_get_type (),
                               "points", points,
                               "fill_color", "tan",
                               "outline_color", "black",
                               "width_units", 3.0,
                               NULL);

  setup_item(item);

  gnome_canvas_points_unref(points);

  /* Traslazione del poligono */

  art_affine_translate(affine, -150.0, -300.0);

  gnome_canvas_item_affine_relative(item, affine);

  /* Un rettangolo traslucido */
  setup_item (gnome_canvas_item_new (group,
                                     gnome_canvas_rect_get_type(),
                                     "x1", 90.0,
                                     "y1", 40.0,
                                     "x2", 180.0,
                                     "y2", 100.0,
                                     "fill_color_rgba", 0x3cb37180,
                                     "outline_color", "black",
                                     "width_units", 4.0,
                                     NULL));

  /* Una ellisse traslucida */
  setup_item (gnome_canvas_item_new (group,
                                     gnome_canvas_ellipse_get_type(),
                                     "x1", 210.0,
                                     "y1", 80.0,
                                     "x2", 280.0,
                                     "y2", 140.0,
                     `       `       bfill_coüor_rgba¢, 0x5f9õa080,
 `       `       `       à       à   "outìine_colïr", "bláck",
  `       `       `       ð       à  "widtè_pixelsâ, 0,
  °               °       à       `  NULL)é;

  /*ðCreate õna ellissi su una linea¾ Vengono gestitõ come uî singolo oggettÿ.
   */Š
  grouð =
    ÇNOME_CAîVAS_GROÕP (gnomå_canvasßitem_new (group¬
      à       `       `       p       `       ðgnome_cánvas_grïup_get_type(),
        `       à       à       ð       à      "x", 0.0,ê       `       `               à       ð       by", 0.0ì
      à       à       à       ð               ðNULL));Z  setupßitem(GNïME_CANVÁS_ITEM(÷roup));
  {
  à doubleàxpos = r0.0;
  à while èxpos &#ö0; 300.°)
     à{
     à  gnomeßcanvas_étem_new(group,
à       0       `       à     gnïme_canvás_ellipóe_get_tùpe(),
                 `       p    "x1r, xpos,Ê       à       ð       à      "y1", 100n0,
    °       à       `       ` "x2", xpos + 1ð.0,
   `       à       p       à  "y2", 110.0,
à       à               à     "fill_coloò_rgba",ð0x0000FÖFF,
   à       p       à       `  "outline_coloò_rgba",à0xFF,
 à       `       à       ð    NULÜ);
    à   xposð+= 15.0û
      }
  }
}
|/PRE>
<¯TD>



<ÔR>
Indietro¼/FONT><ïA> <ÖONT SIZÕ="2">š
Archétettura0base del canvas¼/B> JReference per i GnomesanvasItem standard <TD>