<- PW: Intro - Copertina - PW: Introduzione al Pascal -> |
PlutoWare
L'articoloSiamo giunti alla fine di questa serie di articoli che ci ha introdotti nel magico mondo di GTK+. Questo mese vedremo come si utilizzano i notebook, i widget entry, gli spin button e le progress bar. Saranno accennati, molto velocemente, alcuni widget come le clist (columned list) e text, che dalla prossima versine delle librerie GTK+ subiranno profonde modifiche e migliorie. In attesa delle nuove fiammanti GTK+2.0, nei prossimi mesi, faremo conoscenza con il modo di Gnome. Intanto anche questo mese, come consuetudine, esamineremo un programma di prova che ci faciliterà la comprensione delle funzioni di libreria utilizzate. |
Terminiamo, con questa puntata, la presentazione dei principali widget messici a disposizione da GTK+. Con gli elementi che conosciamo dovremmo essere in grado di realizzare piccole applicazioni grafiche. Si, lo so, ;-) non ho dimenticato menu e toolbar, ma li affronteremo con Gnome, che permette di trattarli in modo più semplice e uniforme rispetto a GTK+.
Ricordo i link alle API Reference di GTK+ 1.2:
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 <stdio.h> #include <gtk/gtk.h> GtkWidget *text; /* funzioni ausiliarie richiamate da on_load_clicked e on_save_clicked */ void carica_file (GtkFileSelection *fs, gpointer user_data) { gchar *filename; FILE *fp; /* recuperiamo il nome del file dal widget file selection */ filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION(user_data)); fp = fopen(filename, "r"); while (!feof(fp)) { gchar buffer[1024]; fgets(buffer, 1024, fp); /* visualizziamo il contenuto del buffer */ gtk_text_insert(GTK_TEXT(text), NULL, NULL, NULL, buffer, -1); } fclose(fp); } void salva_file (GtkFileSelection *fs, gpointer user_data) { gchar *filename; gchar *buffer; FILE *fp; filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION(user_data)); fp = fopen(filename, "w"); /* recuperiamo tutto (0, -1) il testo contenuto del widget text */ buffer = gtk_editable_get_chars(GTK_EDITABLE(text), 0, -1); fprintf(fp, "%s", buffer); fflush(fp); fclose(fp); } /* callback richiamata alla pressione del tasto invio nel widget entry */ void on_entry_activate (GtkEditable *editable, gpointer user_data) { gchar *tmp; gchar *buffer[3]; tmp = gtk_entry_get_text(GTK_ENTRY(editable)); buffer[0] = g_strdup(tmp); /* g_strreverse è una funzione contenuta nella libreria glib, il suo scopo è quello di capovolgere la stringa passatagli. es. char *buffer="pippo"; g_strreverse(buffer); ora buffer conterrà la stringa oppip */ g_strreverse(tmp); buffer[1] = g_strdup(tmp); buffer[2] = NULL; /* aggiungiamo la nuova riga alla lista */ gtk_clist_append(GTK_CLIST(user_data), buffer); /* puliamo il widget entry */ gtk_entry_set_text(GTK_ENTRY(editable), ""); } /* callback richiamata quando si seleziona un elemento contenuto nella clist */ void on_clist_select_row (GtkCList *clist, gint row, gint column, GdkEvent *event, gpointer user_data) { gchar *buffer; /* recuperiamo la stringa contenuta alla riga "row" e colonna "column" il risultato è in buffer */ gtk_clist_get_text(GTK_CLIST(clist), row, column, &buffer); gtk_label_set_text(GTK_LABEL(user_data), buffer); } /* callback richiamata quando viene modificato lo stato dello spin button */ void on_spinbutton_changed (GtkWidget *spin, gpointer user_data) { gint val; /* recuperiamo il valore dello spin button */ val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin)); /* e modifichiamo la progress bar di conseguenza */ gtk_progress_set_value(GTK_PROGRESS(user_data), (gfloat) val); } /* le callback relative al notebook */ void on_next_page_clicked (GtkButton *button, gpointer user_data) { /* possiamo spostarci sulla prossima pagina del notebook */ gtk_notebook_next_page(GTK_NOTEBOOK(user_data)); } void on_prev_page_clicked (GtkButton *button, gpointer user_data) { /* oppure sulla pagina precedente */ gtk_notebook_prev_page(GTK_NOTEBOOK(user_data)); } void on_del_page_clicked (GtkButton *button, gpointer user_data) { gint page; /* prendiamo il numero della pagina corrente */ page = gtk_notebook_get_current_page(GTK_NOTEBOOK(user_data)); /* ed eliminiamola */ gtk_notebook_remove_page(GTK_NOTEBOOK(user_data), page); } void on_add_page_clicked (GtkButton *button, gpointer user_data) { GtkWidget *label; GtkWidget *tab; /* creiamo un figlio al notebook */ label = gtk_label_new("Pagina aggiunta dinamicamente"); gtk_widget_show(label); tab = gtk_label_new("tab"); /* inseriamolo nella nuova pagina */ gtk_notebook_append_page(GTK_NOTEBOOK(user_data), label, tab); } /* le callback richiamate alla pressione dei bottoni load e save */ void on_load_clicked (GtkButton *button, gpointer user_data) { GtkWidget *fs; /* creiamo un file selection */ fs = gtk_file_selection_new("Gtk+ -- Apri un file --"); gtk_widget_show(fs); /* colleghiamo i bottoni ok e cancel alle relative funzioni di callback */ gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), "clicked", GTK_SIGNAL_FUNC(carica_file), (gpointer) fs); gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), (gpointer) fs); gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(fs)->cancel_button), "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), (gpointer) fs); } void on_save_clicked (GtkButton *button, gpointer user_data) { GtkWidget *fs; fs = gtk_file_selection_new("Gtk+ -- Salva un file --"); gtk_widget_show(fs); gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), "clicked", GTK_SIGNAL_FUNC(salva_file), (gpointer) fs); gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), (gpointer) fs); gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(fs)->cancel_button), "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), (gpointer) fs); } /* le callback relative ai radio button */ void on_radio_sd_toggled (GtkToggleButton *togglebutton, gpointer user_data) { gtk_progress_bar_set_orientation (GTK_PROGRESS_BAR(user_data), GTK_PROGRESS_LEFT_TO_RIGHT); } void on_radio_ds_toggled (GtkToggleButton *togglebutton, gpointer user_data) { gtk_progress_bar_set_orientation (GTK_PROGRESS_BAR(user_data), GTK_PROGRESS_RIGHT_TO_LEFT); } void on_radio_cont_toggled (GtkToggleButton *togglebutton, gpointer user_data) { gtk_progress_bar_set_bar_style(GTK_PROGRESS_BAR(user_data), GTK_PROGRESS_CONTINUOUS); } void on_radio_disc_toggled (GtkToggleButton *togglebutton, gpointer user_data) { gtk_progress_bar_set_bar_style(GTK_PROGRESS_BAR(user_data), GTK_PROGRESS_DISCRETE); } /* creazione dell'interfaccia grafica */ GtkWidget* create_window (void) { GtkWidget *window; GtkWidget *notebook; GtkWidget *clist; GtkWidget *entry; GtkWidget *scrolledwindow1; GtkWidget *scrolledwindow2; GtkObject *spinbutton_adj; GtkWidget *spinbutton; GtkWidget *progressbar; GSList *orientamento_group = NULL; GSList *stile_group = NULL; GtkWidget *radio_sd; GtkWidget *radio_ds; GtkWidget *radio_cont; GtkWidget *radio_disc; GtkWidget *clist_string; GtkWidget *vbox1; GtkWidget *vbox2; GtkWidget *vbox3; GtkWidget *vbox4; GtkWidget *hbox; GtkWidget *button_box; GtkWidget *hbox1; GtkWidget *hbox2; GtkWidget *hbox3; GtkWidget *hbox4; GtkWidget *table; GtkWidget *label1; GtkWidget *label2; GtkWidget *label3; GtkWidget *label4; GtkWidget *label5; GtkWidget *label6; GtkWidget *label7; GtkWidget *label8; GtkWidget *tab1; GtkWidget *tab2; GtkWidget *tab3; /* widget bottoni */ GtkWidget *next_page; GtkWidget *prev_page; GtkWidget *del_page; GtkWidget *add_page; GtkWidget *load; GtkWidget *save; GtkWidget *esci; GtkWidget *hseparator; GtkWidget *hbuttonbox1; window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_object_set_data (GTK_OBJECT (window), "window", window); gtk_window_set_title (GTK_WINDOW (window), "GTK+ 4°"); hbox = gtk_hbox_new (FALSE, 5); gtk_widget_show (hbox); gtk_container_add (GTK_CONTAINER (window), hbox); gtk_container_set_border_width (GTK_CONTAINER (hbox), 4); button_box = gtk_vbox_new (FALSE, 5); gtk_box_pack_start (GTK_BOX (hbox), button_box, FALSE, FALSE, 0); gtk_widget_show (button_box); prev_page = gtk_button_new_with_label ("Pagina precedente"); gtk_box_pack_start (GTK_BOX (button_box), prev_page, FALSE, FALSE, 0); gtk_widget_show (prev_page); next_page = gtk_button_new_with_label ("Pagina successiva"); gtk_box_pack_start (GTK_BOX (button_box), next_page, FALSE, FALSE, 0); gtk_widget_show (next_page); del_page = gtk_button_new_with_label ("Elimina una pagina"); gtk_box_pack_start (GTK_BOX (button_box), del_page, FALSE, FALSE, 0); gtk_widget_show (del_page); add_page = gtk_button_new_with_label ("Aggiungi una pagina"); gtk_box_pack_start (GTK_BOX (button_box), add_page, FALSE, FALSE, 0); gtk_widget_show (add_page); vbox1 = gtk_vbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), vbox1, FALSE, FALSE, 0); gtk_widget_show (vbox1); /* costruiamo il notebook */ notebook = gtk_notebook_new (); gtk_widget_show (notebook); gtk_box_pack_start (GTK_BOX (vbox1), notebook, TRUE, TRUE, 0); /* realiziamo uno dei "figli" del notebook. Questo figlio conterrà: - una columned list (inserita in una scrolled window) - un widget entry - alcune etichette */ vbox2 = gtk_vbox_new (FALSE, 0); gtk_widget_show (vbox2); scrolledwindow1 = gtk_scrolled_window_new (NULL, NULL); gtk_widget_show (scrolledwindow1); gtk_box_pack_start (GTK_BOX (vbox2), scrolledwindow1, TRUE, TRUE, 0); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow1), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); clist = gtk_clist_new (2); gtk_widget_show (clist); gtk_container_add (GTK_CONTAINER (scrolledwindow1), clist); gtk_clist_set_column_width (GTK_CLIST (clist), 0, 189); gtk_clist_set_column_width (GTK_CLIST (clist), 1, 80); gtk_clist_column_titles_show (GTK_CLIST (clist)); label1 = gtk_label_new ("stringa1"); gtk_widget_show (label1); gtk_clist_set_column_widget (GTK_CLIST (clist), 0, label1); label2 = gtk_label_new ("stringa2"); gtk_widget_show (label2); gtk_clist_set_column_widget (GTK_CLIST (clist), 1, label2); hbox1 = gtk_hbox_new (FALSE, 10); gtk_widget_show (hbox1); gtk_box_pack_start (GTK_BOX (vbox2), hbox1, FALSE, FALSE, 0); gtk_container_set_border_width (GTK_CONTAINER (hbox1), 5); label3 = gtk_label_new ("Inserisci una stringa"); gtk_widget_show (label3); gtk_box_pack_start (GTK_BOX (hbox1), label3, FALSE, FALSE, 0); entry = gtk_entry_new (); gtk_widget_show (entry); gtk_box_pack_start (GTK_BOX (hbox1), entry, TRUE, TRUE, 0); hbox2 = gtk_hbox_new (FALSE, 10); gtk_widget_show (hbox2); gtk_box_pack_start (GTK_BOX (vbox2), hbox2, FALSE, FALSE, 0); gtk_container_set_border_width (GTK_CONTAINER (hbox2), 5); label4 = gtk_label_new ("Stringa selezionata:"); gtk_widget_show (label4); gtk_box_pack_start (GTK_BOX (hbox2), label4, FALSE, FALSE, 0); clist_string = gtk_label_new (""); gtk_widget_show (clist_string); gtk_box_pack_start (GTK_BOX (hbox2), clist_string, TRUE, TRUE, 0); /* terminata la costruzione del figlio del notebook, inseriamolo nella pagina */ tab1 = gtk_label_new ("Pagina 1"); gtk_widget_show (tab1); gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox2, tab1); /* altro figlio */ vbox3 = gtk_vbox_new (FALSE, 0); gtk_widget_show (vbox3); gtk_container_add (GTK_CONTAINER (notebook), vbox3); gtk_container_set_border_width (GTK_CONTAINER (vbox3), 5); hbox3 = gtk_hbox_new (FALSE, 5); gtk_widget_show (hbox3); gtk_box_pack_start (GTK_BOX (vbox3), hbox3, FALSE, FALSE, 0); gtk_container_set_border_width (GTK_CONTAINER (hbox3), 5); label5 = gtk_label_new ("Seleziona un numero da 1 a 100"); gtk_widget_show (label5); gtk_box_pack_start (GTK_BOX (hbox3), label5, FALSE, FALSE, 0); spinbutton_adj = gtk_adjustment_new (0, 0, 100, 1, 10, 10); spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_adj), 1, 0); gtk_widget_show (spinbutton); gtk_box_pack_start (GTK_BOX (hbox3), spinbutton, FALSE, FALSE, 0); hbox4 = gtk_hbox_new (FALSE, 5); gtk_widget_show (hbox4); gtk_box_pack_start (GTK_BOX (vbox3), hbox4, TRUE, FALSE, 0); gtk_container_set_border_width (GTK_CONTAINER (hbox4), 5); label6 = gtk_label_new ("Il numero da te inserito"); gtk_widget_show (label6); gtk_box_pack_start (GTK_BOX (hbox4), label6, FALSE, FALSE, 0); progressbar = gtk_progress_bar_new (); gtk_widget_show (progressbar); gtk_box_pack_start (GTK_BOX (hbox4), progressbar, TRUE, TRUE, 0); gtk_progress_set_show_text (GTK_PROGRESS (progressbar), TRUE); table = gtk_table_new (2, 3, TRUE); gtk_widget_show (table); gtk_box_pack_start (GTK_BOX (vbox3), table, TRUE, FALSE, 0); radio_sd = gtk_radio_button_new_with_label (orientamento_group, "da sinistra a destra"); orientamento_group = gtk_radio_button_group (GTK_RADIO_BUTTON (radio_sd)); gtk_widget_show (radio_sd); gtk_table_attach (GTK_TABLE (table), radio_sd, 1, 2, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); radio_ds = gtk_radio_button_new_with_label (orientamento_group, "da destra a sinistra"); orientamento_group = gtk_radio_button_group (GTK_RADIO_BUTTON (radio_ds)); gtk_widget_show (radio_ds); gtk_table_attach (GTK_TABLE (table), radio_ds, 2, 3, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); radio_cont = gtk_radio_button_new_with_label (stile_group, "continuo"); stile_group = gtk_radio_button_group (GTK_RADIO_BUTTON (radio_cont)); gtk_widget_show (radio_cont); gtk_table_attach (GTK_TABLE (table), radio_cont, 1, 2, 1, 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); radio_disc = gtk_radio_button_new_with_label (stile_group, "discreto"); stile_group = gtk_radio_button_group (GTK_RADIO_BUTTON (radio_disc)); gtk_widget_show (radio_disc); gtk_table_attach (GTK_TABLE (table), radio_disc, 2, 3, 1, 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); label7 = gtk_label_new ("Orientamento: "); gtk_widget_show (label7); gtk_table_attach (GTK_TABLE (table), label7, 0, 1, 0, 1, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_misc_set_alignment (GTK_MISC (label7), 1, 0.5); label8 = gtk_label_new ("Stile:"); gtk_widget_show (label8); gtk_table_attach (GTK_TABLE (table), label8, 0, 1, 1, 2, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_misc_set_alignment (GTK_MISC (label8), 1, 0.5); tab2 = gtk_label_new ("Pagina 2"); gtk_widget_show (tab2); gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox3, tab2); /* l'ultimo figlio */ vbox4 = gtk_vbox_new (FALSE, 5); gtk_widget_show (vbox4); gtk_container_add (GTK_CONTAINER (notebook), vbox4); gtk_container_set_border_width (GTK_CONTAINER (vbox4), 5); scrolledwindow2 = gtk_scrolled_window_new (NULL, NULL); gtk_widget_show (scrolledwindow2); gtk_box_pack_start (GTK_BOX (vbox4), scrolledwindow2, TRUE, TRUE, 0); gtk_container_set_border_width (GTK_CONTAINER (scrolledwindow2), 3); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow2), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); text = gtk_text_new (NULL, NULL); gtk_text_set_editable(GTK_TEXT(text), TRUE); gtk_text_insert(GTK_TEXT(text), NULL, NULL, NULL, "Trivial editor ;-)...\nCopyLeft 2002 Nicola Fragale\n", -1); gtk_widget_show (text); gtk_container_add (GTK_CONTAINER (scrolledwindow2), text); hseparator = gtk_hseparator_new (); gtk_widget_show (hseparator); gtk_box_pack_start (GTK_BOX (vbox4), hseparator, FALSE, TRUE, 0); hbuttonbox1 = gtk_hbutton_box_new (); gtk_widget_show (hbuttonbox1); gtk_box_pack_start (GTK_BOX (vbox4), hbuttonbox1, FALSE, TRUE, 0); gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox1), GTK_BUTTONBOX_SPREAD); load = gtk_button_new_with_label ("Carica"); gtk_widget_show (load); gtk_container_add (GTK_CONTAINER (hbuttonbox1), load); GTK_WIDGET_SET_FLAGS (load, GTK_CAN_DEFAULT); save = gtk_button_new_with_label ("Salva"); gtk_widget_show (save); gtk_container_add (GTK_CONTAINER (hbuttonbox1), save); GTK_WIDGET_SET_FLAGS (save, GTK_CAN_DEFAULT); tab3 = gtk_label_new ("Pagina 3"); gtk_widget_show (tab3); gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox4, tab3); esci = gtk_button_new_with_label ("Esci"); gtk_widget_show (esci); gtk_box_pack_start (GTK_BOX (vbox1), esci, FALSE, FALSE, 0); /* quando si seleziona un elemento della lista, viene emesso il segnale select_row, che è gestito dalla nostra funzione di callback on_clist_select_row */ gtk_signal_connect (GTK_OBJECT (clist), "select_row", GTK_SIGNAL_FUNC (on_clist_select_row), clist_string); /* collegamento alla callback richiamata alla pressione del tasto invio nel widget entry */ gtk_signal_connect (GTK_OBJECT (entry), "activate", GTK_SIGNAL_FUNC (on_entry_activate), clist); /* collegamento alla callback richiamata alla pressione del bottone esci */ gtk_signal_connect (GTK_OBJECT (esci), "clicked", GTK_SIGNAL_FUNC (gtk_main_quit), NULL); /* Le callback relative al notebook */ gtk_signal_connect (GTK_OBJECT (next_page), "clicked", GTK_SIGNAL_FUNC (on_next_page_clicked), notebook); gtk_signal_connect (GTK_OBJECT (prev_page), "clicked", GTK_SIGNAL_FUNC (on_prev_page_clicked), notebook); gtk_signal_connect (GTK_OBJECT (del_page), "clicked", GTK_SIGNAL_FUNC (on_del_page_clicked), notebook); gtk_signal_connect (GTK_OBJECT (add_page), "clicked", GTK_SIGNAL_FUNC (on_add_page_clicked), notebook); /* le callback relative ai bottoni load e save */ gtk_signal_connect (GTK_OBJECT (load), "clicked", GTK_SIGNAL_FUNC (on_load_clicked), NULL); gtk_signal_connect (GTK_OBJECT (save), "clicked", GTK_SIGNAL_FUNC (on_save_clicked), NULL); /* le callback per gli spin button */ gtk_signal_connect (GTK_OBJECT (spinbutton), "activate", GTK_SIGNAL_FUNC (on_spinbutton_changed), progressbar); gtk_signal_connect (GTK_OBJECT (spinbutton), "changed", GTK_SIGNAL_FUNC (on_spinbutton_changed), progressbar); /* e quelle per i radio button */ gtk_signal_connect (GTK_OBJECT (radio_sd), "toggled", GTK_SIGNAL_FUNC (on_radio_sd_toggled), progressbar); gtk_signal_connect (GTK_OBJECT (radio_ds), "toggled", GTK_SIGNAL_FUNC (on_radio_ds_toggled), progressbar); gtk_signal_connect (GTK_OBJECT (radio_cont), "toggled", GTK_SIGNAL_FUNC (on_radio_cont_toggled), progressbar); gtk_signal_connect (GTK_OBJECT (radio_disc), "toggled", GTK_SIGNAL_FUNC (on_radio_disc_toggled), progressbar); return window; } int main (int argc, char *argv[]) { GtkWidget *window; gtk_init (&argc, &argv); window = create_window (); gtk_widget_show (window); gtk_main (); return 0; } |
Salvate il programma nel file gui.c e compilate utilizzando il comando:
gcc -Wall -g gui.c -o gui `gtk-config --libs --cflags` |
Il notebook appartiene alla famiglia dei widget contenitori; il suo scopo principale è, infatti, quello di contenere altri widget, tuttavia si differenzia dagli altri per il modo in cui assolve al suo compito. Il notebook si presenta come un insieme di pagine sovrapposte, ognuna delle quali è dotata di una linguetta, generalmente posta nella parte superiore della pagina (figura 1). La navigazione tra le pagine avviene cliccando sulle opportune linguette.
Per creare un nuovo notebook, è sufficiente chiamare la funzione gtk_notebook_new();
, che
ritornerà il puntatore al nuovo oggetto.
notebook = gtk_notebook_new (); |
Come detto il notebook appartiene alla famiglia dei widget contenitori. Osserviamo i prototipi delle seguenti funzioni:
void gtk_notebook_append_page (GtkNotebook *notebook, GtkWidget *child, GtkWidget *tab_label); void gtk_notebook_prepend_page (GtkNotebook *notebook, GtkWidget *child, GtkWidget *tab_label); void gtk_notebook_insert_page (GtkNotebook *notebook, GtkWidget *child, GtkWidget *tab_label, gint position); |
Tutte richiedono come primo argomento il puntatore al notebook, e come secondo un puntatore ad un widget child (figlio). Il figlio in questione è il widget o l'insieme di widget che vogliamo inserire nella pagina del notebook, pagina che avrà come etichetta quella definita con il widget tab_label. La prima di queste funzioni "appende" la pagina creata al notebook; la inserisce, cioè, alla fine, dopo tutte quelle già presenti. La seconda funzione, al contrario, inserisce la pagina all'inizio del notebook, prima di tutte; la terza infine inserisce la pagina di notebook in una generica posizione identificata dall'intero position.
Osserviamo il codice del programma:
vbox2 = gtk_vbox_new (FALSE, 0); gtk_widget_show (vbox2); scrolledwindow1 = gtk_scrolled_window_new (NULL, NULL); gtk_widget_show (scrolledwindow1); gtk_box_pack_start (GTK_BOX (vbox2), scrolledwindow1, TRUE, TRUE, 0); .... tab1 = gtk_label_new ("Pagina 1"); gtk_widget_show (tab1); gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox2, tab1); |
vbox2 è una scatola verticale che conterrà i widget che vogliamo mostrare, e che interpreterà, contemporaneamente, la parte di figlio per il notebook. La pagina avrà come etichetta la stringa "Pagina 1".
Vediamo ora alcune delle funzioni disponibili per il notebook:
void gtk_notebook_remove_page (GtkNotebook *notebook, gint page_num); |
gtk_notebook_remove_page();
è utilizzata, come il nome stesso suggerisce,
per rimuovere la pagina posta in posizione page_num dal notebook
gint gtk_notebook_page_num (GtkNotebook *notebook, GtkWidget *child); |
gtk_notebook_page_num();
restituisce il numero della pagina che contiene
il "figlio" child
che è stato passato come secondo argomento nella funzione.
void gtk_notebook_set_page (GtkNotebook *notebook, gint page_num); |
gtk_notebook_set_page();
questa funzione porta in primo piano la pagina
numero page_num. Se si inserisce un valore negativo, la pagina che verrà mostrata sarà l'ultima.
void gtk_notebook_prev_page (GtkNotebook *notebook); void gtk_notebook_next_page (GtkNotebook *notebook); |
gtk_notebook_prev_page(); e gtk_notebook_next_page();
si occupano di mostrare
rispettivamente la pagina precedente e quella successiva rispetto alla pagina corrente.
void gtk_notebook_set_tab_pos (GtkNotebook *notebook, GtkPositionType pos); |
Con gtk_notebook_set_tab_pos();
si può decidere dove posizionare le linguette
del notebook. I possibili valori sono:
void gtk_notebook_set_show_tabs (GtkNotebook *notebook, gboolean show_tabs); |
...e se non volessimo mostrare nessuna linguetta? Allora ricorreremo alla funzione
gtk_notebook_set_show_tabs();
, passandole come valore booleano FALSE.
Dovremmo però poi trovare un metodo alternativo per spostarci tra le pagine. Una alternativa
potrebbe essere quella di affiancare al notebook un widget list (o tree), e in funzione della
posizione dell'elemento selezionato nella lista richiamare la funzione
gtk_notebook_set_page();
che ci mostrerà la pagina richiesta.
void gtk_notebook_set_scrollable (GtkNotebook *notebook, gboolean scrollable); |
Cosa accade se il nostro notebook contiene molte pagine e la nostra finestra è troppo piccola
per poter visualizzare tutte le linguette? Possiamo rassegnarci all'idea di non poter selezionare
alcune pagine? Certo che no! ;-). Ci verrà in aiuto la funzione
gtk_notebook_set_scrollable();
, la quale accetta come secondo argomento un
valore booleano, che se posto a TRUE e nel caso in cui ci siano pagine per le quali
non si possa mostrare la linguatta, porrà a fianco del notebook due freccette, che se cliccate
mostreranno le pagine nascoste.
gint gtk_notebook_get_current_page (GtkNotebook *notebook); |
gtk_notebook_get_current_page();
Ritornerà il numero della pagina
attualmente selezionata
void gtk_notebook_set_tab_label (GtkNotebook *notebook, GtkWidget *child, GtkWidget *tab_label); |
Con gtk_notebook_set_tab_label();
è possibile modificare l'etichetta posta
su una linguetta. Per identificare l'etichetta che vogliamo modificare occorrerà passare come
secondo argomento il puntatore al figlio contenuto nel notebook.
GtkWidget* gtk_notebook_get_tab_label (GtkNotebook *notebook, GkWidget *child); |
Se invece abbiamo la necessità di conoscere l'etichetta (o il widget) contenuto nella linguetta,
ricorreremo alla funzione gtk_notebook_get_tab_label
.
GtkWidget* gtk_notebook_get_nth_page (GtkNotebook *notebook, gint page_num); |
Infine, potrebbe verificarsi il caso in cui conosciamo il numero di una pagina, ma
non il widget figlio che è contenuto nella pagina stessa. In questo caso si ricorre alla
funzione gtk_notebook_get_nth_page();
Nel programma proposto sono definite quattro funzioni di callback che modificheranno lo stato del notebook.
Le prime due sono on_next_page_clicked (GtkButton *button, gpointer user_data);
e
on_prev_page_clicked (GtkButton *button, gpointer user_data);
che sono richiamate alla
pressione di due diversi bottoni.
A queste funzioni viene passato, come user_data, il puntatore al widget notebook; avranno il compito di
mostrare la pagina precedente e quella successiva all'attuale. La callback
on_del_page_clicked ();
ricavato il numero di pagina corrente, la eliminerà dal notebook,
infine on_add_page_clicked ();
crea un semplice widget figlio (un'etichetta) e inserisce
una nuova pagina nel notebook.
Le scrolled windows sono utilizzate per creare aree scorrevoli, all'interno delle quali è possibile inserire qualsiasi widget o composizione di widget. In pratica, utilizzando le scrolled windows vedremo solo parte del widget che vi è contenuto, e per poter vedere le parti nascoste sposteremo la visuale utilizzando le barre di scorrimento che il widget ci mette a disposizione (figura 2).
void gtk_scrolled_window_new(GtkAdjustment *hadjustment, GtkAdjustment *vadjustment); |
E' la funzione da richiamare per creare una scrolled window. Accetta come argomenti due puntatori all'oggetto GtkAdjustment, che possiamo tranquillamente impostare a NULL.
Come detto le scrolled windows sono dotate di barre di scorrimento, una per gli spostamenti orizzontali, l'altra per quelli verticali. Possiamo influenzare il comportamento delle barre di scorrimento ricorrendo alla seguente funzione:
void gtk_scrolled_window_set_policy (GtkScrolledWindow *scrolled_window, GtkPolicyType hscrollbar_policy, GtkPolicyType vscrollbar_policy); |
Il primo argomento è il puntatore restituitoci dalla gtk_scrolled_window_new();
, mentre
con il secondo e il terzo argomento indichiamo alla funzione il tipo di barra desiderata.
hscrollbar_policy
e vscrollbar_policy
possono assumere uno tra i seguenti
valori:
Le scrolled windows, così come altri widget, hanno bisogno di un GtkAdjustment. Ma cos'è un GtkAdjustment? Molti widget possono essere adattati o regolati utilizzando il mouse o la tastiera (ad esempio le scrolled windows, i widget text, gli spin button). Pensiamo ad un editor di testo, possiamo scorrere il testo sullo schermo utilizzando i tasti "pag" o ancora cliccando sulle barre di scorrimento; o possiamo inserire un valore in uno spin button, cliccando sulle freccette per incrementare o decrementare il valore che contiene.
Ogni widget di questo tipo dovrebbe avere i propri segnali per notificare un cambiamento e le proprie funzioni per leggere e scrivere i valori aggiornati. Dovrebbe essere compito del programmatore occuparsi della gestione di tali cambiamenti, aggiornando continuamente lo stato del widget (spostare il testo visualizzato nell'editor, incrementare o decrementare il numero contenuto nello spin button). Per evitare tutto ciò, GTK+ mette a disposizione un oggetto che può essere utilizzato da tutti quei widget che hanno bisogno di essere regolati e adattati, il GtkAdjustment. Ecco la funzione da richiamare per creare un GtkAdjustment.
GtkObject *gtk_adjustment_new(gfloat value, gfloat lower, gfloat upper, gfloat step_increment, gfloat page_increment, gfloat page_size ); |
osserviamo come appare uno spin button (figura 1); di seguito il pezzo di codice, estratto dal programma proposto, utitizzato per crearlo.
spinbutton_adj = gtk_adjustment_new (0, 0, 100, 1, 10, 10); spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_adj), 1, 0); gtk_widget_show (spinbutton); |
value
è il valore corrente/iniziale dell'adjustment; nel nostro esempio
lo spin button viene creato con un valore iniziale di zero, mentre l'immagine mostra il
valore che possedeva in quel momento (10)
lower
è il più piccolo valore che può essere assunto (0 nell'esempio)
upper
è il valore più grande che il widget può avere (in questo caso 100).
Il range dei valori assumibili dal widget è quindi [0..100]
step_increment
è la quantità con cui incrementare (decrementare) il valore del widget.
Se il widget fosse stato una barra di scorrimento, con questo valore ci saremmo riferiti allo spazio da
scrollare nel caso in cui avessimo cliccato sulle frecce poste ai margini della barra. Nel caso dello spin
button indica di quanto incrementare (decrementare) il valore contenuto in esso.
page_increment
è l'incremento (decremento) di pagina. Nel caso, nuovamente, di una barra di
scorrimento, con questo valore si indica di quanto spostare la barra nel caso in cui si facesse click
non sulle freccette, ma nell'interno della barra stessa.
page_size
è la grandezza della pagina, cioè la grandezza dell'area effettivamente visibile
Per poter impostare esplicitamente il valore di un'adjustment si ricorre alla seguente funzione:
void gtk_adjustment_set_value(GtkAdjustment *adjustment gfloat value); |
che accetta come primo argomento il puntatore all'adjustment e come secondo il valore da settare.
Continuiamo l'analisi dei widget di input esaminando il widget gtkentry (figura 3). GtkEntry ci mette a disposizione una casella in cui inserire caratteri alfanumerici, ed è generalmente utilizzato per permettere all'utente l'immissione di brevi stringhe di testo.
La funzione che ci restituisce un puntatore a GtkEntry è naturalmente:
GtkWidget* gtk_entry_new (void); |
Disponiamo di tre funzioni per l'inserimento del testo in un entry, che sono:
void gtk_entry_set_text (GtkEntry *entry, const gchar *text); void gtk_entry_append_text (GtkEntry *entry, const gchar *text); void gtk_entry_prepend_text (GtkEntry *entry, const gchar *text); |
Tutte e tre le funzioni accettano due argomenti, il puntatore al widget e il testo da inserire nel widget stesso, ciò che le differenzia è il modo in cui il testo viene inserito. La prima funzione inserisce il testo nel widget sostituendo ogni precedente stringa, la seconda inserisce il testo alla fine della stringa già presente, mentre l'ultima inserisce il testo prima dell'eventuale stringa presente.
E se volessimo inserire del testo in una posizione qualsiasi? Se esaminiamo le funzioni messe a disposizione da GtkEntry, ci accorgeremo che non c'è una routine che ci permette di fare ciò. Anche in questo caso, la risposta al nostro problema viene dalla gerarchia ad oggetti di Gtk+, esaminiamo quella di GtkEntry:
GtkObject +----GtkWidget +----GtkEditable +----GtkEntry
Come possiamo osservare, GtkEntry è derivato da GtkEditable; spulciando tra le funzioni disponibili per GtkEditable troveremo:
void gtk_editable_insert_text (GtkEditable *editable, const gchar *new_text, gint new_text_length, gint *position); |
il primo argomento (editable
) da passare è il puntatore all'entry, new_text
è il puntatore al testo da inserire, il terzo argomento (new_text_length
) indica la lunghezza
del testo da inserire. L'ultimo (position
) è un parametro di input/output, che va inizializzato con
la posizione in cui inserire il testo, e che al ritorno dalla funzione position
punterà alla posizione
dopo il nuovo testo inserito.
La prima volta che si usa questa funzione potremmo però non conoscere la posizione occupata dal cursore in quel momento. Per ovviare si utilizza la seguente chiamata:
gint gtk_editable_get_position (GtkEditable *editable); |
che ci ritornerà la posizione del cursore.
Esaminiamo ora le seguenti funzioni:
gchar* gtk_entry_get_text (GtkEntry *entry); void gtk_entry_set_visibility (GtkEntry *entry, gboolean visible); |
La prima è utilizzata per recuperare il testo contenuto nell'entry, mentre la seconda permette di stabilire se
visualizzare o meno il contenuto dell'entry. Se visible
è
impostato a TRUE
, il testo contenuto nell'entry è mostrato
normalmente; se invece è impostato a FALSE
, i caratteri immessi
saranno mascherati da altrettanti asterischi. Un'utilissima funzione per tutti
coloro che devono implementare una routine per l'acquisizione di una password.
Come abbiamo visto (figura 1), lo spin button è utilizzato per selezionare un numero da un determinato insieme. E' formato da un entry e da due frecce sovrapposte. Il valore contenuto nello spin button può essere modificato sia cliccando sulle frecce, sia editando il valore direttamente nell'entry.
Osserviamo ancora il codice utilizzato per la creazione dello spin button:
spinbutton_adj = gtk_adjustment_new (0, 0, 100, 1, 10, 10); spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_adj), 1, 0); gtk_widget_show (spinbutton); ... |
e il prototipo della funzione:
GtkWidget* gtk_spin_button_new (GtkAdjustment *adjustment, gfloat climb_rate, guint digits); |
Per creare lo spin button, occorre richiamare la funzione gtk_spin_button_new
e fornire
come primo argomento un puntatore ad un GtkAdjustment
. Il secondo
argomento climb_rate
, indica di quanto lo spin button deve cambiare quando si clicca
su una delle frecce; l'ultimo digits
indica, se il numero da visualizzare ha una parte decimale,
quante cifre mostrare dopo la virgola.
Esaminiamo alcune delle funzioni disponibili per manipolare uno spin button:
void gtk_spin_button_set_digits (GtkSpinButton *spin_button, guint digits); gfloat gtk_spin_button_get_value_as_float (GtkSpinButton *spin_button); gint gtk_spin_button_get_value_as_int (GtkSpinButton *spin_button); void gtk_spin_button_set_value (GtkSpinButton *spin_button, gfloat value); void gtk_spin_button_set_numeric (GtkSpinButton *spin_button, gboolean numeric); |
gtk_spin_button_set_digits();
è impiegata per modificare il numero di cifre
decimali da mostrare dopo la virgola; il secondo argomento della funzione,
digits
, indica le cifre decimali da mostrare.
gtk_spin_button_get_value_as_float();
accetta un solo parametro, il
puntatore allo spin button, e ritorna il valore contenuto nello spin button sotto forma di numero in floating point.
gtk_spin_button_get_value_as_int();
come la funzione precedente,
accetta un puntatore a spin button, ma questa volta il valore contenuto nel widget
viene restituito come intero.
gtk_spin_button_set_value();
ci consente di impostare ad un determinato
valore lo spin button. Il primo argomento è il puntatore al widget, il secondo il valore
da impostare.
gtk_spin_button_set_numeric();
merita un discorso più ampio. Come abbiamo visto, lo spin button è
composto da un GtkEntry e da una coppia di frecce, ma cosa accade se inseriamo una
stringa nell'entry? Come verrà interpretata ad esempio la stringa "xyz"? Normalmente
dovremmo costruire una routine che esamini e filtri l'input dell'utente per evitare che
inserisca caratteri alfabetici che potrebbero danneggiare la nostra applicazione; in questo
caso, ricorreremo a questa funzione. Se la variabile booleana numeric
è
settata a TRUE
, allora tutti i caratteri non numerici, con l'eccezione di "." e
"-", saranno ignorati, se numeric
è settata a FALSE
, l'entry
si comporterà normalmente, accettando tutti i caratteri alfanumerici (notare però che la funzione
non dà un significato a "-" e "."!)
Chi di noi non ha mai visto una frase del genere: "Attendere, prego! Il 37% del processo è completato!!", o un'altra come questa: "Questo computer si autodistruggerà (con conseguente crollo del palazzo che lo contiene) esattamente fra 10 secondi. Avete tempo per: ripensare alla vostra infanzia, uccidere il cattivo e salvare il mondo, portare la nonna a distanza di sicurezza e dar da mangiare al gatto. La Acme Spa vi augura una buona giornata!!" ;-)
Il trascorrere del tempo, l'indicazione dello spazio disponibile su di un disco, la percentuale di processo concluso, tutte queste e molte altre operazioni possono essere implementate utilizzando le progress bar (figura 1) e (figura 4).
Questa è la gerarchia dell'oggetto GtkProgressBar, come vedremo molte delle funzioni che ci permettono di operare sulle progress bar, in realtà sono definite sull'oggetto GtkProgress, genitore di GtkProgressBar.
GtkObject +----GtkWidget +----GtkProgress +----GtkProgressBar
GTK+ ci mette a disposizione molte funzioni per gestire le progress bar, esaminiamone alcune partendo da quelle che ci restituiscono il puntatore all'oggetto.
GtkWidget* gtk_progress_bar_new (void); GtkWidget* gtk_progress_bar_new_with_adjustment (GtkAdjustment *adjustment); |
è sufficiente richiamare la prima funzione ed avremo un puntatore all'oggetto. Se abbiamo
delle esigenze particolari, e vogliamo che la nostra barra vari in un determinato range di valori,
ricorreremo alla seconda funzione, alla quale dovremo fornire un puntatore ad un GtkAdjustment
Osserviamo alcune funzioni di callback, ed il codice utilizzato per l'inizializzazione della progressbar, estratte dal programma allegato.
void on_spinbutton_changed (GtkWidget *spin, gpointer user_data) { gint val; val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin)); gtk_progress_set_value(GTK_PROGRESS(user_data), (gfloat) val); } ... void on_radio_ds_toggled (GtkToggleButton *togglebutton, gpointer user_data) { gtk_progress_bar_set_orientation (GTK_PROGRESS_BAR(user_data), GTK_PROGRESS_RIGHT_TO_LEFT); } void on_radio_cont_toggled (GtkToggleButton *togglebutton, gpointer user_data) { gtk_progress_bar_set_bar_style(GTK_PROGRESS_BAR(user_data), GTK_PROGRESS_CONTINUOUS); } ... progressbar = gtk_progress_bar_new (); gtk_widget_show (progressbar); gtk_box_pack_start (GTK_BOX (hbox4), progressbar, TRUE, TRUE, 0); gtk_progress_set_show_text (GTK_PROGRESS (progressbar), TRUE); ... |
Si inizializza il puntatore richiamando gtk_progress_bar_new ();
, si impacchetta in
una scatola e si richiama la funzione gtk_progress_set_show_text();
,
void gtk_progress_set_show_text (GtkProgress *progress, gint show_text); |
che accetta come primo parametro un puntatore a GtkProgress
, e come
secondo un booleano, che se settato a TRUE
permette la visualizzazione
di un testo all'interno della barra.
Per impostare la barra ad un determinato valore si ricorre alla funzione
gtk_progress_set_value();
, che accetta anch'essa un puntatore a
GtkProgress
come primo argomento e il valore (un numero reale) da impostare.
Con la funzione gtk_progress_get_value();
potremo recuperare il valore attuale
della progress bar.
void gtk_progress_set_value (GtkProgress *progress, gfloat value); gfloat gtk_progress_get_value (GtkProgress *progress); |
Abbiamo visto come impostare e recuperare un valore dalla progress bar; occupiamoci ora della parte estetica. Possiamo fissare l'orientamento della barra, scegliendo tra orizzontale e verticale, possiamo impostarla in modo che cresca da sinistra a destra o da destra a sinistra (per la barra orizzontale), oppure dal basso verso l'alto o dall'alto verso il basso (barra verticale). Inoltre possiamo scegliere come disegnarla, se discreta o continua (figura 4). Tutto questo ricorrendo a due sole funzioni
void gtk_progress_bar_set_bar_style (GtkProgressBar *pbar, GtkProgressBarStyle style); void gtk_progress_bar_set_orientation (GtkProgressBar *pbar, GtkProgressBarOrientation orientation); |
entrambe le funzioni vogliono come primo argomento un puntatore al GtkProgressBar
e come secondo un valore indicante lo stile della barra o l'orientazione della stessa.
I possibili valori per GtkProgressBarStyle
sono i seguenti:
GtkProgressBarOrientation
potrà assumere uno fra i seguenti valori:
Dopo tante ore passate davanti allo schermo a debuggare, finalmente abbiamo una bellissima routine che stima la popolazione di pinguini, gnomi, draghi e diavoletti in giro per l'Italia, passiamo ore a rimirare il grafico a colori, ad inorgoglirci per le nostre conoscenze informatiche e poi ci vien voglia di far partecipe il mondo dei nostri successi, ma acc@#! abbiamo dimenticato la routine di salvataggio!
Esportare dati verso il mondo esterno e caricare dati dal mondo esteno è una delle principali
operazioni compiute giornalmente da ogni computer. Se la nostra applicazione grafica richiede
di salvare e caricare file, dovremo prevedere una finestra tramite la quale l'utente possa
compiere le normali operazioni di input/output. Anche in questo caso GTK+ ci viene in aiuto,
fornendoci una comoda finestra di dialogo dotata di ogni comfort per permetterci di salvare e
caricare i nostri preziosi dati. In figura 5 possiamo osservare come appare questa finestra.
Vediamo come utilizzare questo widget:
void carica_file (GtkFileSelection *fs, gpointer user_data) { gchar *filename; FILE *fp; filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION(user_data)); ... } void on_load_clicked (GtkButton *button, gpointer user_data) { GtkWidget *fs; fs = gtk_file_selection_new("Gtk+ -- Apri un file --"); gtk_widget_show(fs); gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), "clicked", GTK_SIGNAL_FUNC(carica_file), (gpointer) fs); gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), (gpointer) fs); gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(fs)->cancel_button), "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), (gpointer) fs); } .... gtk_signal_connect (GTK_OBJECT (load), "clicked", GTK_SIGNAL_FUNC (on_load_clicked), NULL); .... |
Nel nostro programma sono presenti due bottoni, "load" e "save", ognuno collegato alla propria funzione di callback, che sarà richiamata quando cliccheremo su uno di essi. Esaminiamo il comportamento del bottone load (per il save è tutto identico). Nella funzione di callback, creeremo il widget file selection, che come possiamo vedere in figura 5 presenta nella parte bassa altri due bottoni, uno con etichetta "OK", l'altro con etichetta "Annulla". Sarà compito nostro collegare questi due bottoni alle funzioni di callback che svolgeranno le vere operazioni di input/output.
Ecco alcune delle funzioni atte a trattare il widget File Selection
GtkWidget* gtk_file_selection_new (const gchar *title); void gtk_file_selection_set_filename (GtkFileSelection *filesel, const gchar *filename); gchar* gtk_file_selection_get_filename (GtkFileSelection *filesel); void gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel); void gtk_file_selection_hide_fileop_buttons (GtkFileSelection *filesel); |
Mentre questo è l'elenco dei widget contenuti nella struttura FileSelection (e sì, il widget GtkFileSelection è un esempio di widget composito):
*fileop_dialog; *dir_list, *file_list; *ok_button; *cancel_button; *history_pulldown; *fileop_c_dir, *fileop_del_file, *fileop_ren_file |
gtk_file_selection_new();
crea una finestra di dialogo file selection e ci ritorna un
puntatore al nuovo oggetto. Possiamo passare alla funzione una stringa, che verrà visualizzata
come titolo sulla cornice della finestra.
Nelle due liste (dir_list e file_list), come è facile intuire, verrano mostrate la directory corrente (lista di sinistra) e i file contenuti nella directory (lista di destra). In alto saranno presenti tre bottoni (fileop_c_dir, fileop_del_dir, fileop_ren_dir), che ci permetteranno di creare una nuova directory, cancellare e rinominare un file. Utilizzando il menu pull down (history_pulldown) subito sottostante possiamo spostarci in un'altra directory appartenente al ramo corrente. Sotto alle liste troviamo un widget entry, nel quale possiamo inserire il nome del file che desideriamo aprire o il nome con cui vogliamo salvare un file.
Con le funzioni gtk_file_selection_show_fileop_buttons();
e
gtk_file_selection_hide_fileop_buttons();
possiamo mostrare o nascondere i bottoni
della parte alta (crea directory, ...); entrambe vogliono come parametro il puntatore al widget GtkFileSelection
Per recuperare il nome del file selezionato cliccando su di un elemento della lista a destra o scritto
nell'entry si ricorre alla funzione gtk_file_selection_get_filename();
, anche questa
funzione vuole il puntatore al widget GtkFileSelection
e ritorna il nome del file. Attenzione
però: per utilizzarla il widget non deve ancora essere stato distrutto... sembra una banalità, ma spesso succede di
pretendere di utilizzarla dopo che il widget ha concluso il suo ciclo vitale!!
Per inserire nell'entry il nome del file si ricorre invece alla funzione gtk_file_selection_set_filename();
,
che come primo argomento necessita del solito puntatore a GtkFileSelection
, e
come secondo la stringa contenente il nome del file.
Queste sono le funzioni di cui abbiamo bisogno, ma come realizzeremo effettivamente le operazioni di salvataggio e caricamento? Come accennato precedentemente, dovremo collegare i bottoni "OK" e "Annulla" ad opportune funzioni di callback; il bottone "Annulla" dovrà solo chiudere la finestra di dialogo, mentre cliccando sul bottone "OK" dovremo effettuare alcune operazioni (salvare o caricare) e poi chiudere la finestra.
void on_load_clicked (GtkButton *button, gpointer user_data) { GtkWidget *fs; fs = gtk_file_selection_new("Gtk+ -- Apri un file --"); gtk_widget_show(fs); gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), "clicked", GTK_SIGNAL_FUNC(carica_file), (gpointer) fs); gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), (gpointer) fs); gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(fs)->cancel_button), "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), (gpointer) fs); } |
Nella funzione di callback richiamata alla pressione del bottone "load", creeremo il widget
FileSelection
e collegheremo i bottoni ok_button
e
cancel_button
. Alla pressione di ok_button
sarà prima richiamata la
funzione carica_file
, alla quale inoltre passeremo come dati il puntatore allo stesso
FileSelection
, quindi al ritorno da carica_file
sarà chiamata la funzione
di libreria gtk_widget_destroy
, che distruggerà il widget fs
. Il bottone
"Annulla" sarà collegato semplicemente a gtk_widget_destroy
void carica_file (GtkFileSelection *fs, gpointer user_data) { gchar *filename; FILE *fp; filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION(user_data)); ... } |
Nella funzione carica_file
, recupereremo dal widget GtkFileSelection
con la
funzione gtk_file_selection_get_filename();
il nome del file, ed effettueremo le operazioni necessarie.
Molto brevemente, introdurremo ora i widget clist e text, che come anticipato saranno profondamente modificati nelle nuove GTK+2.0. Oltre alle columed list (clist) e al widget text saranno modificati anche il widget Ctree (columed tree) e i widget tree e list.
Nella figura 6 possiamo vedere la clist, mentre in figura 7 è mostrato il widget text (nel quale è stato caricato il programma di prova che accompagna questo articolo)
Il widget GtkClist mette a disposizione una lista su più colonne, ognuna delle quali può avere un titolo.
void on_entry_activate (GtkEditable *editable, gpointer user_data) { gchar *tmp; gchar *buffer[3]; tmp = gtk_entry_get_text(GTK_ENTRY(editable)); buffer[0] = g_strdup(tmp); g_strreverse(tmp); buffer[1] = g_strdup(tmp); buffer[2] = NULL; gtk_clist_append(GTK_CLIST(user_data), buffer); gtk_entry_set_text(GTK_ENTRY(editable), ""); } void on_clist_select_row (GtkCList *clist, gint row, gint column, GdkEvent *event, gpointer user_data) { gchar *buffer; gtk_clist_get_text(GTK_CLIST(clist), row, column, &buffer); gtk_label_set_text(GTK_LABEL(user_data), buffer); } ... clist = gtk_clist_new (2); ... label1 = gtk_label_new ("stringa1"); gtk_widget_show (label1); gtk_clist_set_column_widget (GTK_CLIST (clist), 0, label1); ... |
GtkWidget* gtk_clist_new (gint columns); GtkWidget* gtk_clist_new_with_titles (gint columns, gchar *titles[]); void gtk_clist_set_column_title (GtkCList *clist, gint column, const gchar *title); void gtk_clist_set_column_widget (GtkCList *clist, gint column, GtkWidget *widget); void gtk_clist_column_title_active (GtkCList *clist, gint column); void gtk_clist_set_text (GtkCList *clist, gint row, gint column, const gchar *text); gint gtk_clist_get_text (GtkCList *clist, gint row, gint column, gchar **text); gint gtk_clist_append (GtkCList *clist, gchar *text[]); gint gtk_clist_prepend (GtkCList *clist, gchar *text[]); gint gtk_clist_insert (GtkCList *clist, gint row, gchar *text[]); void gtk_clist_remove (GtkCList *clist, gint row); void gtk_clist_clear (GtkCList *clist); void user_function (GtkCList *clist, gint row, gint column, GdkEventButton *event, gpointer user_data); |
La clist si crea richiamando o la funzione gtk_clist_new();
o gtk_clist_new_with_titles();
la prima ha un solo parametro in input, il numero di colonne che la clist deve avere, mentre alla seconda oltre al numero
di colonne bisogna fornire anche i rispettivi titoli. Ad esempio, gtk_clist_new(3);
crea una clist con tre colonne, mentre gtk_clist_new(3, titolo);
(dove titolo
è definito
come char *titolo[3] = {"prima", "seconda", "terza"};
) crea sempre una clist con tre colonne,
ma alla prima colonna assegna il titolo "prima", alla seconda il titolo "seconda" e cosi via.
Se per la creazione della clist si è utilizzata la prima funzione, è sempre possibile assegnare (o modificare) in un
successivo momento il titolo di una o più colonne. Per far ciò si ricorre a gtk_clist_set_column_title();
.
Il primo argomento da passare è naturalmente il puntatore alla clist, il secondo (column
) è il
numero della colonna che ci interessa, ed il terzo è il titolo. Attenzione, le colonne sono numerate partendo da zero.
In molti programmi, quando clicchiamo sul titolo di una clist vediamo comparire sul titolo stesso una freccetta puntata verso l'alto o verso il basso, mentre gli elementi contenuti nella clist si riorganizzano (magari in ordine alfabetico). Bene; anche i nostri programmi possono offrire un feature di questo tipo.
Per prima cosa occore un titolo "speciale", invece di una semplice stringa creeremo una scatola contenente
altre due scatole, nella prima delle quali inseriremo il titolo vero e proprio e nella seconda una pixmap.
Quindi ricorreremo alla funzione gtk_clist_set_column_widget();
, che accetta tre parametri, il
puntatore alla clist, il numero della colonna in cui inserire il widget e il puntatore al widget stesso (la nostra
scatola), per inserire il widget nel titolo della colonna. Useremo, poi, la funzione
gtk_clist_column_title_active();
, per attivare la colonna (le facciamo emettere il segnale
"clicked" quando clicchiamo sul titolo) alla quale passeremo il puntatore alla clist e il numero della
colonna che stiamo trattando. Dovremo intercettare il segnale "clicked", e nella nostra funzione di callback
creeremo una nuova scatola contenente la stringa da mostrare e la pixmap di una freccia rivolta nel
verso opportuno (l'opposto di quello dell'attuale). Richiamando gtk_clist_set_column_widget();
modificheremo il titolo della colonna.
I dati possono essere inseriti o nelle singole celle o su intere righe; vediamo come inserire le righe.
Se la nostra lista ha 2 colonne, dovremo creare un vettore di tre elementi (una stringa a colonna, più
NULL
come terminatore) da passare ad apposite funzioni. Per inserire dati alla fine
della lista si ricorre alla funzione gtk_clist_append();
(o alle analoghe
gtk_clist_prepend();
o gtk_clist_insert();
per inserire il vettore
rispettivamente all'inizio della lista o in una posizione generica). Tutte necessitano come primo
parametro del puntatore alla clist, e del vettore di stringhe da inserire; gtk_clist_insert();
necessita inoltre del numero di riga in cui inserire le stringhe.
Per modificare la cella contenuta alla riga x, colonna y, si ricorre alla funzione gtk_clist_set_text();
,
alla quale passeremo il puntatore alla clist, il numero di riga, il numero di colonna e la stringa da inserire.
Invece per recuperare la stringa contenuta nella cella alla riga x, colonna y si ricorre alla funzione
gtk_clist_get_text();
, la stringa ci verrà restituita nella variabile text
.
Per pulire l'intera lista si usa la funzione gtk_clist_clear();
, mentre per eliminare una riga
si richiama la funzione gtk_clist_remove();
.
Cosa accade quando clicchiamo con il mouse su di una cella della lista? Verrà emesso il segnale
"select-row", il quale, se vogliamo compiere operazioni sugli elementi contenuti nella lista, deve
essere intercettato e gestito tramite una funzione di callback, che deve avere un prototipo simile a
quello della funzione user_function();
riportato in alto.
GtkWidget* gtk_text_new (GtkAdjustment *hadj, GtkAdjustment *vadj); guint gtk_text_get_point (GtkText *text); void gtk_text_set_point (GtkText *text, guint index); guint gtk_text_get_length (GtkText *text); void gtk_text_insert (GtkText *text, GdkFont *font, GdkColor *fore, GdkColor *back, const char *chars, gint length); |
GtkObject +----GtkWidget +----GtkEditable +----GtkEntry
Anche il widget GtkText è derivato da GtkEditable, e ci permette di gestire (visualizzare, editare, ...) un testo disposto su più linee. Questo widget ha una serie di "shortcuts" per le più comuni operazioni effettuabili da tastiera, definite di default. Ad esempio:
Ctrl-X Taglia il testo selezionato e lo salva nel clipboard Ctrl-C Copia il testo selezionato nel clipboard Ctrl-V Incolla il testo dal clipboard Ctrl-A Porta il cursore all'inizio della linea Ctrl-E Porta il cursore alla fine della linea ...
Il puntatore all'oggetto GtkText si ottiene dalla chiamata a gtk_text_new();
hadj
e vadj
possono essere impostati a NULL
, in questo caso
sarà GTK+ ad occuparsi di tutto.
gtk_text_get_point();
e gtk_text_set_point();
sono usate, rispettivamente, per
recuperare la posizione corrente del cusore, intesa come numero di caratteri dall'angolo in alto a sinistra
del widget, e per posizionare il cursore in un determinato punto; anche in questo caso la posizione va intesa
come numero di caratteri a partire dall'angolo in alto a sinistra.
gtk_text_get_length();
restituisce la lunghezza del testo, cioè il numero totale di caratteri immessi.
Per inserire una serie di caratteri nel widget si ricorre alla funzione
gtk_text_insert();
il primo argomento è il puntatore al widget
GtkText, il secondo è un puntatore al font che si vuole
utilizzare, il terzo e
il quarto indicano rispettivamente il colore del testo e di sfondo, il quinto è
il
buffer di caratteri da inserire, l'ultimo è il numero di caratteri da
inserire dal buffer, se si passa -1
tutto il buffer sarà inserito nel widget.
Se a font, fore, back
si passa il valore
NULL
, allora
saranno utilizzati il font e i colori di default.
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: Introduzione al Pascal -> |