⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 gtk_tut_it-19.html

📁 gtk是linux一款强大的夸平台的图形化开发工具
💻 HTML
📖 第 1 页 / 共 4 页
字号:
<!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&agrave; basilari, pu&ograve; essere necessario costruirsiun proprio widget. GTK usa molto l'ereditariet&agrave; tra i variwidget e, di solito, vi &egrave; un widget che si avvicina a quello che tiservirebbe, ed &egrave; 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&agrave; creato. Questo eviter&agrave; un duplicazionedi lavoro e far&agrave; s&igrave; che i widget non-GTK puri siano minimi, cos&igrave; 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&igrave; che le altre persone ne possanobeneficiare. Il miglioro modo dove farlo &egrave; 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 &egrave; importante aver capito come gli ogetti di GTK lavorano. Questa sezione &egrave; 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&agrave;e la stabilit&agrave;, 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) &egrave; 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&agrave;il primo campo della struttura di una classe deve essere una copia dellastruttura della classe genitore. La dichiarazione della struttura della classe GtkButton &egrave;:<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&ograve; fare il cast della struttura della sua classe con la GtkContainerClass, e usare i campi rilevanti per gestire i segnali.<P><P>C'&egrave; 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&egrave; la struttura dell'oggetto della classe madre, cos&igrave; che, se necessario, si pu&ograve; 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 &egrave; un widget che&egrave; 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 &egrave; 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 &egrave; la classeDialog. Visto che i nostri bottoni sono inseriti in una tabella, &egrave; naturale pensare che la nostra classe madre possa essere la GtkTable.Sfortunatamente, cos&igrave; non &egrave;. La creazione di un widget &egrave; 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 &egrave; 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&agrave;. A meno che non vogliamo duplicare molte delle fuinzionalit&agrave; 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 &egrave; un widget Tictactoe.<P><P>Qui vi &egrave; il file header completo:<P><BLOCKQUOTE><CODE><PRE>/* tictactoe.h */#ifndef __TICTACTOE_H__#define __TICTACTOE_H__#include &lt;gdk/gdk.h>#include &lt;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 &egrave; 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 (), &amp;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 &egrave;invocato quando una riga, colonna o diagonale &egrave; 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&agrave; <CODE>GTK_RUN_FIRST</CODE>, o <CODE>GTK_RUN_LAST</CODE>,anche se ci sono altre possibilit&agrave;.</LI><LI> <CODE>object_type</CODE>: l'identificativo dell'oggetto a cui questo segnale si riferisce. Esso sar&agrave; 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 &egrave; 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 + -