<- PW - La stabilità dei personal computer - Indice Generale - Copertina - SL - Intro -> |
PlutoWare
L'articoloNello scorso numero abbiamo introdotto le nuove liste di Gtk+, focalizzando
la nostra attenzione soprattutto ai concetti di vista e modello. In questo
numero vedremo come sfruttare alcune delle potenzialità offerte dalle nuove liste.
|
Come già detto nella presentazione, in questo numero completeremo la trattazione delle liste di Gtk+. Il programma di prova presentato non fa nulla, assolutamente nulla, se non mostrare le potenzialità del nuovo widget lista. Inseriremo dei check button, delle icone e degli entry di testo editabili in una lista. Per il programma di esempio, spero mi perdonerete, ho fatto un po' di auto celebrazione, citando in rigoroso ordine alfabetico ;) i membri della redazione del Pluto Journal. Per il buon funzionamento del programma occorre scaricare il seguente file star.png, contenente la piccola stella che potete ammirare in figura 1, o rinominare una qualsiasi immagine che potete trovare sul vostro hard disk.
#include <gtk/gtk.h> typedef struct { gboolean edit; gchar *pixmap_file; gchar *redattore; gchar *rubrica; } RedazionePlutoJournal; RedazionePlutoJournal redazione[] = { {TRUE, "star.png", "Giampaolo Podda", "Coordinatore Pluto Journal"}, {TRUE, "star.png", "Tommaso Di Donato", "Coordinatore Pluto Journal"}, {TRUE, "star.png", "Beppe Lucente", "Hardware in Liberta'"}, {TRUE, "star.png", "Germano Rizzo", "PlutoWare"}, {TRUE, "star.png", "Marina Sturino", "Agora'"}, {TRUE, "star.png", "Nicola Fragale", "Igloo"}, {TRUE, "star.png", "Pietro Leone", "Traduzioni"}, {TRUE, "star.png", "Tommaso Di Donato", "Sistemi Aperti"}, {TRUE, "star.png", "Il Journal vuole te!!", "Collabora con noi ;)"}, {TRUE, "star.png", "Il Journal vuole te!!", "Collabora con noi ;)"}, {FALSE, NULL, NULL, NULL} }; enum { COLONNA_BOOLEAN, COLONNA_PIXMAP, COLONNA_REDATTORE, COLONNA_RUBRICA, COLONNE }; void cell_toggled_cb(GtkCellRendererToggle *cell, const gchar *path_string, gpointer data) { GtkTreeModel *model; GtkTreePath *path; GtkTreeIter iter; gboolean bool; model = (GtkTreeModel *) data; path = gtk_tree_path_new_from_string(path_string); gtk_tree_model_get_iter(model, &iter, path); gtk_tree_path_free(path); gtk_tree_model_get(model, &iter, COLONNA_BOOLEAN, &bool, -1); bool ^= 1; gtk_list_store_set(GTK_LIST_STORE(model), &iter, COLONNA_BOOLEAN, bool, -1); } void cell_edit_cb(GtkCellRendererText *cell, const gchar *path_string, const gchar *new_text, gpointer data) { GtkTreeModel *model; GtkTreePath *path; GtkTreeIter iter; gint *column; gchar *old_text; model = (GtkTreeModel *) data; path = gtk_tree_path_new_from_string(path_string); column = g_object_get_data(G_OBJECT(cell), "column"); gtk_tree_model_get_iter(model, &iter, path); gtk_tree_model_get(model, &iter, column, &old_text, -1); g_free (old_text); gtk_list_store_set(GTK_LIST_STORE(model), &iter, column, new_text, -1); } GdkPixbuf* create_new_pixbuf(gchar *filename) { GdkPixbuf *pix = NULL; g_return_val_if_fail(filename != NULL, NULL); pix = gdk_pixbuf_new_from_file(filename, NULL); if (!pix) g_print("Impossibile leggere il file di immagine %s", filename); return pix; } GtkWidget* create_window (void) { GtkWidget *window; GtkWidget *vbox; GtkWidget *esci; GtkWidget *scrolledwindow; GtkListStore *model; /* l'oggetto model */ GtkWidget *view; /* -\ */ GtkCellRenderer *renderer; /* -/ l'oggetto view */ GtkTreeIter iter; GdkPixbuf *image; RedazionePlutoJournal *r = redazione; window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_title (GTK_WINDOW (window), "Gtk+2.0 Usiamo le liste #2"); vbox = gtk_vbox_new (FALSE, 12); gtk_container_add(GTK_CONTAINER(window), vbox); gtk_container_set_border_width (GTK_CONTAINER (vbox), 12); gtk_widget_show (vbox); scrolledwindow = gtk_scrolled_window_new (NULL, NULL); gtk_box_pack_start (GTK_BOX (vbox), scrolledwindow, TRUE, TRUE, 0); gtk_container_set_border_width (GTK_CONTAINER (scrolledwindow), 10); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_widget_show (scrolledwindow); esci = gtk_button_new_with_label("Esci"); gtk_box_pack_start (GTK_BOX (vbox), esci, FALSE, FALSE, 0); gtk_widget_show (esci); model = gtk_list_store_new(COLONNE, G_TYPE_INT, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); while (r->redattore) { image = create_new_pixbuf(r->pixmap_file); gtk_list_store_append(GTK_LIST_STORE(model), &iter); gtk_list_store_set(GTK_LIST_STORE(model), &iter, COLONNA_PIXMAP, image, COLONNA_BOOLEAN, r->edit, COLONNA_REDATTORE, r->redattore, COLONNA_RUBRICA, r->rubrica, -1); r++; } view = gtk_tree_view_new_with_model (GTK_TREE_MODEL(model)); renderer = gtk_cell_renderer_toggle_new(); g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(cell_toggled_cb), model); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, // posiziona alla fine "Editabile", // titolo renderer, // la cella "active", // attributo della cella COLONNA_BOOLEAN, // colonna NULL); // fine attributi/colonna renderer = gtk_cell_renderer_pixbuf_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, // posiziona alla fine NULL, // questa colonna non ha titolo renderer, // la cella "pixbuf", // attributo della cella COLONNA_PIXMAP, // colonna NULL); // fine attributi/colonna renderer = gtk_cell_renderer_text_new(); g_object_set_data(G_OBJECT(renderer), "column", (gint *) COLONNA_REDATTORE); g_signal_connect(G_OBJECT(renderer), "edited", G_CALLBACK(cell_edit_cb), model); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, "Redattore", renderer, "text", COLONNA_REDATTORE, "editable", COLONNA_BOOLEAN, NULL); renderer = gtk_cell_renderer_text_new(); g_object_set_data(G_OBJECT(renderer), "column", (gint *) COLONNA_RUBRICA); g_signal_connect(G_OBJECT(renderer), "edited", G_CALLBACK(cell_edit_cb), model); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, "Rubrica", renderer, "text", COLONNA_RUBRICA, "editable", COLONNA_BOOLEAN, NULL); gtk_widget_show (view); g_object_unref(model); gtk_container_add (GTK_CONTAINER (scrolledwindow), view); gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (view), TRUE); g_signal_connect(G_OBJECT (esci), "clicked", G_CALLBACK (gtk_main_quit), NULL); 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; } |
Per compilare il programma, salvate l'esempio nel file lista.c e date il comando:
gcc -Wall lista.c -o lista `pkg-config --libs --cflags gtk+-2.0` |
Mi raccomando, fate attenzione: gli apici inversi si ottengono dalla pressione
del tasto ALT Gr
e del tasto apice '
. In figura 1
potete osservare il risultato finale.
In tabella sono elencate le funzioni che analizzeremo in questo articolo. Sono poche, ma molto potenti.
GtkCellRenderer* gtk_cell_renderer_pixbuf_new (void); GtkCellRenderer* gtk_cell_renderer_toggle_new (void); GtkCellRenderer* gtk_cell_renderer_text_new (void); void user_function (GtkCellRendererToggle *cellrenderertoggle, gchar *arg1, gpointer user_data); void user_function (GtkCellRendererText *cellrenderertext, gchar *arg1, gchar *arg2, gpointer user_data); |
Dal main
richiamiamo la routine principale che si
occuperà della creazione della nostra interfaccia grafica,
che è formata, molto semplicemente, da una scrolledwindow
all'interno della quale inseriremo la nostra lista, e da un bottone per chiudere
l'applicazione.
typedef struct { gboolean edit; gchar *pixmap_file; gchar *redattore; gchar *rubrica; } RedazionePlutoJournal; RedazionePlutoJournal redazione[] = { {TRUE, "star.png", "Giampaolo Podda", "Coordinatore Pluto Journal"}, ... {FALSE, NULL, NULL, NULL} }; enum { COLONNA_BOOLEAN, COLONNA_PIXMAP, COLONNA_REDATTORE, COLONNA_RUBRICA, COLONNE }; |
La redazione del Journal è contenuta nel vettore redazione
, ed
ogni elemento del vettore ha 4 campi: un campo booleano,
edit
, che utilizzeremo per permettere od impedire che i
campi contenenti il nome del responsabile di rubrica, redattore
,
ed il nome della rubrica, rubrica
, possano essere modificati. L'ultimo
campo pixmap_file
conterrà il nome del file dell'immagine che
inseriremo. Abbiamo quindi bisogno di quattro colonne, che enumereremo per
facilitarci in seguito la vita (vedi l'articolo precedente
http://www.pluto.linux.it/journal/pj0301/gtk+2-1.html).
Passiamo ora alla creazione del modello. Seguendo l'ordine stabilito nella
struttura RedazionePlutoJournal
, il modello definirà quattro tipi
diversi di colonna: la prima conterrà valori di tipo intero G_TYPE_INT
(il booleano), la seconda conterrà l'immagine GDK_TYPE_PIXBUF
, la terza
e la quarta conterranno delle stringhe G_TYPE_STRING
. A questo punto
non ci resta che popolare il modello con i dati contenuti nella struttura,
utilizzando un ciclo while
model = gtk_list_store_new(COLONNE, G_TYPE_INT, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); while (r->redattore) { image = create_new_pixbuf(r->pixmap_file); gtk_list_store_append(GTK_LIST_STORE(model), &iter); gtk_list_store_set(GTK_LIST_STORE(model), &iter, COLONNA_PIXMAP, image, COLONNA_BOOLEAN, r->edit, COLONNA_REDATTORE, r->redattore, COLONNA_RUBRICA, r->rubrica, -1); r++; } |
Ora che il modello è pronto, creiamo la vista.
renderer = gtk_cell_renderer_toggle_new(); g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(cell_toggled_cb), model); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, // posiziona alla fine "Editabile", // titolo renderer, // la cella "active", // attributo della cella COLONNA_BOOLEAN, // colonna NULL); // fine attributi/colonna |
La prima colonna deve contenere un check button, per permetterci di impostare
un valore booleano (TRUE/FALSE
). Richiameremo la funzione
gtk_cell_renderer_toggle_new()
, che ci ritornerà un puntatore
al widget GtkCellRenderer
. Quindi collegheremo l'oggetto rendered (il
nostro check button) alla funzione di callback cell_toggled_cb
,
ed infine inseriremo la colonna nella vista.
Alla funzione gtk_tree_view_insert_column_with_attributes()
, bisogna
passare il puntatore alla vista, la posizione che la colonna occuperà nella
vista (usiamo -1 per indicare di inserire la colonna in coda a tutte le altre), il
titolo della colonna (o NULL
se non vogliamo titoli) e, per finire,
dobbiamo passare un elenco di coppie attributo/colonna. Tale elenco dev'essere
terminato da NULL
. Nel nostro caso, a noi interessa che gli elementi
della colonna siano dei check button, degli elementi "attivi"; imposteremo quindi
la stringa "active" come attributo e COLONNA_BOOLEAN
come colonna.
renderer = gtk_cell_renderer_pixbuf_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, // posiziona alla fine NULL, // questa colonna non ha titolo renderer, // la cella "pixbuf", // attributo della cella COLONNA_PIXMAP, // colonna NULL); // fine attributi/colonna |
È il momento della colonna delle immagini. Come sempre iniziamo creando il widget
che sarà contenuto nella colonna, richiamando la funzione
gtk_cell_renderer_pixbuf_new()
, e, come nel caso precedente, creiamo
la colonna. Le differenze rispetto all'esempio precedente sono relative alla coppia
attributo/colonna. L'attributo della colonna, in questo caso è "pixbuf"
,
mentre per colonna dobbiamo fornire quello che abbiamo stabilito essere il
"numero" della colonna delle immagini, cioè COLONNA_PIXMAP
.
L'immagine è creata dalla subroutine create_new_pixbuf()
,
alla quale forniamo il nome del file contenente l'immagine, la quale
ci ritornerà un puntatore a GdkPixbuf
se tutto è andato bene,
NULL
in caso contrario.
renderer = gtk_cell_renderer_text_new(); g_object_set_data(G_OBJECT(renderer), "column", (gint *) COLONNA_REDATTORE); g_signal_connect(G_OBJECT(renderer), "edited", G_CALLBACK(cell_edit_cb), model); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, "Redattore", renderer, "text", COLONNA_REDATTORE, "editable", COLONNA_BOOLEAN, NULL); |
Come nei casi precedenti, creiamo il widget che sarà contenuto nella
colonna, (una casella di testo per le ultime due colonne), richiamando la
funzione gtk_cell_renderer_text_new()
, ed inseriamo le colonne
in coda alle altre. In questo caso le coppie attributo/colonna del widget
renderer sono due: con la prima coppia impostiamo l'attributo "text"
per la colonna COLONNA_REDATTORE
, con la seconda coppia impostiamo
l'attributo "editable"
e lo riferiamo alla colonna COLONNA_BOOLEAN
.
Perché ho effettuato questa scelta? Il widget GtkCellRendererText
ha
molte proprietà; tra queste è presente "editable"
, che è di tipo
booleano. In altre parole, la cella può essere o meno editabile a seconda del valore
TRUE/FALSE
assunto da questo attributo. Noi recupereremo questo
valore dalla colonna dei check button. Se un check button è impostato, allora
le caselle di testo presenti su quella riga saranno editabili.
La costruzione della lista è terminata, non ci resta che collegare i widget dei check button e delle caselle di testo alle opportune funzioni di callback. In tabella sono riportati i prototipi delle callback relative a questi widget.
void cell_toggled_cb(GtkCellRendererToggle *cell, const gchar *path_string, gpointer data) { ... void cell_edit_cb(GtkCellRendererText *cell, const gchar *path_string, const gchar *new_text, gpointer data) { ... |
Il funzionamento di queste due fuzioni è veramente molto semplice. La prima, che è richiamata ogni qualvolta clicchiamo su di un check button, si occupa di recuperare lo stato del bottone, calcolare il suo nuovo valore e di salvarlo.
La seconda funzione di callback ha il compito di salvare il nuovo testo
inserito nella casella. Preleva il vecchio testo, libera la memoria da esso
occupata, ed inserisce quello da noi digitato. La sola particolarità è relativa
al fatto che, avendo utilizzato una sola funzione di callback per due caselle
di testo (la colonna dei redattori e la colonna delle rubriche), è necessario
distinguere su quale renderer (quale delle due colonne) operare. Otteniamo
questo risultato associando (g_object_set_data();
) ad ogni renderer
la propria colonna, che sarà poi recuperata (g_object_get_data();
)
nella callback.
Bene, anche questa volta siamo giunti alla fine, non mi resta che darvi appuntamento per la prossima puntata...
http://developer.gnome.org/doc/API/2.0/glib/index.html
http://developer.gnome.org/doc/API/2.0/gobject/index.html
http://developer.gnome.org/doc/API/2.0/gdk/index.html
http://developer.gnome.org/doc/API/2.0/gtk/index.html
http://developer.gnome.org/doc/API/2.0/gdk-pixbuf/index.html
http://developer.gnome.org/doc/API/2.0/pango/index.html
http://developer.gnome.org/doc/API/2.0/atk/book1.html
L'autoreNicola Fragale. È iscritto ad Ingegneria Informatica alla 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. |
<- PW - La stabilità dei personal computer - Indice Generale - Copertina - SL - Intro -> |