Interfacce grafiche per i nostri programmi in GTK+ – parte 3 –

Come accennato nella seconda parte ora parliamo e faremo alcune prove con i nostri container, ovvero i contenitori dei nostri widget, negli esempi precedenti il nostro contenitore era la nostra finestra (window).

I container non sono altro che dei layout, che ci permettono non solo di contenere i nostri widget ma anche di disporli correttamente e a nostro piacimento sulla nostra window.

Ce ne sono di diversi e un po’ alla volta li elencheremo tutti!!! Bene dopo questa piccola premessa che ha detto tutto e niente procediamo con qualche esempio che ci chiarirà le idee a riguardo.

Gli Horizontal ed i Vertical Box

I GtkBox sono due GtkHbox che permette di disporre i nostri widget orizzontalmente e il gtkVbox che come si può bene intuire disporrà i nostri widget verticamente. Quindi procediamo con un bel GtkVbox:

#include <gtk/gtk.h>

static void destroy (GtkWidget*, gpointer);
static gboolean delete_event (GtkWidget*, GdkEvent*, gpointer);

int main (int argc, char *argv[])
{
// Andiamo ad inizializzare il nostro vertical box e lo chiameremo appunto vbox. E per l’esempio aggiungeremo al nostro Vbox anche due bottoni.
GtkWidget *window, *vbox, *button1, *button2;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), “BOXES”);
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
gtk_widget_set_size_request (window, 200, -1);
// Creiamo a questo punto il nostro vertical box, il quale accetta due parametri, il primo riguarda l’omogeneità nella disposizione dei nostri widget contenuti nel vbox menrtre il secondo setta lo spazio dal bordo esterno dei nostri widget.
vbox = gtk_vbox_new (TRUE,10);
button1 = gtk_button_new_with_label (“NASCONDI”);
// Aggiungo il button1 appena creato al nostro container vbox. Dove il segnale collegato al button1 distruge se stesso.
gtk_box_pack_start_defaults (GTK_BOX (vbox), button1);
g_signal_connect_swapped (G_OBJECT (button1), “clicked”, G_CALLBACK (gtk_widget_destroy), (gpointer) button1);
button2 = gtk_button_new_with_label (“CHIUDI”);
// Aggiungo il button2 al container vbox. Button2 invece chiude la finestra.
gtk_box_pack_start_defaults (GTK_BOX (vbox), button2);
g_signal_connect_swapped (G_OBJECT (button2), “clicked”, G_CALLBACK (gtk_widget_destroy), (gpointer) window);
// Aggiungo il nostro layout vbox al nostro container che in questo caso è la nostra window. In poche parole abbiamo il container vbox che contiene i tasti il quale a sua volta è contenuto nel container window.
gtk_container_add (GTK_CONTAINER (window), vbox);
g_signal_connect (G_OBJECT (window), “destroy”, G_CALLBACK (destroy), NULL);
g_signal_connect (G_OBJECT (window), “delete_event”, G_CALLBACK (delete_event), NULL);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}

static void destroy (GtkWidget *window, gpointer data)
{
gtk_main_quit ();
}

static gboolean delete_event (GtkWidget *window, GdkEvent *event, gpointer data)
{
return FALSE;
}
Se tutto è andato bene avremo la finestra come in Figura 1:

Se clicckeremo sul tasto nascondi sparirà il nostro bottone, mentre se premiamo chiudi chiuderemo la nostra applicazione.
Lascio a voi, con gli stessi tasti, creare una horizontal box, un piccolo suggerimento… sicuramente non creeremo il nostro oggetto con il comando gtk_vbox_new 😉

Gli Horizontal ed i Vertical Panes

I panes sono container che allineano e separano i nostri widget con una barra, per fare un esempio i menù delle applicazioni dove ci sono dei separatori!!! Infatti i panes possono essere usati anche per creare i nostri menu.

Un esempio può essere questo:

#include <gtk/gtk.h>

static void destroy (GtkWidget*, gpointer);
static gboolean delete_event (GtkWidget*, GdkEvent*, gpointer);

int main (int argc, char *argv[])
{
// Inizializziamo il nostro oggetto hpaned ed i tre bottoni che metteremo nel nostro horizontal paned.
GtkWidget *window, *hpaned, *button1, *button2;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "PANES");
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
gtk_widget_set_size_request (window, 200, -1);
g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
g_signal_connect (G_OBJECT (window), "delete_event", G_CALLBACK (delete_event), NULL);
// Creiamo il paned orrizontale. Analogo comando ci sarà per il verticale cioè gtk_vpaned_new.
hpaned = gtk_hpaned_new ();
button1 = gtk_button_new_with_label ("PRIMO");
button2 = gtk_button_new_with_label ("SECONDO");
g_signal_connect_swapped (G_OBJECT (button1), "clicked", G_CALLBACK (gtk_widget_destroy), (gpointer) window);
g_signal_connect_swapped (G_OBJECT (button2), "clicked", G_CALLBACK (gtk_widget_destroy), (gpointer) window);
// Aggiungiamo al nostro paned i due bottoni.
gtk_paned_add1 (GTK_PANED (hpaned), button1);
gtk_paned_add2 (GTK_PANED (hpaned), button2);
// Inseriamo il nostro horizontal paned nel container window.
gtk_container_add (GTK_CONTAINER (window), hpaned);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}

static void destroy (GtkWidget *window, gpointer data)
{
gtk_main_quit ();
}

static gboolean delete_event (GtkWidget *window, GdkEvent *event, gpointer data)
{
return FALSE;
}
Se tutto è andato bene avremo una finestra con due tasti come in Figura 2:

Sicuramente qualcuno proverà a inserire un terzo tasto nel paned, però verranno sollevati degli errori alla compilazione. Questo perchè come nel widget window, che può contenere un solo oggetto widget (a meno che non si utilizzi un layout) nel paned possono essere inseriti solo due oggetti widget (sempre che non si utilizzi un layout).

A voi ora il compito di creare un paned verticale utilizzando i tasti precedentemente creati ;-).

I Tables

Fino ad ora abbiamo visto sono dei layout che permettono di posizionare i nostri widget verticalmente oppure orizzontalmente, ecco che allora ci viene in aiuto il widget GtkTable il quale ci permette di definire una tabella nella quale inserire i nostri oggetti seguendo una nostra logica.

#include <gtk/gtk.h>

static void destroy (GtkWidget*, gpointer);
static gboolean delete_event (GtkWidget*, GdkEvent*, gpointer);
static void button_click (void);

int main (int argc, char *argv[])
{
// Inizializziamo i nostri oggetti widget tra cui uno che chiameremo table.
GtkWidget *window, *table, *label, *label2, *name, *button, *button2;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), “TABLE”);
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
gtk_widget_set_size_request (window, -1, -1);
// Creiamo una un layout di tipo tabella, e gli passeremo come parametri il numero di righe, il numero di colonne e se vogliamo una disposizione dei nostri oggetti sulla tabella omogenea gli passeremo la voce TRUE.
table = gtk_table_new (3, 2, TRUE);
label = gtk_label_new (“TESTO DI PROVA”);
label2 = gtk_label_new (“Inserisci testo: “);
name = gtk_entry_new ();
button = gtk_button_new_with_label (“INVIA”);
button2 = gtk_button_new_with_label (“CHIUDI”);
// Aggiungiamo i widget alla tabella. Passandogli le coordinate, cioè mettendo i nostri oggetti widget nelle celle della nostra tabella passando a gtk_table_attach la tabella nella quale vogliamo inserire l’oggetto, le coordinate sinistra, destra, alto e basso. Gli diciamo di espandere l’oggetto tanto grande quanto la cella per quanto riguarda l’asse x e per l’asse y ed infine il padding (lo spazio dal bordo della cella) per l’asse x e l’asse y.
gtk_table_attach (GTK_TABLE (table), label, 0, 2, 0, 1, GTK_EXPAND, GTK_SHRINK, 0 ,0);
gtk_table_attach (GTK_TABLE (table), label2, 0, 1, 1, 2, GTK_EXPAND, GTK_SHRINK, 0 ,0);
gtk_table_attach (GTK_TABLE (table), name, 1, 2, 1, 2, GTK_EXPAND, GTK_SHRINK, 0 ,0);
gtk_table_attach (GTK_TABLE (table), button, 0, 1, 2, 3, GTK_EXPAND, GTK_SHRINK, 0 ,0);
gtk_table_attach (GTK_TABLE (table), button2, 1, 2, 2, 3, GTK_EXPAND, GTK_SHRINK, 0 ,0);
// Settiamo lo spazio per riga e per colonna.
gtk_table_set_row_spacings (GTK_TABLE (table), 5);
gtk_table_set_col_spacings (GTK_TABLE (table), 5);
// Alla fine aggiungiamo la nostra tabella al container window… 😉
gtk_container_add (GTK_CONTAINER (window), table);
g_signal_connect_swapped (G_OBJECT (button), “clicked”, G_CALLBACK (button_click), NULL);
g_signal_connect_swapped (G_OBJECT (button2), “clicked”, G_CALLBACK (gtk_widget_destroy), (gpointer) window);
g_signal_connect (G_OBJECT (window), “destroy”, G_CALLBACK (destroy), NULL);
g_signal_connect (G_OBJECT (window), “delete_event”, G_CALLBACK (delete_event), NULL);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}

static void destroy (GtkWidget *window, gpointer data)
{
gtk_main_quit ();
}

static gboolean delete_event (GtkWidget *window, GdkEvent *event, gpointer data)
{
return FALSE;
}

// Definisco la funzione da connettere all’evento clicked del bottone che abbiamo aggiunto alla nostra tabella contenuta nella window. Potrebbe essere usata per inviare i dati inseriti da altre parti.
void button_click (void)
{
g_print(“MI HAI CLICCATO!\n”);
}
Il tutto dovrebbe presentarsi come in Figura 3:

Prima di procedere e definire gli altri widget ci soffermiamo sulla definizione delle coordinate delle celle della nostra tabella riportando questo semplice schemino che chiarirà ogni dubbio sulle coordinate inserite prima nel listato:

Spero che lo schema sia chiaro ricapitolando i numeri corrispondo ai vertici della nostra tabella. Dove per fare un unica cella per Label1 abbiamo dato come coordinate 0,2 (tutta la riga) e 0,1 (solo la prima riga).

I Fixed container

I Fixed container sono dei contenitori nei quali possiamo inserire qualsiasi oggetto widget però impostando una posizione fissa sulla finestra. Qui si riporta un esempio di un fixed container:

#include <gtk/gtk.h>

static void destroy (GtkWidget*, gpointer);
static gboolean delete_event (GtkWidget*, GdkEvent*, gpointer);

int main (int argc, char *argv[])
{
// Inizializziamo l’oggetto fixed e due tasti.
GtkWidget *window, *fixed, *button1, *button2;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), “FIXED”);
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
gtk_widget_set_size_request (window, -1, -1);
// Creiamo il nostro oggetto fixed.
fixed = gtk_fixed_new ();
button1 = gtk_button_new_with_label (“Pixel by pixel…”);
button2 = gtk_button_new_with_label (“You choose my fate!”);
g_signal_connect_swapped (G_OBJECT (button1), “clicked”, G_CALLBACK (gtk_widget_destroy), (gpointer) window);
g_signal_connect_swapped (G_OBJECT (button2), “clicked”, G_CALLBACK (gtk_widget_destroy), (gpointer) window);
// Aggiungiamo i due tasti al container fixed passando appunto a quale fixed vogliamo inserire il tasto quale oggetto vogliamo inserire ed in fine le coordinate che corrisponderanno alla posizione degli oggetti sulla finestra.
gtk_fixed_put (GTK_FIXED (fixed), button1, 0, 0);
gtk_fixed_put (GTK_FIXED (fixed), button2, 20, 30);
// Alla fine aggiungiamo al container window il container fixed creato.
gtk_container_add (GTK_CONTAINER (window), fixed);
g_signal_connect (G_OBJECT (window), “destroy”, G_CALLBACK (destroy), NULL);
g_signal_connect (G_OBJECT (window), “delete_event”, G_CALLBACK (delete_event), NULL);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}

static void destroy (GtkWidget *window, gpointer data)
{
gtk_main_quit ();
}

static gboolean delete_event (GtkWidget *window, GdkEvent *event, gpointer data)
{
return FALSE;
}
Il tutto dovrebbe apparire come la Figura 4:

Gli Expanders

Gli Expander sono un altro tipo di container che si potrebbero definire come delle tendine che possono contenere a loro volta altri oggetti widget. Vediamo subito un esempio:

#include <gtk/gtk.h>

static void destroy (GtkWidget*, gpointer);
static gboolean delete_event (GtkWidget*, GdkEvent*, gpointer);

int main (int argc, char *argv[])
{
// Inizializziamo i nostri oggetti, la nostra solita finestra, un container expander, una etichetta, un tasto ed una tablella, in poche parole il nostro container window conterr?una container expanders che a sua volta contiene un container table con gli oggetti button e label.
GtkWidget *window, *expander, *label, *button, *table;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), “EXPANDER”);
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
gtk_widget_set_size_request (window, -1, -1);
// Definiamo la nostra tabella con due righe e una colonna.
table = gtk_table_new (2, 1, TRUE);
expander = gtk_expander_new_with_mnemonic (“Clicca per altre _info!”);
label = gtk_label_new (“Mostra e nascondi \nquello che vuoi!”);
button = gtk_button_new_with_label (“CHIUDI”);
// Connettiamo il nostro tasto all’evento clicked il quale distrugger?la nostra finestra.
g_signal_connect_swapped (G_OBJECT (button), “clicked”, G_CALLBACK (gtk_widget_destroy), (gpointer) window);
// Posizioniamo i nostri oggetti button e label nella tabella.
gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, GTK_EXPAND, GTK_SHRINK, 0 ,0);
gtk_table_attach (GTK_TABLE (table), button, 0, 1, 1, 2, GTK_EXPAND, GTK_SHRINK, 0 ,0);
// La nostra tabella sar?contenuta nel container expanders.
gtk_container_add (GTK_CONTAINER (expander), table);
gtk_expander_set_expanded (GTK_EXPANDER (expander), TRUE);
// A sua volta l’expanders sar?contenuto nella nostra finestra.
gtk_container_add (GTK_CONTAINER (window), expander);
g_signal_connect (G_OBJECT (window), “destroy”, G_CALLBACK (destroy), NULL);
g_signal_connect (G_OBJECT (window), “delete_event”, G_CALLBACK (delete_event), NULL);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}

static void destroy (GtkWidget *window, gpointer data)
{
gtk_main_quit ();
}

static gboolean delete_event (GtkWidget *window, GdkEvent *event, gpointer data)
{
return FALSE;
}

Il tutto poi dovrebbe apparire in questa maniera (Figura 5) forse vi sarete chiesti penchè gli oggetti button e label non li abbiamo messi direttamente nell’expanders, questo perchè il container expanders può contenere un solo oggetto come il container window.

Gli Handle Boxes

Gli Handle Boxes sono particolari container i cui oggetti figli possono essere spostati con il mouse con un semplice drag and drop.

Solitamente questo tipo di container può essere usato per contenere dei toolbar oppure dei toolkit, vediamo un semplice esempio:

#include <gtk/gtk.h>

static void destroy (GtkWidget*, gpointer);
static gboolean delete_event (GtkWidget*, GdkEvent*, gpointer);

int main (int argc, char *argv[])
{
// Definiamo il nostro oggetto handle e la label che sar?contenuto in esso.
GtkWidget *window, *handle, *label;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), “HANDLE”);
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
gtk_widget_set_size_request (window, -1, -1);
// Handle – sono le barre che contengono bottoni per le applicazioni. In questo esempio conterr?solo una label.
handle = gtk_handle_box_new ();
label = gtk_label_new (“Detach ME!”);
// Definiamo il nostro handle e la posizione dello stesso..
gtk_handle_box_set_shadow_type (GTK_HANDLE_BOX (handle), GTK_SHADOW_IN);
gtk_handle_box_set_handle_position (GTK_HANDLE_BOX (handle), GTK_POS_LEFT);
// Definiamo la posizione nell’oggetto handle.
gtk_handle_box_set_snap_edge (GTK_HANDLE_BOX (handle), GTK_POS_TOP);
// Aggiungiamo la nostra label all’handle e successivamente l’handle al nostro container window.
gtk_container_add (GTK_CONTAINER (handle), label);
gtk_container_add (GTK_CONTAINER (window), handle);
g_signal_connect (G_OBJECT (window), “destroy”, G_CALLBACK (destroy), NULL);
g_signal_connect (G_OBJECT (window), “delete_event”, G_CALLBACK (delete_event), NULL);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}

static void destroy (GtkWidget *window, gpointer data)
{
gtk_main_quit ();
}

static gboolean delete_event (GtkWidget *window, GdkEvent *event, gpointer data)
{
return FALSE;
}

Ed ecco il risultato… (Figura 6) adesso cliccate sulla label e trascinate fuori la label della finestra ed ecco che che si staccher?

I Notebooks

I Notebooks sono dei container che permettono di ordinare i nostri oggetti widget in diverse pagine tramite dei tasti definiti come tab. A seconda del tab che clicchiamo avremo all’interno diversi widget.

Guardiamo subito in dettaglio il listato per crearne uno, quando andremo definire un notebook dovremo definire delle label per i nostri tab e degli oggetti da inserire nelle nostre pagine:

#include <gtk/gtk.h>

static void destroy (GtkWidget*, gpointer);
static gboolean delete_event (GtkWidget*, GdkEvent*, gpointer);
static void switch_page (GtkButton*, GtkNotebook*);

int main (int argc, char *argv[])
{
// Definiamo tutti gli oggetti per implementare il nostro notebook, definendo appunto il nostro notebook e le label per i nostri tab oltre a definire i child (le pagine) e qualche layout come una tabella per disporre pi?oggetti.
GtkWidget *window, *notebook, *button, *label1, *label2, *label3, *label4, *label5, *label6, *child1, *child2, *child3, *table;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), “NOTEBOOKS”);
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
gtk_widget_set_size_request (window, 300, 200);
// Settiamo una icona per la nostra finestra.
gtk_window_set_icon_from_file (GTK_WINDOW (window), “/home/user/SCRIPT/kate.xpm”, NULL);
// Inizializziamo il nostro oggetto notebook.
notebook = gtk_notebook_new ();
// Inizializziamo tutte la label che saranno inserite nelle nostre tab.
label1 = gtk_label_new (“PAGE1”);
label2 = gtk_label_new (“PAGE2”);
label3 = gtk_label_new (“PAGE3”);
// Inizializziamo cos?le nostre pagine inserendo una label.
child1 = gtk_label_new (“Vai a PAGE2!”);
child2 = gtk_label_new (“Vai a PAGE1!”);
child3 = gtk_label_new (“Sei alla PAGE3!”);
// Cliccando sulla label della pagina andremo alla pagina 1 o 2 a seconda di dove ci troviamo. Nella funzione che abbiamo creato e chiamata switch_page andremo a leggere la pagina corrente per poi settare quella che abbiamo impostato al click sulla label.
g_signal_connect (G_OBJECT (child1), “clicked”, G_CALLBACK (switch_page), (gpointer) notebook);
g_signal_connect (G_OBJECT (child2), “clicked”, G_CALLBACK (switch_page), (gpointer) notebook);
// Inseriamo cos?le nostre pagine al notebook definendo ed inserendo le nostre label sui tab.
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), child1, label1);
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), child2, label2);
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), child3, label3);
// Mettiamo i nostri tab in basso.
gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_BOTTOM);
// Inseriamo come child (come pagina) al posto di definire una label inseriamo una tabella con diversi oggetti disposti.
table = gtk_table_new (3, 1, TRUE);
label4 = gtk_label_new (“Testo di prova 1”);
label5 = gtk_label_new (“Testo di prova 2”);
button = gtk_button_new_with_label (“CHIUDI TUTTA LA WINDOW”);
g_signal_connect_swapped (G_OBJECT (button), “clicked”, G_CALLBACK (gtk_widget_destroy), (gpointer) window);
gtk_table_attach (GTK_TABLE (table), label4, 0, 1, 0, 1, GTK_EXPAND, GTK_SHRINK, 0 ,0);
gtk_table_attach (GTK_TABLE (table), label5, 0, 1, 1, 2, GTK_EXPAND, GTK_SHRINK, 0 ,0);
gtk_table_attach (GTK_TABLE (table), button, 0, 1, 2, 3, GTK_EXPAND, GTK_SHRINK, 0, 0);
label6 = gtk_label_new (“TASTI”);
//Il child pu?essere qualsiasi oggetto widget (per esempio una table che contiene altri widget).
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), table, label6);
gtk_container_add (GTK_CONTAINER (window), notebook);
g_signal_connect (G_OBJECT (window), “destroy”, G_CALLBACK (destroy), NULL);
g_signal_connect (G_OBJECT (window), “delete_event”, G_CALLBACK (delete_event), NULL);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}

static void destroy (GtkWidget *window, gpointer data)
{
gtk_main_quit ();
}

static gboolean delete_event (GtkWidget *window, GdkEvent *event, gpointer data)
{
return FALSE;
}

static void switch_page (GtkButton *button, GtkNotebook *notebook)
{
// Leggo la pagina corrente.
gint page = gtk_notebook_get_current_page (notebook);
if (page == 0)
{
// Setto la pagina desiderata in questo caso la pagina 1.
gtk_notebook_set_current_page (notebook, 1);
}else{
gtk_notebook_set_current_page (notebook, 0);
}
}

Il tutto dovrebbe apparire come in Figura 7:

Ora non ci resta che fare alcune prove aggiungendo o togliendo pagine nelle quali potremo impostare dei layout a piacere inserendo delle entry oppure dei tasti. 😉