<- PW: Alle radici dello GNOME - Prima Puntata - Copertina - PW: Come ti metto in moto il telescopio -> |
PlutoWare
L'elemento grafico fondamentale in GTK+ è, come già detto, il widget. Esistono diversi tipi di widget; si va dal widget che permette di rappresentare un bottone al widget che implementa un tooltip, al widget per le barre di scorrimento, e cosi via. Ogni widget deriva dalla classe base GtkWidget, e da questa eredita una serie di proprietà.
Ma come sono realmente realizzati gli oggetti in GTK+? Ogni GtkObject
è formato essenzialmente da due strutture: una rappresenta la classe dell'oggetto,
l'altra una istanza di quell'oggetto.
typedef struct _ModuloRubrica ModuloRubrica; typedef struct _ModuloRubricaClass ModuloRubricaClass; struct _ModuloRubrica { GnomeDialog dialog; /* il notebook */ GtkWidget *notebook; /* lista degli oggetti contenuti nelle pagine del notebook */ GList *childs; ... MODO_MODULO modo; } struct _ModuloRubricaClass { GnomeDialogClass parent_class; void (* modulo_rubrica_aiuto) (ModuloRubrica *modulo, gint pagina); }; guint modulo_rubrica_get_type (void); GtkWidget *modulo_rubrica_new (MODO_MODULO modo);
Per il mio programma avevo bisogno di una finestra di dialogo che
presentasse all'utente i vari campi da riempire con i dati dell'indirizzo. Ho allora derivato
dalla classe genitore GnomeDialog la mia classe ModuloRubrica; così facendo, la
mia classe ha ereditato tutti metodi di GnomeDialog (che a sua volta è derivato
da GtkWindow, derivata da un derivato di ... GtkWidget, ed ha ereditato i metodi di
GtkWindow che ha ereditato ...). Come potete osservate il primo membro della struttura
ModuloRubrica è GnomeDialog; in questo modo ModuloRubrica erediterà tutti i
metodi e i campi di quest'ultimo, di fatto estendendolo, con un concetto di ereditarietà simile
a quello di altri linguaggi; sarà poi possibile utilizzare i metodi della classe genitore
effettuando un cast da ModuloRubrica a GnomeDialog. Lo stesso
principio si applica per ModuloRubricaClass; in questo è inoltre presente un metodo
che opererà sull'oggetto. Una piccola premessa: Per la realizzazione di un widget, è necessario scrivere alcune funzioni.
In GTK+, ogni oggetto ha un tipo, e ogni tipo è associato ad un intero, che
deve essere unico. Quando si scrive un nuovo widget è necessario scrivere
una funzione che restituisca il tipo del nuovo oggetto. La funzione
Vediamo ora come avvengono l'inizializzazione della classe e dell'oggetto.
Per inizializzare la classe, GTK+ verifica che tutte le classi superiori siano state
inizializzate, e in caso negativo le inizializza in modo opportuno; quindi effettua
una copia byte per byte della classe genitore nella classe derivata. In questo modo,
la classe derivata eredita i puntatori a funzione della classe superiore (utilizzabili, come
accennato precedentemente, tramite cast). Infine viene inizializzata la classe derivata.
Se volessimo sostituire una funzione ereditata con una nostra funzione, basterebbe
sostituire i puntatori a funzione nella nostra classe in modo appropriato.
void (* show) (GtkWidget *widget);
Se volessimo sostituire la funzione che ci mostra il widget, con la
nostra funzione
Oltre al costruttore, ogni widget deve avere un distruttore. La funzione distruttore è definita così in GtkObject: void (*destroy) (GtkObject *object);
Come potete osservare nella funzione che inizializza
la mia classe, la funzione ereditata da GtkObject è stata sostituita con una
funzione appropriata, che sa come distruggere il widget (ancora da terminare :).
object_class->destroy = modulo_rubrica_destroy;
In questo modo ogni volta che si distrugge il modulo_rubrica, viene richiamata la
funzione puntata da Dopo la classe viene inizializzato l'oggetto. Questa funzione è eseguita ogni volta che un nuovo oggetto viene istanziato. Per finire un accenno alle macro che effettuano il cast. #define MODULO_RUBRICA_TYPE (modulo_rubrica_get_type()) #define MODULO_RUBRICA(obj) (GTK_CHECK_CAST((obj), MODULO_RUBRICA_TYPE, ModuloRubrica)) #define MODULO_RUBRICA_CLASS(klass) (GTK_CHECK_CLASS_CAST((klass), MODULO_RUBRICA_TYPE, ModuloRubricaClass)) #define IS_MODULO_RUBRICA(obj) (GTK_CHECK_TYPE((obj), MODULO_RUBRICA_TYPE)) #define IS_MODULO_RUBRICA_CLASS(klass) (GTK_CHECK_CLASS_TYPE((klass), MODULO_RUBRICA_TYPE)) Queste macro permettono, ad esempio, di verificare se il widget passato ad una funzione è del tipo corretto; ad es: void foo (ModuloRubrica *modulo) { g_return_if_fail(IS_MODULO_RUBRICA(modulo)); ... }foo si aspetta come parametro un puntatore a ModuloRubrica, quindi a run time le librerie di GTK+ effettuano una verifica sul tipo del puntatore passato, utilizzando la macro IS_MODULO_RUBRICA() . Come detto nel
precedente articolo, il nostro programma sta per la maggior parte del tempo a non
fare nulla, o meglio esegue un loop infinito, in attesa che avvenga un qualche evento
e di passare il controllo dell'applicazione ad una qualche funzione di callback, per ritornare
poi nel ciclo principale dell'applicazione (gtk_main). Ora se nella funzione foo il test sul tipo
è positivo l'elaborazione continua normalmente, altrimenti ritorna nel loop gtk_main().
Tramite queste macro è possibile accedere alle classi genitrici.
Come già detto, ModuloRubrica è derivato da GnomeDialog, che è
una finestra di dialogo con tre bottoni alla base. Per poter accedere ai bottoni, è
stato necessario ricorrere ad una macro per convertire ModuloRubrica in uno GnomeDialog, e
accedere così agli elementi della classe genitore.
In conclusione, se volete scrivere un widget per GTK+ (o gnome) avete bisogno almeno di:
GtkType modulo_rubrica_get_type(void) { static guint modulo_rubrica_type = 0; if (!modulo_rubrica_type) { static const GtkTypeInfo modulo_rubrica_info = { "ModuloRubrica", sizeof(ModuloRubrica), sizeof(ModuloRubricaClass), (GtkClassInitFunc) modulo_rubrica_class_init, (GtkObjectInitFunc) modulo_rubrica_init, NULL, /* riservato per compatibilità */ NULL, /* riservato per compatibilità */ (GtkClassInitFunc) NULL }; modulo_rubrica_type = gtk_type_unique(gnome_dialog_get_type(), &modulo_rubrica_info); } return modulo_rubrica_type; }; torna su /* inizializzazione della classe ModuloRubrica */ static void modulo_rubrica_class_init(ModuloRubricaClass *klass) { GtkObjectClass *object_class; GtkWidgetClass *widget_class; GtkWindowClass *window_class; object_class = (GtkObjectClass*) klass; widget_class = (GtkWidgetClass*) klass; window_class = (GtkWindowClass*) klass; object_class->destroy = modulo_rubrica_destroy; parent_class = gtk_type_class(gnome_dialog_get_type()); /* segnali gestiti dalla classe */ modulo_rubrica_signals[HELP] = gtk_signal_new ("help", GTK_RUN_LAST, object_class->type, GTK_SIGNAL_OFFSET(ModuloRubricaClass, modulo_rubrica_aiuto), modulo_rubrica_marshal_signal, GTK_TYPE_NONE, 1, GTK_TYPE_INT); gtk_object_class_add_signals (object_class, modulo_rubrica_signals, LAST_SIGNAL); klass->modulo_rubrica_aiuto = NULL; } static void modulo_rubrica_marshal_signal (GtkObject *object, GtkSignalFunc func, gpointer func_data, GtkArg *args) { ModuloRubricaSignal rfunc; rfunc = (ModuloRubricaSignal) func; (*rfunc) (object, GTK_VALUE_INT(args[0]), func_data); } torna su /* inizializzazione della struttura dati ModuloRubrica */ static void modulo_rubrica_init (ModuloRubrica *modulo) { /* bottoni globali al widget */ GList *button_list; /* frame */ GtkWidget *frame_dati; GtkWidget *frame_rete; GtkWidget *frame_telefono; GtkWidget *frame_note; /* etichette notebook */ GtkWidget *label_dati; GtkWidget *label_rete; GtkWidget *label_telefono; GtkWidget *label_note; modulo->notebook = gtk_notebook_new(); /* bottoni globali alla finestra di dialogo modulo */ gnome_dialog_append_buttons(GNOME_DIALOG(modulo), GNOME_STOCK_BUTTON_OK, GNOME_STOCK_BUTTON_HELP, GNOME_STOCK_BUTTON_CLOSE, NULL); su /* bottone di default: -- ok */ gnome_dialog_set_default (GNOME_DIALOG(modulo), 0); ... gtk_widget_show (modulo->notebook); } torna su static void modulo_rubrica_destroy(GtkObject *object) { ModuloRubrica *modulo; g_return_if_fail (object != NULL); g_return_if_fail (IS_MODULO_RUBRICA(object)); modulo = MODULO_RUBRICA(object); GTK_OBJECT_CLASS(parent_class)->destroy (object); } torna su GtkWidget * modulo_rubrica_new(MODO_MODULO modo) { GtkWidget *modulo; if (count == 0) { modulo = gtk_type_new(modulo_rubrica_get_type()); modulo_rubrica_set_modulo(modulo); } else { modulo = modulo_rubrica_get_modulo(); modulo_rubrica_clean_modulo(MODULO_RUBRICA(modulo)); } MODULO_RUBRICA(modulo)->modo = modo; ... return GTK_WIDGET(modulo); } torna su |
<- PW: Alle radici dello GNOME - Prima Puntata - Copertina - PW: Come ti metto in moto il telescopio -> |