📄 gtk_tut_it-19.html
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML><HEAD> <META NAME="GENERATOR" CONTENT="SGML-Tools 1.0.9"> <TITLE>GTK Tutorial: Scrivere un proprio Widget</TITLE> <LINK HREF="gtk_tut_it-20.html" REL=next> <LINK HREF="gtk_tut_it-18.html" REL=previous> <LINK HREF="gtk_tut_it.html#toc19" REL=contents></HEAD><BODY BGCOLOR="#FFFFFF"><A HREF="gtk_tut_it-20.html">Avanti</A><A HREF="gtk_tut_it-18.html">Indietro</A><A HREF="gtk_tut_it.html#toc19">Indice</A><HR NOSHADE><H2><A NAME="s19">19. Scrivere un proprio Widget</A></H2><H2><A NAME="ss19.1">19.1 Panoramica</A></H2><P>Anche se la distribuzione GTK contiene molto tipi di widget che possonocoprire molte necessità basilari, può essere necessario costruirsiun proprio widget. GTK usa molto l'ereditarietà tra i variwidget e, di solito, vi è un widget che si avvicina a quello che tiservirebbe, ed è spesso possibile creare un nuovo widget con poche lineedi codice. Ma prima di iniziare il lavoro su un nuovo widget, vediamo se qualcuno non lo ha già creato. Questo eviterà un duplicazionedi lavoro e farà sì che i widget non-GTK puri siano minimi, così daaiutare sia chi crea il codice che chi l'interfaccia per applicazioni GTK molto grosse. D'altra parte, quando hai finito di scrivere un widget, annuncialo a tutto il mondo così che le altre persone ne possanobeneficiare. Il miglioro modo dove farlo è la <CODE>gtk-list</CODE>.<P>I sorgenti completi per i widget di esempio possono essere presi dallo stessosito da cui avete scaricato questo tutorial, oppure da:<P><A HREF="http://www.msc.cornell.edu/~otaylor/gtk-gimp/tutorial">http://www.msc.cornell.edu/~otaylor/gtk-gimp/tutorial</A><P><P><H2><A NAME="ss19.2">19.2 L'anatomia di un widget</A></H2><P>Per creare un nuovo widget è importante aver capito come gli ogetti di GTK lavorano. Questa sezione è solo una breve spiegazione. Guarda ladocumentazione di riferimento per maggiori dettagli.<P><P>I widget GTK sono implementati in un modo orientato agli oggetti,anche se usando il C standard. Questo aumenta notevolmente la portabilitàe la stabilità, specialmente per le correnti generazioni di compilatori C++;comunque questo significa che chi scrive un widget deve fare attenzionead alcuni dettagli di implementazione. L'informazione comune a tutte leistanze di una classe di widget (ad esempio: a tutti i bottoni) è memorizzata <EM>class structure</EM>. C'e' solamente una copia di questo in cui sono memorizzate le informazioni riguardanti i segnali della classe (assomiglia ad una funzione virtuale in C). Per supportare l'ereditarietàil primo campo della struttura di una classe deve essere una copia dellastruttura della classe genitore. La dichiarazione della struttura della classe GtkButton è:<P><BLOCKQUOTE><CODE><PRE>struct _GtkButtonClass{ GtkContainerClass parent_class; void (* pressed) (GtkButton *button); void (* released) (GtkButton *button); void (* clicked) (GtkButton *button); void (* enter) (GtkButton *button); void (* leave) (GtkButton *button);};</PRE></CODE></BLOCKQUOTE><P><P>Quando un bottone viene trattato come un contenitore (ad esempio quando viene ridimensionato) si può fare il cast della struttura della sua classe con la GtkContainerClass, e usare i campi rilevanti per gestire i segnali.<P><P>C'è anche una struttura per ogni widget che viene creata ad ogni istanza. Questa struttura ha campi per memorizzare le informazioni che sono differenti per ogni volta che il widgetviene istanziato. Chiameremo questa struttura la <EM> strutturaoggetto</EM>. Per la classe Bottone, questa ha l'aspetto:<P><BLOCKQUOTE><CODE><PRE>struct _GtkButton{ GtkContainer container; GtkWidget *child; guint in_button : 1; guint button_down : 1;};</PRE></CODE></BLOCKQUOTE><P><P>Si noti che, similmente alla struttura della classe, il primo campoè la struttura dell'oggetto della classe madre, così che, se necessario, si può fare ilcast di questa struttura con quella dell'oggetto della classe madre.<P><H2><A NAME="ss19.3">19.3 Creare un Widget composto</A></H2><H3>Introduzione</H3><P>Un tipo di widget a cui potreste essere interessati è un widget cheè semplicemnte un aggregato di altri widget GTK. Questo tipo di widget non fa nulla che non possa essere fatto creando un nuovowidget, ma fornisce un modo conveniente per inscatolare elementi dell'interfaccia utente per poi riutilizzarli. I widget FileSelection e ColorSelection della ditribuzione standardsono esempi di questo tipo di widget.<P><P>Il widget di esempio che creeremo in questo capitolo è il Tictactoe, un vettore 3x3 di bottoni a commutazione il quale emetteun segnale quando tutti e 3 i bottoni di una riga, colonna o di unadiagonale sono premuti.<P><H3>Scegliere la classe madre</H3><P>La classe madre per un widget composto e' tipicamente la classe contenitrice che racchiude tutti gli elementi del widget composto.Per esempio, la classe madre del widget FileSelection è la classeDialog. Visto che i nostri bottoni sono inseriti in una tabella, è naturale pensare che la nostra classe madre possa essere la GtkTable.Sfortunatamente, così non è. La creazione di un widget è divisotra 2 funzioni : la funzione <CODE>WIDGETNAME_new()</CODE> che viene invocatadall'utente, e la funzione <CODE>WIDGETNAME_init()</CODE> che ha il compitoprincipale di inizializzare il widget che è indipendente dai valoripassati alla funzione <CODE>_new()</CODE>. Widget figli o discendenti possono chiamare, solamente, la funzione del loro widget genitore. Ma questa divisione del lavoro non funziona bene per la tabella, laquale, quando creata, necessita di conoscere il numero di righe ecolonne che la comporrà. A meno che non vogliamo duplicare molte delle fuinzionalità della <CODE>gtk_table_new()</CODE> nel nostro widgetTictactoe, faremmo meglio a evitare di derivarlo dalla GtkTable. Per questaragione lo deriviamo invece da GtkVBox, e uniamo la nostra tabelladentro il VBox.<P><H3>Il File Header</H3><P>Ogni classe di widget ha un file header il quale dichiara l'oggetto e lastruttura della classe del widget, comprese le funzioni pubbliche.Per prevenire duplicati di definizioni, noi includiamo l'intero file header fra:<P><BLOCKQUOTE><CODE><PRE>#ifndef __TICTACTOE_H__#define __TICTACTOE_H__...#endif /* __TICTACTOE_H__ */</PRE></CODE></BLOCKQUOTE><P>E per far felici i programmi in C++ che includono il nostro file header, in:<P><BLOCKQUOTE><CODE><PRE>#ifdef __cplusplusextern "C" {#endif /* __cplusplus */...#ifdef __cplusplus}#endif /* __cplusplus */</PRE></CODE></BLOCKQUOTE><P>Insieme alle funzioni e alle strutture, dichiariamo tre macro standard nel nostro file header, <CODE>TICTACTOE(obj)</CODE>,<CODE>TICTACTOE_CLASS(klass)</CODE>, e <CODE>IS_TICTACTOE(obj)</CODE>, i quali rispettivamente fanno il cast di un puntatore ad un puntatore ad un ogetto od ad una strutturadi classe, e guarda se un oggetto è un widget Tictactoe.<P><P>Qui vi è il file header completo:<P><BLOCKQUOTE><CODE><PRE>/* tictactoe.h */#ifndef __TICTACTOE_H__#define __TICTACTOE_H__#include <gdk/gdk.h>#include <gtk/gtkvbox.h>#ifdef __cplusplusextern "C" {#endif /* __cplusplus */#define TICTACTOE(obj) GTK_CHECK_CAST (obj, tictactoe_get_type (), Tictactoe)#define TICTACTOE_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, tictactoe_get_type (), TictactoeClass)#define IS_TICTACTOE(obj) GTK_CHECK_TYPE (obj, tictactoe_get_type ())typedef struct _Tictactoe Tictactoe;typedef struct _TictactoeClass TictactoeClass;struct _Tictactoe{ GtkVBox vbox; GtkWidget *buttons[3][3];};struct _TictactoeClass{ GtkVBoxClass parent_class; void (* tictactoe) (Tictactoe *ttt);};guint tictactoe_get_type (void);GtkWidget* tictactoe_new (void);void tictactoe_clear (Tictactoe *ttt);#ifdef __cplusplus}#endif /* __cplusplus */#endif /* __TICTACTOE_H__ */</PRE></CODE></BLOCKQUOTE><P><H3>La funzione <CODE>_get_type()</CODE></H3><P>Continuiamo ora con l'implementazione del nostro widget. Una funzionebasilare di ogni widget è la funzione <CODE>WIDGETNAME_get_type()</CODE>.Questa funzione, quando chiamata la prima volta, comunica a GTK la classe del widget, e ottiene un identificativo univoco per la classe delwidget. Chiamate successive restituiscono semplicemente l'identificativo.<P><BLOCKQUOTE><CODE><PRE>guinttictactoe_get_type (){ static guint ttt_type = 0; if (!ttt_type) { GtkTypeInfo ttt_info = { "Tictactoe", sizeof (Tictactoe), sizeof (TictactoeClass), (GtkClassInitFunc) tictactoe_class_init, (GtkObjectInitFunc) tictactoe_init, (GtkArgSetFunc) NULL, (GtkArgGetFunc) NULL }; ttt_type = gtk_type_unique (gtk_vbox_get_type (), &ttt_info); } return ttt_type;}</PRE></CODE></BLOCKQUOTE><P><P>La struttura GtkTypeInfo ha la seguente definizione:<P><BLOCKQUOTE><CODE><PRE>struct _GtkTypeInfo{ gchar *type_name; guint object_size; guint class_size; GtkClassInitFunc class_init_func; GtkObjectInitFunc object_init_func; GtkArgSetFunc arg_set_func; GtkArgGetFunc arg_get_func;};</PRE></CODE></BLOCKQUOTE><P><P>I campi di questa struttura sono abbastanza auto-esplicativi.Ignoreremo, per ora, i campi <CODE>arg_set_func</CODE> e <CODE>arg_get_func</CODE>:hanno un ruolo importante, ma ancora largamente nonimplementato, nel permettere ai linguaggi interpretatidi settare convenientemente le opzioni del widget.Una volta che il GTK ha completato correttamente una copia di questastruttura, sa come creare un oggetto di un particolare widget.<P><H3>La funzione <CODE>_class_init()</CODE> </H3><P>La funzione <CODE>WIDGETNAME_class_init()</CODE> inizialiazza i campi dellastruttura della classe del widget, e setta ogni segnale della classe.Per il nostro widget Tictactoe ha il seguente aspetto:<P><BLOCKQUOTE><CODE><PRE>enum { TICTACTOE_SIGNAL, LAST_SIGNAL};static gint tictactoe_signals[LAST_SIGNAL] = { 0 };static voidtictactoe_class_init (TictactoeClass *class){ GtkObjectClass *object_class; object_class = (GtkObjectClass*) class; tictactoe_signals[TICTACTOE_SIGNAL] = gtk_signal_new ("tictactoe", GTK_RUN_FIRST, object_class->type, GTK_SIGNAL_OFFSET (TictactoeClass, tictactoe), gtk_signal_default_marshaller, GTK_TYPE_NONE, 0); gtk_object_class_add_signals (object_class, tictactoe_signals, LAST_SIGNAL); class->tictactoe = NULL;}</PRE></CODE></BLOCKQUOTE><P><P>Il nostro widget ha semplicemente il segnale ``tictactoe'' che èinvocato quando una riga, colonna o diagonale è completamente premuta.Non tutti i widget composti necessitano di segnali, quindi se staileggendo questo per la prima volta, puoi anche saltare alla prossima sezione,dal momento che a questo punto le cose diventano un po' complicate.<P>La funzione:<BLOCKQUOTE><CODE><PRE>gint gtk_signal_new (const gchar *name, GtkSignalRunType run_type, GtkType object_type, gint function_offset, GtkSignalMarshaller marshaller, GtkType return_val, guint nparams, ...);</PRE></CODE></BLOCKQUOTE><P>crea un nuovo segnale. I parametri sono:<P><UL><LI> <CODE>name</CODE>: Il nome del segnale.</LI><LI> <CODE>run_type</CODE>: Se il segstore predefinito viene eseguito prima o dopodi quello dell'utente. Di norma questo sarà <CODE>GTK_RUN_FIRST</CODE>, o <CODE>GTK_RUN_LAST</CODE>,anche se ci sono altre possibilità.</LI><LI> <CODE>object_type</CODE>: l'identificativo dell'oggetto a cui questo segnale si riferisce. Esso sarà anche applicato agli oggetti discendenti.</LI><LI> <CODE>function_offset</CODE>: L'offset nella struttura della classe di unpuntatore al gestore predefinito.</LI><LI> <CODE>marshaller</CODE>: una funzione che è usata per invocare il gestoredel segnale. Per gestori di segnali che non hanno argomenti oltre all'oggetto che emette il segnale e i dati dell'utente, possiamo usarela funzione predefinita <CODE>gtk_signal_default_marshaller</CODE></LI><LI> <CODE>return_val</CODE>: Il tipo del valore di ritorno.</LI><LI> <CODE>nparams</CODE>: Il numero di parametri del gestore di segnali (oltreai due predefiniti menzionati sopra)</LI><LI> <CODE>...</CODE>: i tipi dei parametri</LI></UL><P>Quando si specificano i tipi, si usa l'enumerazione <CODE>GtkType</CODE>:<P><BLOCKQUOTE><CODE><PRE>typedef enum{ GTK_TYPE_INVALID, GTK_TYPE_NONE, GTK_TYPE_CHAR, GTK_TYPE_BOOL, GTK_TYPE_INT,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -