<- PW: Intro - Copertina - PW: MultiGnomeTerminal -> |
PlutoWare
L'articoloSiamo arrivati alla terza puntata del nostro corso sulla programmazione sotto GTK+, le librerie su cui si basa GNOME. In questa puntata termineremo la descrizione dei widget contenitori, trattando in modo più approfondito le tabelle; introdurremo alcuni bottoni molto utili, e presenteremo qualche widget decorativo, in modo da rendere le nostre applicazioni più belle da vedere ( anche l'occhio vuole la sua parte! :-) ). Faremo questo introducendo e analizzando un programma di prova; come sempre, questo semplificherà molto la comprensione dei meccanismi, fornendo un esempio pratico e semplice che potrete poi adattare alle vostre applicazioni. I widget trattati in questa puntata sono molto eterogenei, ma osserveremo come con pochi elementi sia possibile costruire qualcosa che somigli, vagamente ;-), ad un pannello di controllo per una applicazione, il tutto senza disdegnare qualche "abbellimento". |
Abbiamo visto nelle puntate precedenti come sia semplice costruire una applicazione grafica utilizzando le librerie di GTK+. Prima di continuare, se ancora non lo avete fatto, vi invito a scaricare le API Reference di GTK+ (http://developer.gnome.org/doc/API/gtk-docs.tar.gz) , GLib (http://developer.gnome.org/doc/API/glib-docs.tar.gz) e GDK (http://developer.gnome.org/doc/API/gdk-docs.tar.gz)
#include <gtk/gtk.h> /* la variabile globale cambia viene modificata ad ogni pressione del toggle button */ gboolean cambia = TRUE; /* *** le funzioni di callback *** */ /* on_checkbutton1_clicked viene richiamata quando si clicca sul primo check button */ void on_checkbutton1_clicked (GtkButton *button, gpointer user_data) { gboolean bool; bool = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)); if (bool) { gtk_tooltips_disable(GTK_TOOLTIPS(user_data)); g_print("\nHai scelto di disabilitare i suggerimenti"); } else { gtk_tooltips_enable(GTK_TOOLTIPS(user_data)); g_print("\nOk, ripristiniamo i suggerimenti"); } } void on_checkbutton2_clicked(GtkButton *button, gpointer user_data) { gboolean bool; bool = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)); g_print("\nl'opzione n° 2 %s settata", bool ? "è" : "non è"); } /* le funzioni di callback associate ai radio button: */ void on_radiobutton1_clicked(GtkButton *button, gpointer user_data) { GtkWidget *frame = GTK_WIDGET(user_data); if (cambia) { gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE); gtk_frame_set_label(GTK_FRAME(frame), " Ombra attiva: GTK_SHADOW_NONE "); } } void on_radiobutton2_clicked(GtkButton *button, gpointer user_data) { GtkFrame *frame = GTK_FRAME(user_data); if (cambia) { gtk_frame_set_shadow_type(frame, GTK_SHADOW_IN); gtk_frame_set_label(frame, " Ombra attiva: GTK_SHADOW_IN "); } } void on_radiobutton3_clicked(GtkButton *button, gpointer user_data) { if (cambia) { gtk_frame_set_shadow_type(GTK_FRAME(user_data), GTK_SHADOW_OUT); gtk_frame_set_label(GTK_FRAME(user_data), " Ombra attiva: GTK_SHADOW_OUT "); } } void on_radiobutton4_clicked (GtkButton *button, gpointer user_data) { GtkFrame *frame = GTK_FRAME(user_data); if (cambia) { gtk_frame_set_shadow_type(frame, GTK_SHADOW_ETCHED_IN); gtk_frame_set_label(frame, " Ombra attiva: GTK_SHADOW_ETCHED_IN "); } } void on_radiobutton5_clicked (GtkButton *button, gpointer user_data) { GtkFrame *frame = GTK_FRAME(user_data); if (cambia) { gtk_frame_set_shadow_type(frame, GTK_SHADOW_ETCHED_OUT); gtk_frame_set_label(frame, " Ombra attiva: GTK_SHADOW_ETCHED_OUT "); } } /* la funzione di callback per il toggle button: verifica lo stato del bottone e setta la variabile globale "cambia" di conseguenza */ void on_togglebutton_clicked (GtkButton *button, gpointer user_data) { gboolean bool; bool = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)); if (bool) { g_print("\nCon il togglebutton settato non è"); g_print("\npermesso cambiare l'ombreggiatura al frame"); cambia = FALSE; } else { g_print("\nIl togglebutton non è selezionato. Puoi fare ciò che vuoi"); cambia = TRUE; } } int main (int argc, char *argv[]) { /* prendete: */ GtkWidget *window; /* la finestra principale dell'applicazione */ GtkWidget *frame1; /* dei bordi decorativi */ GtkWidget *frame2; GtkWidget *vbox1; /* qualche scatola */ GtkWidget *vbox2; GtkWidget *hbox1; GtkWidget *hbox2; GtkWidget *tabella; /* una tabella */ GtkWidget *etichetta; /* una etichetta, per comunicare con l'utente */ GtkWidget *togglebutton; /* un toggle button */ GtkWidget *checkbutton1; /* qualche bottone check */ GtkWidget *checkbutton2; GSList *gruppo_radio = NULL; /* bottoni radio quanto bastano, e il loro gruppo */ GtkWidget *radiobutton1; GtkWidget *radiobutton2; GtkWidget *radiobutton3; GtkWidget *radiobutton4; GtkWidget *radiobutton5; GtkWidget *vseparator; /* altri widget decorativi */ GtkWidget *hseparator; GtkWidget *esci; /* un bottone di uscita */ GtkTooltips *tooltip; /* e se dessimo qualche suggerimeto? */ /* mescolate con cura ed otterrete... */ gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_title (GTK_WINDOW (window), "Bottoni e decorazioni!!"); /* creiamo una cornice con l'etichetta "Bottoni e decorazioni" */ frame1 = gtk_frame_new (" Bottoni e decorazioni "); gtk_container_set_border_width (GTK_CONTAINER (frame1), 5); /* impacchettiamo la cornice direttamente nella finestra principale */ gtk_container_add (GTK_CONTAINER (window), frame1); /* mostriamola */ gtk_widget_show (frame1); /* creiamo un tooltips */ tooltip = gtk_tooltips_new(); /* creiamo alcune scatole, che ci permetteranno di disporre i widget della nostra applicazione */ vbox1 = gtk_vbox_new (FALSE, 5); gtk_container_add (GTK_CONTAINER (frame1), vbox1); gtk_container_set_border_width (GTK_CONTAINER (vbox1), 5); gtk_widget_show (vbox1); vbox2 = gtk_vbox_new (FALSE, 5); gtk_box_pack_start (GTK_BOX (vbox1), vbox2, FALSE, FALSE, 0); gtk_container_set_border_width (GTK_CONTAINER (vbox2), 5); gtk_widget_show (vbox2); /* hbox1 conterrà i check button */ hbox1 = gtk_hbox_new (TRUE, 5); gtk_box_pack_start (GTK_BOX (vbox2), hbox1, FALSE, FALSE, 0); gtk_container_set_border_width (GTK_CONTAINER (hbox1), 5); gtk_widget_show (hbox1); /* creiamo un check button, lo impacchettiamo in hbox1 e lo mostriamo */ checkbutton1 = gtk_check_button_new_with_label("Nascondi i suggerimenti"); gtk_box_pack_start (GTK_BOX (hbox1), checkbutton1, FALSE, FALSE, 0); gtk_widget_show (checkbutton1); checkbutton2 = gtk_check_button_new_with_label("Opzione n. 2"); gtk_box_pack_start (GTK_BOX (hbox1), checkbutton2, FALSE, FALSE, 0); gtk_widget_show (checkbutton2); /* un altro frame */ frame2 = gtk_frame_new (" Ombra attiva: "); gtk_widget_show (frame2); gtk_box_pack_start (GTK_BOX (vbox2), frame2, TRUE, TRUE, 0); /* una tabella di 2 righe x 3 colonne, ci consentirà di disporre una serie di widget in un modo gradevole da vedere: come esercizio provate a sostituire la tabella con delle scatole (hbox e vbox) e osservate le differenze estetiche. Usate una scatola verticale nella quale impacchetterere tre scatole orizzontali, nelle scatole orizzontali inserite rispettivamente 2, 2 e 1 radio button */ tabella = gtk_table_new (2, 3, FALSE); gtk_widget_show (tabella); gtk_container_add (GTK_CONTAINER (frame2), tabella); /* i radio button */ /* creiamo un radio button con etichetta "nessuna" e lo associamo al gruppo "gruppo_radio" */ radiobutton1 = gtk_radio_button_new_with_label (gruppo_radio, "nessuna"); gruppo_radio = gtk_radio_button_group (GTK_RADIO_BUTTON (radiobutton1)); /* il radio button creato lo inseriamo nella prima cella in alto a sinistra della tabella */ gtk_table_attach (GTK_TABLE (tabella), radiobutton1, 0, 1, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); /* lo mostriamo e */ gtk_widget_show (radiobutton1); /* gli associamo un tooltip, che verrà visualizzato ogni volta che il puntatore del mouse gli si fermerà su */ gtk_tooltips_set_tip(tooltip, radiobutton1, "Non voglio ombre!!", NULL); radiobutton2 = gtk_radio_button_new_with_label (gruppo_radio, "interna"); gruppo_radio = gtk_radio_button_group (GTK_RADIO_BUTTON (radiobutton2)); gtk_table_attach (GTK_TABLE (tabella), radiobutton2, 1, 2, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_widget_show (radiobutton2); gtk_tooltips_set_tip(tooltip, radiobutton2, "Dammi un'ombra interna", NULL); radiobutton3 = gtk_radio_button_new_with_label (gruppo_radio, "esterna"); gruppo_radio = gtk_radio_button_group (GTK_RADIO_BUTTON (radiobutton3)); gtk_table_attach (GTK_TABLE (tabella), radiobutton3, 2, 3, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_widget_show (radiobutton3); gtk_tooltips_set_tip(tooltip, radiobutton3, "Applica un'ombreggiatura esterna", NULL); radiobutton4 = gtk_radio_button_new_with_label (gruppo_radio, "scolpita interna"); gruppo_radio = gtk_radio_button_group (GTK_RADIO_BUTTON (radiobutton4)); gtk_table_attach (GTK_TABLE (tabella), radiobutton4, 0, 1, 1, 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_widget_show (radiobutton4); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radiobutton4), TRUE); gtk_tooltips_set_tip(tooltip, radiobutton4, "L'ombreggiatura scolpita interna, è il default", NULL); radiobutton5 = gtk_radio_button_new_with_label (gruppo_radio, "scolpita esterna"); gruppo_radio = gtk_radio_button_group (GTK_RADIO_BUTTON (radiobutton5)); gtk_table_attach (GTK_TABLE (tabella), radiobutton5, 1, 2, 1, 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_widget_show (radiobutton5); gtk_tooltips_set_tip(tooltip, radiobutton5, "Proviamo anche l'ombreggiatura scolpita esterna", NULL); hbox2 = gtk_hbox_new (TRUE, 0); gtk_box_pack_start (GTK_BOX (vbox2), hbox2, FALSE, FALSE, 0); gtk_widget_show (hbox2); etichetta = gtk_label_new ("Un togglebutton"); gtk_box_pack_start (GTK_BOX (hbox2), etichetta, TRUE, TRUE, 0); gtk_widget_show (etichetta); vseparator = gtk_vseparator_new (); gtk_box_pack_start (GTK_BOX (hbox2), vseparator, FALSE, FALSE, 0); gtk_widget_show (vseparator); togglebutton = gtk_toggle_button_new_with_label ("Cliccami"); gtk_box_pack_start (GTK_BOX (hbox2), togglebutton, TRUE, TRUE, 0); gtk_widget_show (togglebutton); gtk_tooltips_set_tip(tooltip, togglebutton, "Se attivo, il togglebutton impedisce il cambiamento delle ombre", NULL); hseparator = gtk_hseparator_new (); gtk_box_pack_start (GTK_BOX (vbox1), hseparator, FALSE, FALSE, 0); gtk_widget_show (hseparator); esci = gtk_button_new_with_label ("Chiudi"); gtk_box_pack_start (GTK_BOX (vbox1), esci, FALSE, FALSE, 0); gtk_widget_show (esci); gtk_tooltips_set_tip(tooltip, esci, "Fammi uscire da questo programma.", NULL); /* Le funzioni callback */ gtk_signal_connect (GTK_OBJECT (window), "delete_event", GTK_SIGNAL_FUNC (gtk_main_quit), NULL); gtk_signal_connect (GTK_OBJECT (esci), "clicked", GTK_SIGNAL_FUNC (gtk_main_quit), NULL); gtk_signal_connect (GTK_OBJECT (checkbutton1), "clicked", GTK_SIGNAL_FUNC (on_checkbutton1_clicked), tooltip); gtk_signal_connect (GTK_OBJECT (checkbutton2), "clicked", GTK_SIGNAL_FUNC (on_checkbutton2_clicked), NULL); gtk_signal_connect (GTK_OBJECT (radiobutton1), "clicked", GTK_SIGNAL_FUNC (on_radiobutton1_clicked), frame2); gtk_signal_connect (GTK_OBJECT (radiobutton2), "clicked", GTK_SIGNAL_FUNC (on_radiobutton2_clicked), frame2); gtk_signal_connect (GTK_OBJECT (radiobutton3), "clicked", GTK_SIGNAL_FUNC (on_radiobutton3_clicked), frame2); gtk_signal_connect (GTK_OBJECT (radiobutton4), "clicked", GTK_SIGNAL_FUNC (on_radiobutton4_clicked), frame2); gtk_signal_connect (GTK_OBJECT (radiobutton5), "clicked", GTK_SIGNAL_FUNC (on_radiobutton5_clicked), frame2); gtk_signal_connect (GTK_OBJECT (togglebutton), "clicked", GTK_SIGNAL_FUNC (on_togglebutton_clicked), NULL); gtk_widget_show (window); gtk_main (); g_print("\nContinua...\n\n"); return 0; } |
Per compilare il programma utilizzate il comando:
gcc -Wall -g bottoni.c -o bottoni `gtk-config --libs --cflags` |
Ricordiamo che lo script gtk-config si occupa di fornire al preprocessore C e al linker gli opportuni percorsi per gli header da includere e per le librerie da linkare.
Lanciate l'eseguibile da una finestra di terminale; il programma stamperà a video alcuni messaggi, che aiuteranno a comprenderne l'esecuzione.
Iniziamo ad esaminare il codice, partendo sempre dalla funzione main():
frame1 = gtk_frame_new (" Bottoni e decorazioni "); gtk_container_set_border_width (GTK_CONTAINER (frame1), 5); gtk_container_add (GTK_CONTAINER (window), frame1); gtk_widget_show (frame1); |
il primo nuovo statement GTK+ che si incontra è il gtk_frame_new()
, che istanzia un
widget che non avevamo mai incontrato: il Frame. Un Frame è fondamentalmente una cornice,
utilizzata per racchiudere uno o più widget; su di essa può inoltre essere applicata un'etichetta (figura 1).
Il codice qui sopra creerà una cornice contenente l'etichetta "Bottoni e decorazioni"; se si
desiderasse di non avere etichette nel frame sarebbe sufficiente passare NULL come parametro
alla funzione, nel qual caso verrebbe creato un semplice rettangolo (figura 2).
Figura 1: un frame con etichette |
Figura 2: un frame senza etichette |
La chiamata a funzione è la seguente:
GtkWidget *gtk_frame_new(const gchar *label); |
Durante l'elaborazione, potrebbe presentarsi la necessità di dover cambiare l'etichetta contenuta nel frame; in questo caso si ricorre a:
void gtk_frame_set_label(GtkFrame *frame, const gchar *label); |
Il frame può essere disegnato in 5 modi diversi, a seconda del tipo di ombreggiatura che si vuole impostare; la funzione preposta alla variazione delle ombre è:
void gtk_frame_set_shadow_type(GtkFrame *frame, GtkShadowType type); |
Pensate il frame come una linea che può essere sottoposta ad una sorgente di luce: cambiando la posizione della sorgente, si otterranno diversi tipi di ombra. Ad esempio illuminando la retta da sinistra, la sua ombra sarà sulla sua destra. I tipi di ombra disponibili sono 5 e sono definiti nel modo seguente:
GTK_SHADOW_NONE GTK_SHADOW_IN GTK_SHADOW_OUT GTK_SHADOW_ETCHED_IN GTK_SHADOW_ETCHED_OUT
Vediamo in dettaglio le loro caratteristiche:
Altri elementi decorativi presenti in questo programma sono i separatori, che sostanzialmente si limitano a disegnare una linea. Sono dei widget veramente semplici da usare, basta crearli, impacchettarli e mostrarli! Possono essere di due tipi, orizzontali e verticali. Le funzioni da richiamare per creare un separatore sono le seguenti:
GtkWidget *gtk_vseparator_new (); GtkWidget *gtk_hseparator_new (); |
La prima crea un separatore verticale, la seconda, com'è facile intuire, uno orizzontale (figura 3).
Ecco alcune delle funzioni necessarie alla creazione e alla gestione dei tooltip:
GtkTooltips *gtk_tooltips_new(); void gtk_tooltips_set_tip(GtkTooltips *tooltips, GtkWidget *widget, const gchar *tip_text, const gchar *tip_private); void gtk_tooltips_enable(GtkTooltips *tooltips); void gtk_tooltips_disable(GtkTooltips *tooltips); |
Figura 4 Un tooltip
Un tooltip si crea richiamando gtk_tooltips_new()
, che ci ritornerà un puntatore all'oggetto tooltip.
Per collegare il tooltip al widget si utilizza la funzione gtk_tooltips_set_tip()
. Questa
funzione accetta come primo argomento il puntatore al tooltip, come secondo il puntatore
al widget sul quale vogliamo che il suggerimento appaia, come terzo la stringa che
vogliamo mostrare e infine, come quarto, una stringa che per il momento può essere
tranquillamente impostata a NULL.
Ecco un esempio estratto dal programma allegato:
/* creiamo il tooltip */ tooltip = gtk_tooltips_new(); ... esci = gtk_button_new_with_label ("Chiudi"); gtk_box_pack_start (GTK_BOX (vbox1), esci, FALSE, FALSE, 0); gtk_widget_show (esci); /* associamo al bottone il tooltip con la stringa "Fammi uscire ..." */ gtk_tooltips_set_tip(tooltip, esci, "Fammi uscire da questo programma.", NULL); ... |
Attenzione: diversamente dagli altri widget, il widget tooltips
non ha bisogno di essere
mostrato, quindi non richiamate gtk_widget_show(tooltip)
; inoltre, è sufficiente creare un
solo tooltip, e di volta in volta utilizzando la funzione gtk_tooltips_set_tip()
, si assocerà una
diversa stringa a widget diversi.
E se volessimo scrivere una qualche routine di configurazione per dare all'utente l'opportunità
di abilitare o disabilitare l'uso dei tooltip? Allora utilizzeremo le chiamate gtk_tooltips_enable()
e
gtk_tooltips_disable()
; la prima permette la visualizzazione del tooltip, la seconda la impedisce.
... etichetta = gtk_label_new ("Un togglebutton"); gtk_box_pack_start (GTK_BOX (hbox2), etichetta, TRUE, TRUE, 0); gtk_widget_show (etichetta); ... |
Altro widget che si usa molto ed è molto facile da utilizzare: l'etichetta. Ecco qualche funzione:
GtkWidget *gtk_label_new(gchar *str); void gtk_label_set_text(GtkLabel *label, gchar *str); void gtk_label_get_text(GtkLabel *label, gchar **str); void gtk_label_set_justify(GtkLabel *label, GtkJustification jtype); |
come sempre, è necessario creare il nostro widget chiamando la funzione
gtk_label_new()
; ottenuto il puntatore all'oggetto possiamo lavorarci su.
La funzione gtk_label_new()
accetta come parametro la stringa che dovrà
mostrare; se si dovesse modificare il valore della stringa durante l'elaborazione,
si ricorra alla chiamata gtk_label_set_text()
. Questa funzione accetta il
puntatore al widget etichetta e la nuova stringa da mostrare. Se invece volessimo recuperare
la stringa contenuta nella label, dovremmo usare la funzione gtk_label_get_text()
,
che ritornerà nella variabile str
la stringa attualmente visualizzata. Infine
vediamo l'ultima di queste funzioni, gtk_label_set_justify()
. Trattandosi di etichette,
è naturale cercare di allinearle. I valori che è possibile passare come jtype
sono i
seguenti, e il significato dovrebbe essere intuitivo a chiunque abbia mai usato un word processor:
GTK_JUSTIFY_LEFT GTK_JUSTIFY_RIGHT GTK_JUSTIFY_CENTER GTK_JUSTIFY_FILL
Come accennato la volta scorsa, GTK+ oltre alla scatola ci mette a disposizione un altro contenitore, la tabella. Le tabelle sono formate da un'insieme di celle (Figura 5), all'interno delle quali è possibile inserire un widget (o un insieme di widget composti fra loro).
... tabella = gtk_table_new (2, 3, FALSE); gtk_widget_show (tabella); gtk_container_add (GTK_CONTAINER (frame2), tabella); ... |
due funzioni:
GtkWidget *gtk_table_new(gint rows, gint columns, gboolean homogeneous); void gtk_table_attach(GtkTable *table, GtkWidget *child, gint left_attach, gint right_attach, gint top_attach, gint bottom_attach, GtkAttachOptions xoptions, GtkAttachOptions yoptions, gint xpadding, gint ypadding); |
La prima funzione crea una nuova tabella, di dimensione rows
x columns
;
homogeneous
è un valore booleano, se impostato a TRUE
renderà la
grandezza delle celle omogenea, farà in modo, cioè, che tutte le celle abbiano dimensione uguale
a quella della cella di dimensione massima; se homogeneous
fosse settato a
FALSE
, allora ogni cella avrebbe avuto la dimensione minima necessaria per lei sola.
Quindi se volessimo creare una tabella 2x3 (di due righe e tre colonne) omogenea, bisognerebbe
richiamare gtk_table_new(2, 3, TRUE);
L'angolo in alto a sinistra della tabella
avrà coordinate (0, 0), la cella contenuta nella prima riga/prima colonna avrà quindi coordinate (0, 0). (Figura 5)
Questo sistema di coordinate è utilizzato nella seconda funzione, che si occupa di inserire il
widget nella cella scelta. Vediamola in dettaglio: table
è naturalmente la tabella,
child
è il widget che vogliamo inserire in essa (un bottone, una etichetta, una
pixmap, ...), left_attach
, right_attach
, top_attach
e
bottom_attach
definiscono le coordinate del punto in cui attaccare il widget.
left
e right
definiscono anche quante celle il widget deve occupare:
se infatti volessimo che il nostro widget occupi tutta la prima riga della tabella 2x3
precedentemente creata, allora left_attach
avrebbe valore 0, mentre
right_attach
avrebbe valore 3; se avessimo voluto occupare solo la prima cella,
allora right_attach
avrebbe avuto valore 1. Trattandosi della prima riga, i valori di
top e bottom attach sarebbero stati in ogni caso 0 e 1, rispettivamente. xoptions
e
yoptions
specificano come attaccare il widget. I possibili valori che possono assumere (e che è possibile combinare con un or, mediante l'operatore "|
"
in modo da avere più proprietà contemporaneamente)
sono i seguenti:
GTK_FILL GTK_SHRINK GTK_EXPANDcon
GTK_FILL
si permetterà al widget di espandersi e di utilizzare
tutto lo spazio disponibile, nel caso in cui la cella fosse più grande del widget stesso.
GTK_SHRINK
farà restringere il widget nel caso in cui lo spazio
a disposizione della tabella non fosse sufficente. GTK_EXPAND
, infine,
permetterà alla tabella di espandersi e di occupare tutto lo spazio in eccesso nella finestra.
xpadding
e ypadding
indicano quanti pixel lasciare
attorno al widget come spazio vuoto.
I toggle button sono derivati dai normali bottoni ed hanno un aspetto simile al loro, ma ciò che li differenzia dai bottoni è il fatto che possono assumere due stati (premuto o rilasciato); mantengono il loro stato finchè esso non viene esplicitamente modificato. Le funzioni che gestiscono i toggle button sono le seguenti:
GtkWidget *gtk_toggle_button_new( void ); GtkWidget *gtk_toggle_button_new_with_label(gchar *label ); gboolean gtk_toggle_button_get_active(GtkToggleButton *toggle_button); void gtk_toggle_button_set_active(GtkToggleButton *toggle_button, gint state); |
gtk_toggle_button_new()
e gtk_toggle_button_new_with_label()
creano il nuovo oggetto; si differenziano nel fatto che la prima crea un toggle button vuoto,
e toccherà a noi inserirvi un'etichetta o un'immagine; la seconda invece crea un bottone
con l'etichetta passatagli. Il toggle button è utile soprattutto per la capacità di ricordare e mantenere
il proprio stato. Esteticamente (ma questo è un giudizio del tutto personale) sono da preferire
i check button ai toggle button. Le proprietà del toggle button (memoria dello stato) sono ereditate
dai check button e dai radio button.
Per verificare se un toggle button è premuto o meno si utilizza la funzione
gtk_toggle_button_get_active()
, la quale ritornerà un valore booleano,
TRUE
se il bottone che è passato alla funzione è premuto,
FALSE
altrimenti. Naturalmente è anche possibile modificare lo stato del
bottone senza cliccarlo. Ad esempio, immaginiamo di dover scrivere una routine di configurazione,
e di voler settare il toggle button a TRUE
per default. Per far ciò si ricorre
alla funzione gtk_toggle_button_set_active()
, questa funzione accetta come primo
argomento il puntatore al bottone da modificare, e come secondo un valore booleano, TRUE
se vogliamo il bottone premuto, FALSE
se lo vogliamo rilasciato.
I check button, come accennato, ereditano dai toggle button la proprietà di ricordare il proprio stato, ma esteticamente sono molto diversi. Si presentano come un quadratino con a fianco, a scelta, una stringa; il bottone verrà selezionato cliccando nel quadratino. (Figura 6). Esistono solo due funzioni per i check button, le seguenti:
GtkWidget *gtk_check_button_new( void ); GtkWidget *gtk_check_button_new_with_label ( gchar *label ); |
la prima, gtk_check_button_new()
quando richiamata crea un check button senza etichetta
e ne ritorna il puntatore; la seconda gtk_check_button_new_with_label()
crea il
check button con etichetta allegata.
Ma come possiamo usare i check button, se le uniche funzioni a disposizione sono queste? La risposta è nella struttura ad oggetti di GTK+: osserviamo la gerarchia dell'oggetto check button
GtkObject +----GtkWidget +----GtkContainer +----GtkBin +----GtkButton +----GtkToggleButton +----GtkCheckButton
GtkCheckButton è derivato da GtkToggleButton, quindi eredita da quest'ultimo tutte le proprietà; dunque, per verificare lo stato di un check button utilizzeremo la funzione che verifica lo stato di un toggle button. Osserviamo le funzioni di callback associate ai due check button (ricordiamo che il primo argomento della funzione di callback è un puntatore all'oggetto che ha emesso il segnale che ha fatto richiamare la funzione, mentre il secondo è un puntatore agli eventuali dati da passare alla funzione):
void on_checkbutton1_clicked (GtkButton *button, gpointer user_data) { gboolean bool; bool = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)); ... |
button è il nostro check button, derivato dai toggle button, quindi per conoscere il suo stato applichiamo la macro
GTK_TOGGLE_BUTTON
, usate per trasformarlo in toggle button, e utilizziamo l'opportuna funzione
per recuperarne lo stato. Naturalmente possiamo utilizzare lo stesso procedimento per settare lo stato del check button
a TRUE
o FALSE
, ad esempio con
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE)
si setterà il check button.
I radio button si differenziano dai check button, oltre che per la forma (figura 7), anche per il fatto di essere utilizzati in gruppi; solo uno dei radio button appartenenti ad un determinato gruppo può essere attivo in un determinato istante. Utilizzando i radio button si può far effettuare all'utente una scelta fra più opzioni. Sono solo un po' più difficili da utilizzare dei check button, proprio per il fatto di dover considerare il gruppo di appartenenza dei bottoni. Ecco alcune funzioni che creano i radio button e il gruppo di appartenenza:
GtkWidget *gtk_radio_button_new(GSList *group); GtkWidget *gtk_radio_button_new_with_label(GSList *group, gchar *label); GSList *gtk_radio_button_group(GtkRadioButton *radio_button); |
Figura 7 I radio button
Per la creazione di un radio button si può utilizzare gtk_radio_button_new()
o
gtk_radio_button_new_with_label()
, la prima creerà un radio button, la seconda
affiancherà al radio button un'etichetta. Entrambe le funzioni richiedono un puntatore a GSList.
Ma cos'è una GSList? Come accennato nel primo articolo, una delle librerie su cui si appoggia GTK+,
Glib, mette a disposizione funzioni e strutture dati per trattare liste concatenate, alberi, tabelle hash. Sono
disponibili due tipi di liste concatenate (linked list): le liste semplici, appunto le GSList (in cui un elemento punta al successivo) e
liste doppie, le GList (nelle quali un elemento punta sia all'elemento successivo, sia a quello
precedente).
Ogni volta che si crea un nuovo radio button è necessario chiamare la funzione
gtk_radio_button_group()
, in modo da associare il bottone appena creato al
relativo gruppo di appartenenza. Inoltre, è opportuno che il puntatore alla lista del gruppo
sia inizializzato a NULL, la prima volta che si creano gruppo e primo radio button.
Osserviamo in basso alcune righe di codice del programma di prova: la variabile del gruppo
viene inizializzata nel momento della sua dichiarazione, poi si crea il primo dei radio button
che andranno a far parte del gruppo "gruppo_radio", e si inserisce il puntatore al bottone
nella lista gruppo_radio. Questo procedimento si ripeterà per ogni bottone che appartiene
al gruppo "gruppo_radio". Se la nostra applicazione prevede più gruppi di radio button, tutte
queste operazioni vanno replicate per tutti i gruppi e per tutti i bottoni di ogni gruppo.
... GSList *gruppo_radio = NULL; ... radiobutton1 = gtk_radio_button_new_with_label (gruppo_radio, "nessuna"); gruppo_radio = gtk_radio_button_group (GTK_RADIO_BUTTON (radiobutton1)); ... radiobutton2 = gtk_radio_button_new_with_label (gruppo_radio, "interna"); gruppo_radio = gtk_radio_button_group (GTK_RADIO_BUTTON (radiobutton2)); ... |
E' inoltre opportuno settare un bottone di ogni gruppo come bottone di default. Analogamente
ai check button, per poter accedere al valore del bottone ed eventualmente modificarlo
si utilizzano le funzioni definite per i toggle button, applicando al puntatore al radio button
l'opportuno cast. Quindi se vogliamo, ad esempio, che radiobutton1
sia il bottone che appare
selezionato all'avvio dell'applicazione, utilizzeremo una chiamata come questa:
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radiobutton1), TRUE);
.
Se vogliamo sapere se radiobutton2
è attivo utilizzeremo la chiamata
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radiobutton2));
, che ci
ritornerà un valore booleano (TRUE
se il bottone è selezionato,
FALSE
se non lo è).
Siamo così giunti alla conclusione anche di questo articolo. In attesa del prossimo, vi invito ad esercitarvi, modificando il programma allegato. Provate ad esempio a far sì che uno qualunque dei widget della finestra "si nasconda" alla pressione di un bottone. Non dovrebbe essere difficile (leggete anche il primo articolo, se non ricordate qualcosa)! Ancora, provate a cambiare l'etichetta di un bottone, sempre alla pressione di un altro bottone. Suggerimento: intercettate il segnale "clicked" di un bottone e passate alla funzione di callback il puntatore al widget da nascondere o l'etichetta da modificare, e nella funzione callback mostrate/nascondete il widget o modificate l'etichetta. Alla prossima!
L'autoreNicola Fragale. E' iscritto a Ingegneria Informatica al Federico II di Napoli. I suoi ultimi interessi informatici sono rivolti all'XML e particolarmente a GConf. Attualmente sta scrivendo una rubrica per GTK+ e GNOME, naturalmente rilasciata sotto licenza GPL. Nel tempo libero gli piace programmare, leggere (Stephen King l'autore preferito), stare con la sua ragazza e sentire i suoi amici. |
<- PW: Intro - Copertina - PW: MultiGnomeTerminal -> |