📄 gtk_tut_it-19.html
字号:
GTK_TYPE_UINT, GTK_TYPE_LONG, GTK_TYPE_ULONG, GTK_TYPE_FLOAT, GTK_TYPE_DOUBLE, GTK_TYPE_STRING, GTK_TYPE_ENUM, GTK_TYPE_FLAGS, GTK_TYPE_BOXED, GTK_TYPE_FOREIGN, GTK_TYPE_CALLBACK, GTK_TYPE_ARGS, GTK_TYPE_POINTER, /* sarebbe bello poter togliere alla fine i prossimi due */ GTK_TYPE_SIGNAL, GTK_TYPE_C_CALLBACK, GTK_TYPE_OBJECT} GtkFundamentalType;</PRE></CODE></BLOCKQUOTE><P><P><CODE>gtk_signal_new()</CODE> restituisce un identificatore unico intero per il segnale, che memorizziamo nel vettore <CODE>tictactoe_signals</CODE>, cheindicizzeremo usando una enumerazione. (Convenzionalmente, gli elementi dell'enumerazionesono i nomi dei segnali, in maiuscolo, ma qui ci potrebbe essere un conflitto con la macro <CODE>TICTACTOE()</CODE>, quindi l'abbiamo chiamato <CODE>TICTACTOE_SIGNAL</CODE><P>Dopo aver creato un nostro segnale, abbiamo bisogno di dire a GTKdi associare il nostro segnale alla classe Tictactoe. Lo facciamoinvocando <CODE>gtk_object_class_add_signals()</CODE>. Settiamo quindi a NULLil puntatore che punta al gestore predefinito per il segnale ``tictactoe'' a NULL, indicando che non ci sono azioni predefinite.<P><H3>La funzione <CODE>_init()</CODE></H3><P><P>Ogni classe di Widget necessita anche di una funzione per inizializzare la struttura dell'oggetto. Usualmente questa funzione ha il ruolo abbastanzalimitato di assegnare ai campi della struttura i valori predefiniti.Per widget composti, comunque, questa funzione crea, anche,i widget componenti del widget composto.<P><BLOCKQUOTE><CODE><PRE>static voidtictactoe_init (Tictactoe *ttt){ GtkWidget *table; gint i,j; table = gtk_table_new (3, 3, TRUE); gtk_container_add (GTK_CONTAINER(ttt), table); gtk_widget_show (table); for (i=0;i<3; i++) for (j=0;j<3; j++) { ttt->buttons[i][j] = gtk_toggle_button_new (); gtk_table_attach_defaults (GTK_TABLE(table), ttt->buttons[i][j], i, i+1, j, j+1); gtk_signal_connect (GTK_OBJECT (ttt->buttons[i][j]), "toggled", GTK_SIGNAL_FUNC (tictactoe_toggle), ttt); gtk_widget_set_usize (ttt->buttons[i][j], 20, 20); gtk_widget_show (ttt->buttons[i][j]); }}</PRE></CODE></BLOCKQUOTE><P><H3>E il resto...</H3><P><P>C'è un'altra funzione che ogni widget (eccetto i Widget di base come GtkBin che non possono essere instanziati) deve avere : la funzioneche l'utente invoca per creare un oggetto di quel tipo. Questa è convenzionalmente chiamata <CODE>WIDGETNAME_new()</CODE>. In alcuni widget,non nel caso del nostro Tictactoe, questa funzione richiede degli argomenti, e fa alcune operazioni basandosi su di essi. Le altredue funzioni sono specifiche del widget Tictactoe.<P><P><CODE>tictactoe_clear()</CODE> è una funzione pubblica che resetta tutti i bottoni, nel widget, allo stato iniziale (non premuto). Notate l'uso di <CODE>gtk_signal_handler_block_by_data()</CODE> per impedire che il nostrogestore dei segnali venga attivato quando non ce n'è bisogno.<P><P><CODE>tictactoe_toggle()</CODE> è il gestore del segnale che viene invocato quando l'utente preme il bottone. Esso guarda se vi èqualche combinazione vincente che coinvolge i bottoni premuti, e nelcaso ci fosse, emette il segnale ``tictactoe''.<P><BLOCKQUOTE><CODE><PRE> GtkWidget*tictactoe_new (){ return GTK_WIDGET ( gtk_type_new (tictactoe_get_type ()));}void tictactoe_clear (Tictactoe *ttt){ int i,j; for (i=0;i<3;i++) for (j=0;j<3;j++) { gtk_signal_handler_block_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt); gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (ttt->buttons[i][j]), FALSE); gtk_signal_handler_unblock_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt); }}static voidtictactoe_toggle (GtkWidget *widget, Tictactoe *ttt){ int i,k; static int rwins[8][3] = { { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 }, { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 } }; static int cwins[8][3] = { { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 }, { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 }, { 0, 1, 2 }, { 2, 1, 0 } }; int success, found; for (k=0; k<8; k++) { success = TRUE; found = FALSE; for (i=0;i<3;i++) { success = success && GTK_TOGGLE_BUTTON(ttt->buttons[rwins[k][i]][cwins[k][i]])->active; found = found || ttt->buttons[rwins[k][i]][cwins[k][i]] == widget; } if (success && found) { gtk_signal_emit (GTK_OBJECT (ttt), tictactoe_signals[TICTACTOE_SIGNAL]); break; } }}</PRE></CODE></BLOCKQUOTE><P><P><P>E finalmente un programma di esempio che usa il nostro widgetTictactoe:<P><BLOCKQUOTE><CODE><PRE>#include <gtk/gtk.h>#include "tictactoe.h"/* Invocato quando una riga, colonna o diagonale e' completata. */voidwin (GtkWidget *widget, gpointer data){ g_print ("Yay!\n"); tictactoe_clear (TICTACTOE (widget));}int main (int argc, char *argv[]){ GtkWidget *window; GtkWidget *ttt; gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_title (GTK_WINDOW (window), "Aspect Frame"); gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (gtk_exit), NULL); gtk_container_border_width (GTK_CONTAINER (window), 10); /* Crea un nuovo widget Tictactoe. */ ttt = tictactoe_new (); gtk_container_add (GTK_CONTAINER (window), ttt); gtk_widget_show (ttt); /* E gli aggancia il segnale "tictactoe" */ gtk_signal_connect (GTK_OBJECT (ttt), "tictactoe", GTK_SIGNAL_FUNC (win), NULL); gtk_widget_show (window); gtk_main (); return 0;}</PRE></CODE></BLOCKQUOTE><P><H2><A NAME="ss19.4">19.4 Creare un widget a partire da zero</A></H2><H3>Introduzione</H3><P><P>In questa sezione impareremo meglio come i widget si mostrano sullo schermoe interagiscono con gli eventi. Come esempio, creeremoun widget di quadrante analogico con un puntatore che l'utente può trascinare per assegnare il valore.<P><H3>Mostrare un widget sullo schermo</H3><P>Ci sono alcuni passi che sono necessari nella visualizzazione sulloschermo. Dopo che il widget è stato creato con una chiamata a <CODE>WIDGETNAME_new()</CODE>, sono necessarie alcune altre funzioni:<P><UL><LI> <CODE>WIDGETNAME_realize()</CODE> è responsabile della creazione di una finestra X per il widget se ne ha una.</LI><LI> <CODE>WIDGETNAME_map()</CODE> è invocata dopo che l'utente ha chiamato <CODE>gtk_widget_show()</CODE>. E' responsabile di vedere se ilwidget è attualmente disegnato sullo schermo (<EM>mappato</EM>). Per una classe contenitore, essa deve anche creare chiamate alle funzioni <CODE>map()</CODE>> per ogni widget figlio.</LI><LI> <CODE>WIDGETNAME_draw()</CODE> è invocata quando <CODE>gtk_widget_draw()</CODE> viene chiamata per il widget o per uno dei suoipredecessori. Esso fa sì che l'attuale chiamata allafunzione di disegno del widget disegni il widget sullo schermo.Per la classe contenitore, questa funzione deve eseguire lechiamate alla funzioni <CODE>gtk_widget_draw()</CODE> di ogni suo widgetfiglio.</LI><LI> <CODE>WIDGETNAME_expose()</CODE> è un gestore per l'evento di esposizioneper il widget. Esso crea le chiamate necessarie alle funzioni di disegnoper disegnare la porzione che si è resa visibile. Per le classi contenitore, questa funzione deve generare gli eventi di ``expose'' per tutti i widget figli che non hanno una propria finestra (se essi hannouna loro finestra, sarà X che genererà i necessari eventi di expose).</LI></UL><P><P>Potete notare che le ultime due funzioni sono molto simili, ognuna èresponsabile per il disegno del widget sullo schermo. Infatti moltitipi di widget non sanno relamente la differenza tra le due.La funzione di predefinita <CODE>draw()</CODE> nella classe widget, semplicementegenera un sintetico evento di ``expose'' per l'area da ridisegnare.Comunque, alcuni tipi di widget possono risparmiare tempo distinguendole due funzioni. Per esempio, se un widget ha piu' finestre X, allora visto che l'evento ``expose'' identifica solo la finestra esposta, esso può ridisegnare solo la finestra interessata, cosa che non è possibile per chiamate a <CODE>draw()</CODE>.<P><P>I widget contenitori, anche se essi non farebbero differenze,non possono semplicemente usare la funzione <CODE>draw()</CODE> perchè per i loro widget figli la differenza potrebbere essere importante. Comunque, sarebbe uno spreco duplicare il codice di disegno nelle duefunzioni. La convenzione è che questi widget abbiano una funzionechiamata <CODE>WIDGETNAME_paint()</CODE> che disegna il widget, che è poichiamata dalle funzioni <CODE>draw()</CODE> e <CODE>expose()</CODE><P><P>Nell'approccio del nostro esempio, visto che il widget, ha una sola finestra, possiamo utilizzare il modo piu' sempliceed usare la funzione predefinita <CODE>draw()</CODE> e implementaresolamente la funzione <CODE>expose()</CODE>.<P><H3>Le origini del widget Dial</H3><P>Come tutti gli animali terresti sono semplicemente varianti del primoamfibio, i widget Gtk tendono ad essere varianti di altri widget, precedentementescritti. Così, anche se questa sezione è intitolata ``Creareun widget a partire da zero", il nostro widget inizia in realtà con il codice sorgente del widget Range. Questo è stato preso come punto d'inizioperche' sarebbe carino se il nostro widget avesse lastessa interfaccia del widget Scale il quale è semplicemente unaspecializzazione del widget Range. Così, sebbene il codice sorgente e' presentato sotto in forma definitiva, non si deve pensare che sia statoscritto <EM>deus ex machina</EM> in questo modo. Se poi non avete familiaritàcon il funzionamento del widget Scale dal punto di vista di chi scriveun'applicazione, potrebbe essere una buona idea guardare indietro prima di continuare.<P><H3>Le basi</H3><P>Una parte del nostro widget potrebbe essere simileal widget Tictactoe. In primo luogo, abbiamo il file header:<P><BLOCKQUOTE><CODE><PRE>/* GTK - The GIMP Toolkit * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#ifndef __GTK_DIAL_H__#define __GTK_DIAL_H__#include <gdk/gdk.h>#include <gtk/gtkadjustment.h>#include <gtk/gtkwidget.h>#ifdef __cplusplusextern "C" {#endif /* __cplusplus */#define GTK_DIAL(obj) GTK_CHECK_CAST (obj, gtk_dial_get_type (), GtkDial)#define GTK_DIAL_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_dial_get_type (), GtkDialClass)#define GTK_IS_DIAL(obj) GTK_CHECK_TYPE (obj, gtk_dial_get_type ())typedef struct _GtkDial GtkDial;typedef struct _GtkDialClass GtkDialClass;struct _GtkDial{ GtkWidget widget; /* Politica di update (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */ guint policy : 2; /* Bottone correntemente premuto o 0 altrimenti */ guint8 button; /* Dimensione della componente Dial. */ gint radius; gint pointer_width; /* ID del timer di update, o 0 altrimenti */ guint32 timer; /* Angolo corrente. */ gfloat angle; /* Vecchi valori dell'aggiustamento così sappiamo quando * qualcosa cambia */ gfloat old_value; gfloat old_lower; gfloat old_upper; /* L'oggetto adjustament che memorizza i dati per questo dial */ GtkAdjustment *adjustment;};struct _GtkDialClass{ GtkWidgetClass parent_class;};GtkWidget* gtk_dial_new (GtkAdjustment *adjustment);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -