<- PW: Intro - Archivio Generale - Copertina - PW: Bash -> |
PlutoWare
L'articoloNegli ultimi numeri del Pluto Journal abbiamo introdotto le librerie di GTK+, iniziamo con questo articolo ad esaminare le librerie di Gnome. Vedremo come realizzare una piccola applicazione dotata esclusivamente di una barra per i menu, una toolbar e una Appbar. Vedremo inoltre come creare le finestre di dialogo che ci permetteranno di comunicare con l'utente nel modo più appropriato possibile. Come sempre il tutto è accompagnato da un programma di prova. Non mi resta che augurarvi buona lettura ;-) |
Gnome è parte del progetto GNU, è l'acronimo di GNU Network Object Model Environment e nasce nel 1997, con l'obiettivo di rendere disponibile una tecnologia simile a OLE e COM di Microsoft sui sistemi Unix. Il progetto iniziale si è sviluppato molto rapidamente, spostandosi poi, verso la creazione di un desktop environment, spinto sia dal successo che stava ottenendo KDE e dal fatto che le librerie QT su cui KDE è basato erano allora soggette ad una licenza non open source, sia dalla semplicità di utilizzo delle librerie GTK+ per la creazione di interfacce grafiche.
Con le vecchie librerie di GTK+ (fino alle 1.2.x) il programmatore poteva decidere l'aspetto grafico della propria applicazione in totale autonomia, tuttavia questa libertà aveva anche degli aspetti controproducenti. I programmi sviluppati con GTK+ non avevano un aspetto coerente. Pensiamo alle icone inserite sulle toolbar, un programmatore potrebbe decidere di associare una certa icona all'apertura di un file, mentre un altro programmatore potrebbe scegliere di associare alla stessa azione una icona completamente diversa. Oppure un programmatore potrebbe associare alla sequenza di tasti ctrl+c la funzione chiudi della propria applicazione. Tutto ciò genera interfacce grafiche tutte diverse tra loro, e aumenta la confusione nell'utente finale (abituato a cercare e trovare voci di menu in determinati posti, ad utilizzare alcune combinazioni di tasti per compiere operazioni di uso comune, ad es. ctrl+x, ctrl+c, ctrl+v per taglia, copia, incolla).
Con Gnome si è cercato di rimediare a questi problemi. Le librerie di Gnome hanno standardizzato alcuni aspetti delle interfacce grafiche. Mettono a disposizione del programmatore un set di icone predefinito (l'icona "salva con nome" sarà identica su tutte le applicazioni scritte per Gnome), un insieme di finestre di dialogo ed altro ancora. Utilizzando, quindi, le librerie di Gnome è possibile scrivere applicazioni coerenti, dotate tutte di un aspetto uniforme. Le applicazioni scritte per Gnome, possono essere eseguite anche al di fuori di Gnome stesso (in KDE ad esempio), è sufficiente che sul computer siano installate le librerie. Alcuni delle innovazioni introdotte da Gnome (set di icone, finestre di dialogo standard) sono state riprese dalle nuove GTK+2.0.
Le librerie di Gnome a cui mi riferirò in questo articolo sono relative alla versione 1.4.x. Potete scaricare la documentazione da:
In figura 1 e figura 2 possiamo osservare il programma.
#include <gnome.h> #define VERSION "0.1" #define PACKAGE "CiaoGnome" /* creiamo una finestra di dialogo di "classe" informazioni */ void display_message (gchar *str) { GtkWidget *messagebox; GtkWidget *button; /* Creiamo il dialogo con un bottone OK che rimuoveremo immediatamente. Tutto questo per evitare un bug nelle attuali librerie di gnome */ messagebox = gnome_message_box_new (str, GNOME_MESSAGE_BOX_INFO, GNOME_STOCK_BUTTON_OK, NULL); gtk_container_remove (GTK_CONTAINER (GNOME_DIALOG (messagebox)->action_area), GNOME_DIALOG (messagebox)->buttons->data); GNOME_DIALOG (messagebox)->buttons = NULL; gtk_window_set_title (GTK_WINDOW (messagebox), "Informazioni"); gnome_dialog_append_button (GNOME_DIALOG (messagebox), GNOME_STOCK_BUTTON_OK); button = GTK_WIDGET (g_list_last (GNOME_DIALOG (messagebox)->buttons)->data); gtk_widget_show (button); gtk_signal_connect_after (GTK_OBJECT (messagebox), "close", GTK_SIGNAL_FUNC (gtk_widget_destroy), NULL); gtk_widget_show(messagebox); } /* creiamo una finestra di dialogo di "classe" Warning */ void non_implementata (void) { GtkWidget *messagebox; GtkWidget *button; messagebox = gnome_message_box_new ("Attenzione questa opzione non è ancora disponibile", GNOME_MESSAGE_BOX_WARNING, GNOME_STOCK_BUTTON_OK, NULL); gtk_container_remove (GTK_CONTAINER (GNOME_DIALOG (messagebox)->action_area), GNOME_DIALOG (messagebox)->buttons->data); GNOME_DIALOG (messagebox)->buttons = NULL; gtk_window_set_title (GTK_WINDOW (messagebox), "Informazioni"); gnome_dialog_append_button (GNOME_DIALOG (messagebox), GNOME_STOCK_BUTTON_OK); button = GTK_WIDGET (g_list_last (GNOME_DIALOG (messagebox)->buttons)->data); gtk_widget_show (button); gtk_signal_connect (GTK_OBJECT (messagebox), "close", GTK_SIGNAL_FUNC (gtk_widget_destroy), NULL); gtk_widget_show(messagebox); } /* creiamo una finestra di dialogo con le note di copyrigth del nostro programma */ void info (void) { const gchar *authors[] = { "Nicola Fragale", /* se il programma è sviluppato da più persone, inserirne qui i nominativi */ NULL }; GtkWidget *info_dialog; info_dialog = gnome_about_new (PACKAGE, VERSION, "Copyleft (c) 2002 Pluto Journal", authors, "Inseriamo qui una breve descrizione del nostro programma....", NULL); gtk_window_set_modal (GTK_WINDOW (info_dialog), TRUE); gtk_signal_connect (GTK_OBJECT (info_dialog), "close", GTK_SIGNAL_FUNC (gtk_widget_destroy), NULL); gtk_widget_show(info_dialog); } /* funzioni di callback richiamate alla pressione di un bottone sulla toolbar o all'attivazione di una voce di menu */ void on_new_file_activate (GtkMenuItem *menuitem, gpointer user_data) { gchar *str; if (strcmp(user_data, "tb") == 0) str = g_strdup("Hai selezionato Nuovo File dalla toolbar"); else str = g_strdup("Hai selezionato Nuovo File dal menu"); display_message(str); } void on_open_activate (GtkMenuItem *menuitem, gpointer user_data) { gchar *str; str = g_strdup_printf("Hai selezionato Apri da: %s", (gchar*) user_data); display_message(str); g_free(str); } void on_save_activate (GtkMenuItem *menuitem, gpointer user_data) { gchar *str; if (strcmp(user_data, "tb") == 0) str = g_strdup("Hai selezionato Salva dalla toolbar"); else str = g_strdup("Hai selezionato Salva dal menu"); display_message(str); } void on_save_as_activate (GtkMenuItem *menuitem, gpointer user_data) { display_message("Hai selezionato \"Salva con nome\" dal menu"); } void on_cut_activate (GtkMenuItem *menuitem, gpointer user_data) { non_implementata(); } void on_copy_activate (GtkMenuItem *menuitem, gpointer user_data) { non_implementata(); } void on_paste_activate (GtkMenuItem *menuitem, gpointer user_data) { non_implementata(); } /* definiamo i nostri menu */ /* Menu "File" */ static GnomeUIInfo file_menu_uiinfo[] = { GNOMEUIINFO_MENU_NEW_ITEM ("_Nuovo File", NULL, on_new_file_activate, "fm"), GNOMEUIINFO_MENU_OPEN_ITEM (on_open_activate, "menu file"), GNOMEUIINFO_MENU_SAVE_ITEM (on_save_activate, "fm"), GNOMEUIINFO_MENU_SAVE_AS_ITEM (on_save_as_activate, NULL), GNOMEUIINFO_SEPARATOR, GNOMEUIINFO_MENU_EXIT_ITEM (gtk_main_quit, NULL), GNOMEUIINFO_END }; /* Menu "Edit" ("Modifica") */ static GnomeUIInfo edit_menu_uiinfo[] = { GNOMEUIINFO_MENU_CUT_ITEM (on_cut_activate, NULL), GNOMEUIINFO_MENU_COPY_ITEM (on_copy_activate, NULL), GNOMEUIINFO_MENU_PASTE_ITEM (on_paste_activate, NULL), GNOMEUIINFO_END }; /* Menu "Aiuto" */ static GnomeUIInfo help_menu_uiinfo[] = { GNOMEUIINFO_MENU_ABOUT_ITEM (info, NULL), GNOMEUIINFO_END }; /* Raggruppiamo i menu in una barra */ static GnomeUIInfo menubar_uiinfo[] = { GNOMEUIINFO_MENU_FILE_TREE (file_menu_uiinfo), GNOMEUIINFO_MENU_EDIT_TREE (edit_menu_uiinfo), GNOMEUIINFO_MENU_HELP_TREE (help_menu_uiinfo), GNOMEUIINFO_END }; /* Disegniamo l'interfaccia grafica della nostra applicazione */ GtkWidget* create_app (void) { GtkWidget *app; /* puntatore alla finestra principale */ GtkWidget *dock; /* contenitore rimuovibile*/ GtkWidget *toolbar; GtkWidget *tmp_toolbar_icon; GtkWidget *appbar; GtkWidget *nuovo; GtkWidget *apri; GtkWidget *salva; GtkWidget *esci; GtkWidget *vbox1; GtkWidget *label1; /* creiamo la finestra principale */ app = gnome_app_new (PACKAGE, "Ciao Gnome..."); gtk_widget_set_usize(GTK_WIDGET(app), 400, 300); /* recuperiamo il puntatore al dock */ dock = GNOME_APP (app)->dock; gtk_widget_show (dock); /* creiamo la barra dei menu */ gnome_app_create_menus (GNOME_APP (app), menubar_uiinfo); /* creiamo la toolbar */ toolbar = gtk_toolbar_new (GTK_ORIENTATION_HORIZONTAL, GTK_TOOLBAR_BOTH); gtk_widget_show (toolbar); /* inseriamo la toolbar in un nuovo dock */ gnome_app_add_toolbar (GNOME_APP (app), GTK_TOOLBAR (toolbar), "toolbar", GNOME_DOCK_ITEM_BEH_EXCLUSIVE, GNOME_DOCK_TOP, 1, 0, 0); /* impostiamo alcune caratteristiche della toolbar */ gtk_container_set_border_width (GTK_CONTAINER (toolbar), 1); gtk_toolbar_set_space_size (GTK_TOOLBAR (toolbar), 16); gtk_toolbar_set_space_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_SPACE_LINE); gtk_toolbar_set_button_relief (GTK_TOOLBAR (toolbar), GTK_RELIEF_NONE); /* creiamo le icone da inserire nella toolbar */ tmp_toolbar_icon = gnome_stock_pixmap_widget (app, GNOME_STOCK_PIXMAP_NEW); nuovo = gtk_toolbar_append_element (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_CHILD_BUTTON, NULL, "Nuovo", "Nuovo file", NULL, tmp_toolbar_icon, on_new_file_activate, "tb"); gtk_widget_show (nuovo); tmp_toolbar_icon = gnome_stock_pixmap_widget (app, GNOME_STOCK_PIXMAP_OPEN); apri = gtk_toolbar_append_element (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_CHILD_BUTTON, NULL, "Apri", "Apri file", NULL, tmp_toolbar_icon, on_open_activate, "tool bar"); gtk_widget_show (apri); tmp_toolbar_icon = gnome_stock_pixmap_widget (app, GNOME_STOCK_PIXMAP_SAVE); salva = gtk_toolbar_append_element (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_CHILD_BUTTON, NULL, "Salva", "Salve file", NULL, tmp_toolbar_icon, on_save_activate, "tb"); gtk_widget_show (salva); gtk_toolbar_append_space (GTK_TOOLBAR (toolbar)); tmp_toolbar_icon = gnome_stock_pixmap_widget (app, GNOME_STOCK_PIXMAP_EXIT); esci = gtk_toolbar_append_element (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_CHILD_BUTTON, NULL, "Esci", NULL, NULL, tmp_toolbar_icon, gtk_main_quit, NULL); gtk_widget_show (esci); /* creiamo una scatola, in cui inseriremo gli elementi grafici della nostra applicazione */ vbox1 = gtk_vbox_new (FALSE, 0); gtk_widget_show (vbox1); gnome_app_set_contents (GNOME_APP (app), vbox1); /* la nostra applicazione grafica è tutta qui, una etichetta */ label1 = gtk_label_new ("Ciao Mondo"); gtk_widget_show (label1); gtk_box_pack_start (GTK_BOX (vbox1), label1, TRUE, TRUE, 0); /* creiamo una AppBar (comprende una statusbar, e una progressbar) */ appbar = gnome_appbar_new (TRUE, TRUE, GNOME_PREFERENCES_NEVER); gnome_app_set_statusbar (GNOME_APP (app), appbar); gtk_widget_show (appbar); /* installiamo i tooltips */ gnome_app_install_menu_hints (GNOME_APP (app), menubar_uiinfo); /* gestiamo almeno un segnale per uscire dalla applicazione */ gtk_signal_connect (GTK_OBJECT (app), "destroy", GTK_SIGNAL_FUNC (gtk_main_quit), NULL); /* ritorniamo il puntatore alla finestra principale del nostro programma */ return app; } /* Main */ int main (int argc, char *argv[]) { GtkWidget *app; /* inizializziamo le librerie di Gnome */ gnome_init (PACKAGE, VERSION, argc, argv); app = create_app (); gtk_widget_show (app); gtk_main (); return 0; } |
Salvate il programma di prova nel file ciao_gnome.c e compilate con:
gcc -Wall -g ciao_gnome.c -o ciao_gnome `gnome-config gnomeui --libs --cflags` |
Lo script di shell gnome-config si occuperà di passare al compilatore e al linker i giusti percorsi per i file da includere e le librerie dinamiche da collegare al nostro eseguibile. Chiamate gnome-config --help per ottenere un'elenco completo delle librerie supportate dallo script.
Come visto con GTK+, anche per poter utilizzare le librerie di Gnome è
necessario prima di tutto inizializzarle. La funzione che inizializza le
librerie di Gnome, che a loro volta inizializzeranno quelle di GTK+, GDK e Glib,
è gnome_init()
. Il prototipo della funzione è il seguente.
int gnome_init (const char *app_id, const char *app_version, int argc, char **argv); |
app_id
è un identificatore del programma che stiamo scrivendo,
utilizzato internamente da Gnome, in genere è il nome stesso del programma, mentre
app_version
è invece il numero di versione del programma. Occorre
poi fornire alla funzione argc e argv, che verranno esaminati alla ricerca di
eventuali parametri passati sulla riga di comando. Inizializzate le librerie si
può passare alla costruzione dell'interfaccia grafica del nostro programma.
Da main richiamiamo la nostra funzione create_app();
nella quale
metteremo tutto il codice che crea la finestra grafica. La prima cosa da
fare è naturalmente creare la finestra principale. Abbiamo visto nella serie
su GTK+, che la finestra principale della nostra applicazione grafica si
ottiene richiamando la funzione gtk_window_new. Le librerie di Gnome ci
mettono a disposizione una funzione analoga e un nuovo widget per la
realizzazione delle nostre applicazioni grafiche. La finestra principale
dei nostri programmi si ottiene chiamando la funzione gnome_app_new();
GtkWidget* gnome_app_new (const gchar *appname, const gchar *title); |
Il primo argomento da passare alla funzione è, generalmente, il nome del
nostro programma e deve essere lo stesso nome che è stato passato a
gnome_init();
, il secondo è il titolo che vogliamo che abbia
il programma (che apparirà sulla cornice della nostra finestra), passando
NULL
non verrà mostrato nessun titolo. La funzione ritornerà
un puntatore al widget creato.
Come detto in precedenza, utilizzando le librerie di Gnome è possibile
creare applicazioni grafiche aventi tutte un aspetto coerente. Tutte le
applicazioni di Gnome avranno una barra di menu, una toolbar, una statusbar,
decideremo poi noi se utilizzarle o meno. La barra dei menu e la toolbar
sono staccabili dalla finestra principale. Il widget che permette di ottenere
oggetti fluttuanti è GnomeDock
, e appartiene alla famiglia dei
widget contenitori. Per una applicazione "standard", avente cioè una barra di
menu e una toolbar, non è necessario creare nessun widget GnomeDoc, in quanto
sarà la funzione gnome_app_new();
a creare il GnomeDoc per noi.
Per utilizzare il widget è sufficiente recuperarne il puntatore e mostrarlo.
... dock = GNOME_APP (app)->dock; gtk_widget_show (dock); ... |
A questo punto disponiamo di un widget che ci consente di staccare, riattaccare e spostare sullo schermo menu, toolbar e tutto quello che la nostra immaginazione ci suggerisce.
Disponiamo di un GnomeDoc, ora riempiamolo, creando prima di tutto la barra dei menu. Per far ciò utilizzeremo questa funzione.
void gnome_app_create_menus (GnomeApp *app, GnomeUIInfo *uiinfo); |
Il primo argomento da passare è il puntatore alla nostra applicazione, mentre
il secondo è un puntatore alla barra dei menu. In Gnome per costruire i menu,
si ricorre ad una struttura GnomeUIInfo
. Per ogni elemento di menu
deve essere definita una struttura GnomeUIInfo
. Ecco come è definita
la struttura.
typedef struct { GnomeUIInfoType type; gchar *label; gchar *hint; gpointer moreinfo; gpointer user_data; gpointer unused_data; GnomeUIPixmapType pixmap_type; gconstpointer pixmap_info; guint accelerator_key; GdkModifierType ac_mods; GtkWidget *widget; } GnomeUIInfo; |
riempiendo i campi di questa struttura è possibile definire per ogni elemento di menu il nome da mostrare, l'icona da associare al nome, la combinazione di tasti per attivare direttamente la funzione associata, un suggerimento (tooltip), ecc.
Esaminiamo in dettaglio la struttura.
type
è un marcatore di tipo, serve ad indicare il tipo
di menu (voce di menu) che stiamo creando.
label
è il nome del menu ("Apri", "Salva", ...)
hint
è l'eventuale suggerimento che vogliamo fornire. Se l'elemento che
stiamo costruendo appartiene ad un menu a tendina, allora il suggerimento apparirà
nella status bar, altrimenti se l'elemento appartiene alla
toolbar il suggerimento apparirà come un tooltip
moreinfo
dipende dal tipo di menu che stiamo creando
user_data
è un puntatore ai dati che eventualmente vogliamo passare alla
funzione di callback che sarà richiamata alla pressione dell'elemento
unused_data
attualmente questa voce va impostata a NULL, nelle prossime
versioni di Gnome sarà utilizzata.
pixmap_type
è un valore di tipo GnomeUIPixmapType
, e specifica
il tipo di pixmap_info
pixmap_info
può contenere il nome di un file da cui ricavare l'immagine da
associare all'elemento, oppure può essere una pixmap dello Gnome Stock, o ancora una pixmap
definita inline come dati xpm, o infine, NULL
se non vogliamo immagini.
accelerator_key
è il tasto associato all'elemento, per la scorciatoia da tastiera
ac_mods
è una maschera associata al tasto scorciatoia
widget
deve essere impostato a NULL. Si occupa Gnome di riempire questo campo
nel momento in cui il menu o la toolbar sono creati.
Alcuni valori assumibili da GnomeUIInfo
GnomeUIInfoType | Descrizione | Campo moreinfo |
GNOME_APP_UI_ENDOFINFO | L'ultimo elemento della struttura GnomeUIInfo | Nessuno |
GNOME_APP_UI_ITEM | Un elemento normale, o un radio button se all'interno di un gruppo radio | La funzione di callback |
GNOME_APP_UI_TOGGLEITEM | Un toggle/check button | La funzione di callback |
GNOME_APP_UI_RADIOITEMS | Un gruppo di radio buttons | Vettore di pulsanti radio appartenenti al gruppo |
GNOME_APP_UI_SUBTREE | Sottomenu | struttura GnomeUIInfo descrivente il sottomenu |
GNOME_APP_UI_SEPARATOR | Separatore tra gli elementi di menu | Nessuno |
Vediamo come sono stati realizzati i menu nel programma di prova.
... static GnomeUIInfo file_menu_uiinfo[] = { GNOMEUIINFO_MENU_NEW_ITEM ("_Nuovo File", NULL, on_new_file_activate, "fm"), GNOMEUIINFO_MENU_OPEN_ITEM (on_open_activate, "menu file"), GNOMEUIINFO_MENU_SAVE_ITEM (on_save_activate, "fm"), GNOMEUIINFO_MENU_SAVE_AS_ITEM (on_save_as_activate, NULL), GNOMEUIINFO_SEPARATOR, GNOMEUIINFO_MENU_EXIT_ITEM (gtk_main_quit, NULL), GNOMEUIINFO_END }; ... static GnomeUIInfo menubar_uiinfo[] = { GNOMEUIINFO_MENU_FILE_TREE (file_menu_uiinfo), GNOMEUIINFO_MENU_EDIT_TREE (edit_menu_uiinfo), GNOMEUIINFO_MENU_HELP_TREE (help_menu_uiinfo), GNOMEUIINFO_END }; |
La variabile passata alla funzione gnome_app_create_menus();
è
menubar_uiinfo. In questa variabile, utilizzando alcune macro fornite da Gnome,
sono state definite le voci che appariranno sulla barra dei menu. In questo caso la
nostra barra dei menu comprenderà l'elemento "File" (GNOMEUIINFO_MENU_FILE_TREE),
l'elemento "Modifica" (GNOMEUIINFO_MENU_EDIT_TREE) e l'elemento "Aiuto"
(GNOMEUIINFO_MENU_HELP_TREE). Altre tra le macro disponibili sono:
A questo punto occorre definire quali elementi andranno inseriti in ognuno dei menu
principali. Per farlo ricorreremo ancora alla struttura GnomeUIInfo. Esaminiamo il menu
che apparirà alla pressione di "File", definito da file_menu_uiinfo[]
.
Tipicamente gli elementi contenuti in un menu File sono: Nuovo, Apri, Salva,
Salva con nome ed esci. Come per le voci di menu presenti sulla barra, anche per gli
elementi contenuti nel menu, sono disponibili alcune macro che semplificano la vita.
L'elemento di menu "Nuovo file" è creato utilizzando il seguente codice:
GNOMEUIINFO_MENU_NEW_ITEM ("_Nuovo File", NULL, on_new_file_activate, "fm") |
Il primo valore passato alla macro è la stringa che sarà visualizzata, in questo caso è "_Nuovo File", ma qual'è la funzione dell'underscore prima della lettera N? Durante la creazione del menu, Gnome esaminerà la stringa alla ricerca di underscore. Se trovati saranno eliminati, e la lettera seguente sarà utilizzata come scorciatoia, quindi in questo caso, per creare un nuovo file, ci sarà sufficiente premere i tasti ALT+N. Se avessimo posto l'underscore prima della lettera F ("Nuovo _File") allora avremmo potuto aprire un nuovo file utilizzando la combinazione di tasti ALT+F.
il valore successivo, in questo caso impostato a NULL
è una eventuale stringa da
usare come tooltip. Segue poi la funzione di callback da richiamare all'attivazione della
voce del menu, e infine gli eventuali dati da passare alla funzione di callback.
Supponiamo che questa macro non fosse stata disponibile, in questo caso avremmo dovuto costruire tutto noi riempiendo la struttura GnomeUIInfo. Vediamo come dovremmo procedere se ad esempio volessimo inserire nel menu "File" tra il separatore ed "esci", un elemento di menu per esportare i dati trattati dalla nostra applicazione.
GNOMEUIINFO_SEPARATOR, { GNOME_APP_UI_ITEM, /* il tipo di elemento */ "_Export", /* la stringa da mostrare */ NULL, /* il tooltip */ on_export_activate, /* la funzione callback da richiamare */ NULL, /* dati da passare alla funzione */ NULL, /* non usato */ GNOME_APP_PIXMAP_NONE, /* il tipo di pixmap da usare */ NULL, /* nessuna pixmap (GNOME_APP_PIXMAP_NONE) */ GDK_e, /* tasto scorciatoia */ GDK_MOD1_MASK, /* maschera per il tasto scorciatoia */ NULL /* impostare a NULL, usato da Gnome */ }, GNOMEUIINFO_MENU_EXIT_ITEM (gtk_main_quit, NULL), |
Nulla di difficile ;-) vero?, ma se ripetuto per decine di volte potrebbe risultare noioso, e allora, avanti con le macro!!.
Per costruire la toolbar, si può procedere nello stesso modo. Si definisce una variabile di tipo GnomeUIInfo contenente tutte le voci che ci servono e si chiama poi la seguente funzione.
void gnome_app_create_toolbar (GnomeApp *app, GnomeUIInfo *uiinfo); |
Se si hanno delle esigenze particolari, o si vuole un maggior controllo allora si può procedere come nel programma di esempio.
... toolbar = gtk_toolbar_new (GTK_ORIENTATION_HORIZONTAL, GTK_TOOLBAR_BOTH); gtk_object_set_data(GTK_OBJECT (app), "toolbar", toolbar); gtk_widget_show (toolbar); gnome_app_add_toolbar (GNOME_APP (app), GTK_TOOLBAR (toolbar), "toolbar", GNOME_DOCK_ITEM_BEH_EXCLUSIVE, GNOME_DOCK_TOP, 1, 0, 0); gtk_container_set_border_width (GTK_CONTAINER (toolbar), 1); gtk_toolbar_set_space_size (GTK_TOOLBAR (toolbar), 20); gtk_toolbar_set_space_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_SPACE_LINE); gtk_toolbar_set_button_relief (GTK_TOOLBAR (toolbar), GTK_RELIEF_NONE); tmp_toolbar_icon = gnome_stock_pixmap_widget (app, GNOME_STOCK_PIXMAP_NEW); nuovo = gtk_toolbar_append_element (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_CHILD_BUTTON, NULL, "Nuovo", "Nuovo file", NULL, tmp_toolbar_icon, on_new_file_activate, "tb"); gtk_widget_show (nuovo); ... |
Sono state eseguite poche operazioni. E' stata creata una toolbar con determinate caratteristiche ed è stata inserita nello GnomeDock. Quindi sono stati inseriti nella toolbar una serie di elementi. Vediamo in dettaglio.
toolbar = gtk_toolbar_new (GTK_ORIENTATION_HORIZONTAL, GTK_TOOLBAR_BOTH); gtk_object_set_data(GTK_OBJECT (app), "toolbar", toolbar); gtk_widget_show (toolbar); |
GtkWidget* gtk_toolbar_new (GtkOrientation orientation, GtkToolbarStyle style); void gtk_object_set_data (GtkObject *object, const gchar *key, gpointer data); gpointer gtk_object_get_data (GtkObject *object, const gchar *key); |
gtk_toolbar_new ();
crea un oggetto toolbar e restituisce il puntatore al
widget creato. La funzione accetta due parametri; il primo indica l'orientazione della
toolbar e può assumere uno fra i seguenti valori:
Con gtk_object_set_data()
è possibile associare ad un oggetto una
coppia chiave-dati. Nel nostro caso la chiave "toolbar" e il puntatore all'oggetto
toolbar sono stati salvati nell'oggetto app. In seguito utilizzando la funzione
gtk_object_get_data();
ed avendo a disposizione il puntatore all'oggetto
app, sarà possibile recuperare tramite la chiave "toolbar" il puntatore alla toolbar.
gnome_app_add_toolbar (GNOME_APP (app), GTK_TOOLBAR (toolbar), "toolbar", GNOME_DOCK_ITEM_BEH_EXCLUSIVE, GNOME_DOCK_TOP, 1, 0, 0); |
void gnome_app_add_toolbar (GnomeApp *app, GtkToolbar *toolbar, const gchar *name, GnomeDockItemBehavior behavior, GnomeDockPlacement placement, gint band_num, gint band_position, gint offset); |
gnome_app_add_toolbar()
crea un nuovo widget GnomeDockItem contenente la toolbar,
e lo aggiunge al dock della nostra applicazione
Alla funzione bisogna passare il puntatore alla finestra principale dell'applicazione, il puntatore alla toolbar che deve essere inserita nel nuovo dock, ed un nome per l'elemento dock che conterrà la toolbar. I successivi valori sono relativi agli attributi che dovrà possedere il nuovo dock. GnomeDockItemBehavior definisce il comportamento del dock e può assumere uno dei seguenti valori:
GnomeDockPlacement definisce la posizione in cui inserire il nuovo dock, e può essere:
Gli ultimi parametri da fissare sono relativi a
band_num
che è il numero della "striscia" in cui inserire il nuovo dock.
La striscia, il contenitore, che contiene la barra dei menu ha numero zero, il
successivo contenitore, che conterrà la toolbar avrà numero 1, e cosi via.
band_position
indica la posizione che il dock deve avere nella striscia
numero band_num. offset
offset dal dock precedente. Se volessimo inserire
un nuovo dock contenente un'altra toolbar subito dopo la prima, allora band_num sarebbe
impostata ad 1, band_position sarebbe 1, e infine offset indicherebbe di quanto i due
dock si dovrebbero distanziare.
... gtk_toolbar_set_space_size (GTK_TOOLBAR (toolbar), 20); gtk_toolbar_set_space_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_SPACE_LINE); gtk_toolbar_set_button_relief (GTK_TOOLBAR (toolbar), GTK_RELIEF_NONE); ... |
void gtk_toolbar_set_space_size (GtkToolbar *toolbar, gint space_size); void gtk_toolbar_set_space_style (GtkToolbar *toolbar, GtkToolbarSpaceStyle space_style); void gtk_toolbar_set_button_relief (GtkToolbar *toolbar, GtkReliefStyle relief); GtkWidget* gtk_toolbar_append_element (GtkToolbar *toolbar, GtkToolbarChildType type, GtkWidget *widget, const char *text, const char *tooltip_text, const char *tooltip_private_text, GtkWidget *icon, GtkSignalFunc callback, gpointer user_data); |
A questo punto disponiamo di una toolbar inserita in un dock, dobbiamo soltanto decidere che proprietà debba avere la toolbar e poi inserire le icone.
gtk_toolbar_set_space_size();
è utilizzata per definire quanti pixel
devono essere grandi gli elementi separatori. Cioè, due icone sulla toolbar possono
essere separati da uno spazio vuoto o da una barra verticale; con questa funzione
si stabilisce quanto spazio devono occupare questi separatori. Accetta due argomenti,
il puntatore alla toolbar ed un intero che indica i pixel da usare.
gtk_toolbar_set_space_style ();
definisce lo stile dei separatori (spazio
vuoto o barra verticale). Il primo argomento è il puntatore alla toolbar, il secondo
indica lo stile e può assumere uno tra i due seguenti valori.
gtk_toolbar_set_button_relief();
definisce l'aspetto esteriore delle icone,
cioè come devono apparire i bottoni sulla toolbar. Il primo argomento è ancora il
puntatore alla toolbar, il secondo è lo stile del bottone e puo valere:
(nota: GTK+ Reference Manual non documenta i possibili valori che può assumere
GtkReliefStyle, da prove effettuate, io non ho riscontrato differenze tra
GTK_RELIEF_NORMAL e GTK_RELIEF_HALF).
tmp_toolbar_icon = gnome_stock_pixmap_widget (app, GNOME_STOCK_PIXMAP_NEW); nuovo = gtk_toolbar_append_element (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_CHILD_BUTTON, NULL, "Nuovo", "Nuovo file", NULL, tmp_toolbar_icon, on_new_file_activate, "tb"); |
GtkWidget* gnome_stock_pixmap_widget (GtkWidget *window, const char *icon); |
gnome_stock_pixmap_widget();
Per creare un'icona ricorriamo a questa
funzione, che ha come primo argomento un puntatore alla finestra principale
dell'applicazione, come secondo accetta una stringa e ritorna un puntatore al widget
contenente l'icona. Questa funzione è utilizzata per recuperare una immagine dal set
di icone predefinite di Gnome; la stringa da passare è un identificatore dell'icona.
Ecco, in un brevissimo elenco, come sono definite le icone predefinite di Gnome:
#define GNOME_STOCK_PIXMAP_NEW "New" #define GNOME_STOCK_PIXMAP_OPEN "Open" #define GNOME_STOCK_PIXMAP_CLOSE "Close" ... #define GNOME_STOCK_PIXMAP_UNDO "Undo" #define GNOME_STOCK_PIXMAP_REDO "Redo" ...
Ok, abbiamo la toolbar, e le icone, ora creiamo il bottone che inseriremo sulla
toolbar e sul quale metteremo la nostra immagine. Per far ciò ricorreremo alla
funzione gtk_toolbar_append_element();
che inserisce un nuovo
elemento alla fine della toolbar. Il primo parametro da passare è il puntatore
alla toolbar, il secondo è un valore di tipo GtkToolbarChildType
;
definisce il tipo del widget che stiamo inserendo sulla toolbar e può assumere uno tra
il terzo parametro può essere impostato a NULL
, il quarto è la
stringa che apparirà sulla toolbar (unitamente all'icona o meno), il quinto è
il tooltip che sarà mostrato nel caso in cui il puntatore del mouse stazionerà
sull'elemento, il sesto può essere impostato a NULL
, a meno che
non vogliate sviluppare un sistema di help "context-sensitive" (una ulteriore
e più approfondita descrizione del widget), il settimo è il widget che ci è
stato restituito dalla precedente funzione gnome_stock_pixmap_widget
e che rappresenta l'icona che vogliamo che appaia sul bottone, infine ottavo e
nono parametro rappresentano la funzione di callback da richiamare alla pressione
del bottone e gli eventuali dati da passare alla callback. La funzione ci ritornerà
un puntatore al nuovo elemento inserito.
GtkWidget* gtk_toolbar_prepend_element (GtkToolbar *toolbar, GtkToolbarChildType type, GtkWidget *widget, const char *text, const char *tooltip_text, const char *tooltip_private_text, GtkWidget *icon, GtkSignalFunc callback, gpointer user_data); GtkWidget* gtk_toolbar_insert_element (GtkToolbar *toolbar, GtkToolbarChildType type, GtkWidget *widget, const char *text, const char *tooltip_text, const char *tooltip_private_text, GtkWidget *icon, GtkSignalFunc callback, gpointer user_data, gint position); void gtk_toolbar_set_orientation (GtkToolbar *toolbar, GtkOrientation orientation); void gtk_toolbar_set_style (GtkToolbar *toolbar, GtkToolbarStyle style); |
In alto sono elencate alcune tra le altre funzioni che operano sulle toolbar,
gtk_toolbar_prepend_element();
e gtk_toolbar_insert_element();
come gtk_toolbar_append_element();
inseriscono un elemento sulla
toolbar, ma mentre la prima di queste inserisce il widget all'inizio,
la seconda lo inserisce in una generica posizione, individuata dall'intero
position
. La toolbar può apparire in orizzontale o in vericale, per
impostare il suo orientamento ricorreremo a gtk_toolbar_set_orientation();
a cui passeremo il puntatore alla toolbar e il tipo di
orientamento desiderato
Per terminare con le toolbar osserviamo quest'ultima funzione:
gtk_toolbar_set_style();
. Questa funzione ci permette di scegliere come
devono essere mostrati gli oggetti inseriti sulla toolbar. In precedenza abbiamo creato
e inserito sulla barra dei bottoni contenenti una icona e una stringa di testo. Potremmo
dare la possibilità all'utente di decidere cosa visualizzare sulla barra, solo le icone,
solo la stringa di testo o entrambe. Per far ciò ricorreremo a
gtk_toolbar_set_style();
. Accetta come primo argomento il solito puntatore
alla toolbar, mentre il secondo deve avere uno tra questi valori.
L'ultimo widget che dovrebbe essere presente di default in tutte le applicazioni
grafiche di Gnome è la barra di stato. La barra di stato di Gnome si presenta
divisa in due parti. Quella di sinistra è utilizzata per mostrare messaggi di
aiuto, di default vengono indirizzati al quasta zona i suggerimenti associati
alle voci di menu. La parte di destra è, invece, una progressbar. Per il futuro
è previsto il supporto di un minibuffer simile a quello presente in Emacs. Come
sappiamo, come per tutti gli altri widget, per utilizzare la GnomeAppBar occorre
crearla richiamando la opportuna funzione ..._new();
:
... /* creiamo una AppBar (comprende una statusbar, e una progressbar) */ appbar = gnome_appbar_new (TRUE, TRUE, GNOME_PREFERENCES_NEVER); gnome_app_set_statusbar (GNOME_APP (app), appbar); gtk_widget_show (appbar); ... |
GtkWidget* gnome_appbar_new (gboolean has_progress, gboolean has_status, GnomePreferencesType interactivity); void gnome_appbar_set_default (GnomeAppBar *appbar, const gchar *default_status); void gnome_appbar_push (GnomeAppBar *appbar, const gchar *status); void gnome_appbar_pop (GnomeAppBar *appbar); void gnome_appbar_clear_stack (GnomeAppBar *appbar); void gnome_appbar_set_progress (GnomeAppBar *appbar, gfloat percentage); GtkProgress* gnome_appbar_get_progress (GnomeAppBar *appbar); |
gnome_appbar_new();
accetta tre parametri e restituisce un puntatore
al widget creato. Il primo ed il secondo argomento sono dei booleani. Se
has_progress
è impostato a TRUE
allora sarà presente la
progressbar, se è settato a FALSE
, non avremo progress bar. Analogamente
con has_status
impostato a TRUE
la appbar presenterà
la status bar. L'ultimo parametro GnomePreferencesType interactivity
serve a stabilire se la appbar avrà anche funzioni di minibuffer (alla EMACS) o sarà una
semplice status bar, e può assumere uno tra i seguenti valori:
Per il momento tuttavia non è possibile utilizzare le caratteristiche di minibuffer; ciò dipende dal fatto che è basato sul widget GtkEntry, che non ha una grande flessibilità.
gnome_appbar_set_default();
accetta un puntatore alla appbar e
una stringa. Lo scopo di questa funzione è quello di mostrare per default la
stringa che abbiamo impostato. Se non bisogna mostrare nient'altro (nei momenti
vuoti) viene visualizzata la nostra stringa.
La status bar è strutturata a stack, cioè verrà sempre mostrata la stringa
che si trova in cima alla pila. Storicamente le funzioni che operano sugli
stack sono due, push e pop, la prima inserisce un elemento sullo stack, la
seconda lo estrae. Anche per la GnomeAppBar sono presenti due funzioni di
questo tipo: gnome_appbar_push();
e gnome_appbar_pop();
.
La prima funzione vuole come primo argomento il puntatore alla appbar e come secondo
la stringa da inserire sulla pila, ed ha, per l'appunto, il compito di inserire
sullo stack la stringa passata, che sarà quindi visualizzata. La seconda, al
contrario, accetta il puntatore alla appbar ed ha il compito di eliminare
l'elemento che si trova in cima allo stack. Quindi verrà cancellata l'ultima
stringa inserita e sarà mostrata la penultima stringa "pushata" ;-).
Con gnome_appbar_clear_stack ();
si pulisce lo stack da tutte le stringhe
presenti; se si è impostata una stringa di default, questa sarà mostrata, altrimenti
vedremo la barra vuota. Il parametro che dobbiamo passare alla funzione è il puntatore
alla appbar.
gnome_appbar_set_progress();
ha in ingresso il puntatore alla appbar
e un gfloat
, cioè un numero reale, e serve ad impostare la progress bar
al valore desiderato. Si potrebbe scrivere una routine che incrementa il valore
della barra mentre la nostra applicazione svolge una certa applicazione, mostrando
in questo modo l'avanzamento dello stato. Tuttavia sconsiglio l'uso di questa funzione,
in quanto nella documentazione si ipotizza una sua possibile "morte".
Infine
gnome_appbar_get_progress();
accetta il puntatore alla appbar e restituisce
un puntatore al widget GtkProgress. La progressbar potrà, quindi, essere manipolata con
le funzioni messe a disposizione da GTK+. Per ritornare alla nostra ipotetica routine,
per realizzarla è sufficiente recuperare il puntatore alla progressbar e poi manipolarla
con le funzioni disposnibili in GTK+
Una delle innovazioni introdotte da Gnome e riprese dalle nuove librerie di GTK+ riguarda le finestre di dialogo. Spesso il programmatore ha la necessità di comunicare con l'utente, per fargli scegliere una certa opzione, o per comunicargli lo stato della applicazione o ancora per chiedere di confermare o meno l'esecuzione di una operazione delicata (ad esempio la cancellazione o la sovrascrittura di un file). Con GTK+ ogni programmatore avrebbe creato la propria finestra, avrebbe inserito una stringa con cui spiegava la presenza della finestra e avrebbe inserito uno o più bottoni in funzione della operazione da svolgere. Magari i più fantasiosi e meno pigri avrebbero inserito una icona a sinistra della stringa e qualche separatore tra stringa e bottoni. Ad ogni modo ogni applicazione avrebbe avuto le proprie finestre di dialogo, tutte diverse tra loro.
Gnome ci mette a disposizione alcune funzioni che creano delle finestre standard per comunicare una situazione di errore, o di pericolo, o per confermare l'avvenuta conclusione di una certa operazione.
figura 5 comunichiamo qualcosa |
figura 6 segnaliamo un'anomalia |
void display_message (gchar *str) { GtkWidget *messagebox; GtkWidget *button; ... messagebox = gnome_message_box_new (str, GNOME_MESSAGE_BOX_INFO, GNOME_STOCK_BUTTON_OK, NULL); } ... messagebox = gnome_message_box_new ("Attenzione questa opzione non è ancora disponibile", GNOME_MESSAGE_BOX_WARNING, GNOME_STOCK_BUTTON_OK, NULL); ... gnome_about_new (PACKAGE, VERSION, "Copyleft (c) 2002 Pluto Journal", authors, "Inseriamo qui una breve descrizione del nostro programma....", NULL); ... |
GtkWidget* gnome_ok_dialog (const gchar *message); GtkWidget* gnome_error_dialog (const gchar *error); GtkWidget* gnome_warning_dialog (const gchar *warning); GtkWidget* gnome_question_dialog (const gchar *question, GnomeReplyCallback callback, gpointer data); GtkWidget* gnome_ok_cancel_dialog (const gchar *message, GnomeReplyCallback callback, gpointer data); GtkWidget* gnome_about_new (const gchar *title, const gchar *version, const gchar *copyright, const gchar **authors, const gchar *comments, const gchar *logo); |
gnome_ok_dialog();
(figura 5),
gnome_warning_dialog();
(figura 6), e
gnome_error_dialog();
(figura 7),
accettano tutti una stringa e creano rispettivamente una generica finestra di
informazioni, una finestra di warning ed una di errore. Tutte ritornano il puntatore
alla finestra creata.
Abbiamo visto tre finestre molto semplici, ma che certamente non sono
sufficienti a coprire tutte le possibili esigenze. Con
gnome_question_dialog ();
è possibile creare una finestra
di dialogo, dotata di due bottoni ("Si" e "No"); essa accetta come primo
argomento la stringa da visualizzare, come secondo un puntatore una
funzione di callback che sarà richiamata alla pressione di uno dei due
bottoni, e come terzo un puntatore agli eventuali dati da passare alla
funzione.
Quando si clicca su uno dei due bottoni, alla callback viene passato implicitamente anche il numero associato al bottone; se si clicca su "Si" viene passato il numero 0, se si clicca su "No" viene passato 1. In questo modo all'interno della callback è possibile indirizzare l'esecuzione del programma in funzione del bottone cliccato. Ad esmpio, pensiamo ad una finestra di dialogo che ci chieda "Sei sicuro di voler uscire?", se nella callback riceviamo lo zero, allora è stato cliccato "si" ed usciamo dal programma, se riceviamo 1 allora è stato cliccato su "no" e continuiamo l'esecuzione. La funzione ritorna un puntatore alla finestra.
gnome_ok_cancel_dialog ();
ha un funzionamento analogo a
gnome_question_dialog ();
, la differenza è nel fatto che
i bottoni associati alla finestra avranno etichette "Ok" e "Cancel",
ognuno con le rispettive icone.
Per ognuna di queste funzioni ne esiste una simile, che si differenzia
dalla omologa per la variante *_parented();
. La funzione
*_parented();
accetta gli stessi parametri della funzione
omologa con in più il puntatore alla finestra che è "logicamente"
genitore della finestra di dialogo.
L'ultima funzione che esamineremo in questo articolo appartiene sempre
alla classe delle finestre di dialogo, ma ha una funzione speciale. E'
la finestra che conterrà le note di copyright (o copyleft ;)) della nostra
applicazione, gnome_about_new();
.
I parametri da passare sono il titolo e il numero di versione della nostra
applicazione; ancora una volta è una buona idea passare gli stessi valori
dati a gnome_init()
, quindi occorre passare la stringa con le
note di copyright, gli autori del programma (passati come vettore di
stringhe terminato da NULL
), infine una stringa con una breve
descrizione del programma ed eventualmente una stringa contenente il nome di
un file in cui è contenuta una immagine da utilizzare come logo.
Bene, anche in questo numero del Pluto Journal abbiamo approfondito le nostre conoscenze sulle librerie di Gnome e GTK+. L'appuntamento è al prossimo numero.
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 - Archivio Generale - Copertina - PW: Bash -> |