Questo capitolo contiene le domande che vengono poste più frequentemente e le
loro risposte, come riferimenti all'interno del libro. Si consulti l'indice
Chiamando la funzione gdk_beep().
Normalmente g_malloc() e
g_free() sono semplici wrappers costruiti su
malloc() e free(), alle quali
aggiungono alcune funzionalità extra descritte nel
la sezione Memoria nel il capitolo glib: portabilità e funzioni di utilità generale
. Ad ogni modo, quando èattivato il profiling della
memoria, queste non sono più intercambiabili con
malloc() e free(), per cui ogni
volta che si scansiona le funzioni in modo scorretto il programma andrà in
crash.
Se state utilizzando la libreria C GNU, che viene distribuita in tutte le
distribuzioni Linux, si può utilizzare una sua particolare caratteristica
che permette di eseguire il debug di questo genere di errore. Si imposti la variabile di ambiente MALLOC_CHECK_ a 2 prima di avviare il programma, quindi avviate gdb. Non appena una chiamata di free() riceve un puntatore che non è stato creato da malloc() verrà chiamata abort().
Si sta molto probabilmente combattendo una battaglia persa in partenza. I widget non sono ciò di cui avete bisogno, in questo caso. Considerate piuttosto l'uso di GtkDrawingArea oppure del GnomeCanvas per creare visualizzazioni personalizzate.
Se avete bisogno invece di widget interattivi, come GtkEntry oppure GtkButton, potete, in questo caso, provare ad utilizzare GtkLayout oppure GtkFixed.
Se si devono soddisfare esigenze molto specilistiche, si deve probabilmente
scrivere un vostro widget personale da capo. Il
il capitolo Scrivere un GtkWidget
descrive questo tipo di procedura.
glib non chiama malloc() ogni volta che necessita di un nuovo nodo in una struttura di dati. Se lo facesse, la creazione (ad esempio) di liste linkate diventerebbe sostanzialmente più lenta. Invece, glib memorizza blocchi di memoria di stesse dimensioni per utilizzarli in queste strutture di dati. Dato che questi blocchi sono disponibili per il riciclaggio una volta che il programma termina, non vengono mai passati a free() (Ovviamente il sistema operativo richiederà la memoria, ma strumenti come ccmalloc e Purify li mostreranno come dei leak.)
Per evitare questo, si può inserire un nuovo GAllocator all'interno della maggior parte delle
strutture di dati. Un GAllocator è un
blocco di memoria come precedentemente descritto. Createlo manualmente, in
modo da avere un puntatore ad esso. In questo modo potrete successivamente
passarlo a free() una volta terminata
l'operazione. In
Figura 1
sono elencate le funzioni di rilievo per GList. Una piccola scorsa in glib.h rivelerà le funzioni corrispondenti per altre strutture di dati.
L'argomento name di g_allocator_new() viene utilizzato per i messaggi di debug. L'argomento n_preallocs viene passato a g_mem_chunk_new().
Questi messaggi sono originati dalla chiamata a g_return_if_fail(), presente all'inizio di molte
funzioni di GTK+. (Appariranno unicamante se la copia di GTK+ è stata
compilata con i simboli di debug attivi - deve esserlo se si sta
sviluppando una applicazione!). Si guardi l'esatta asserzione che è
fallita per capire cosa ha causato il messaggio di errore. Una molto
comune: se accidentalmente si acceda da un widget o un oggetto ormai
distrutto, avrete un puntatore verso spazzatura. Fra le altre cose, questo
significa che il tipo di tag non sarà valido, quindi i controlli di tipo eseguiti a runtime di GTK+ falliranno.
Per motivi soprattutto storici. Qualche volta però esiste una ragione valida. Ad esempio, GTK+ non include gdk_imlib e di conseguenza nessun widget che lo utilizza. In generale, GTK+ impone meno "regole" rispetto a Gnome. Alcuni widget di Gnome sono deliberatamente poco flessibili per evitare che gli sviluppatori creino una interfaccia non coerente. GTK+ non segue questa politica. Infine, alcuni widget di Gnome sono considerati troppo sperimentali per essere inclusi in GTK+. In ogni caso, widget di base di Gnome descritti in questi libro non appartengono a questa categoria.
Se la finestra è un GnomeDialog, la posizione è configurabile da parte dell'utente e non dovete eseguire questa operazione. Nella maggior parte dei casi risulta comunque una operazione piuttosto strana, ma esistono delle eccezioni, come le schermate di avvio. La funzione che cercate è gtk_window_set_position(). Potete decidere se lasciare al gestore di finestre il posizionamento di questa (default), centrarla oppure mostrarla nella posizione in cui si trova il puntatore del mouse. Esiste una enumerazione che corrisponde a queste impostazioni: GTK_WIN_POS_NONE, GTK_WIN_POS_CENTER, GTK_WIN_POS_MOUSE. Ad esempio:
|
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
|
Dovete eseguire questa operazione prima di chiamare gtk_widget_show(), poiché la funzione agisce sulla posizione che avrà la finestra quando apparirà la prima volta sullo schermo.
No. Quando gli sviluppatori pongono questa domanda solitamente sono alla ricerca di una interfaccia di astrazione che disegni sullo schermo oppure su una stampante. Questo non ha niente a che vedere con GTK+ al momento. GnomeCanvas avrà molto probabilmente una caratteristica come questa nelle versioni future.
È disponibile la libreria gnome-print, che gestisce alcuni dettagli molto noiosi di basso livello per la gestione dei caratteri e di PostScript. Questa fornisce anche un dialogo per la scelta della stampante.
Si ricordi sempre due cose:
-
Il processo figlio non deve neanche tentare di utilizzare la GUI. Dato che questo condivide i descrittori del file con il processo padre, inclusa la connessione di GTK+ al server X, GTK+ diventerà molto confuso.
-
Il processo figlio deve essere terminato con _exit() piuttosto che con exit(). Chiamando exit() verrà chiuso GTK+ danneggiando il processo padre. (GTK+ registra una funzione di "cleanup" utilizzando atexit()).
la sezione Realizzare, mappare e visualizzare nel il capitolo Fondamenti di GTK+
fornisce alcuni dettagli a riguardo. Ma ecco un breve sommario.
Mostrare un widget implica eventualmente la sua mappatura (per essere precisi, il widget viene messo in coda per essere mappato quando il widget padre viene mappato). Mappare un widget significa chiamare gdk_window_show() per mostrare la GdkWindow del widget sullo schermo (se ha una GdkWindow, alcuni widget non la posseggono). Per mappare un widget dovete prima realizzarlo. Di conseguenza visualizzare un widget implica la sua realizzazione.
Esiste una eccezione, ovviamente. Realizzare un widget
significa allocare risorse sul server X per questo, la maggior parte delle
volte si tratta di una GdkWindow. Alcune
operazioni che si potranno fare possono richiedere l'esistenza della
GdkWindow, quindi si potrà il widget a realizzarsi immediatamente. gtk_widget_realize() serve proprio a questo. Dato che i widget padre devono essere realizzati prima dei widget figlio, gtk_widget_realize() realizzerà immediamente anche tutti i padri. Uno di questi deve essere una finestra toplevel, altrimenti la realizzazione fallirà.
Se forzate la realizzazione di un widget, dovete comunque chiamare gtk_widget_show() dato che la realizzazione non esegue la mappatura del widget.
Un errore comune: se state utilizzando GTK_WIDGET(widget)->window, widget deve essere realizzato.
Comunque sia, dobbiamo far notare che forzare la realizzazione di un widget è sempre un'idea poco buono. È una funzione inefficienti e scomoda di basso livello. Nella maggior parte dei casi potete lavorare in modo da evitare questo tipo di chiamata.
Creare una pixmap richiede una colormap. gdk_pixmap_create_from_xpm_d() richiede una GdkWindow come argomento per poter estrarre una colormap. Molto probabilmente state tentando di utilizzare la window di un widget non ancora realizzato, che equivale a NULL. Potete provare ad utilizzare una nuova funzione, gdk_pixmap_colormap_create_from_xpm_d(), che accetta una colormap come argomento. Se passate una colormap, il suo argomento window può essere uguale a NULL. Utilizzare Imlib invece risulta sempre essere la soluzione migliore. Le funzioni per le pixmap di Imlib sono comunque più veloci.
Per una varietà di ragioni, l'interfaccia grafica di una applicazione tende ad essere eccezionalmente volatile, a cambiare aspetto molto velocemente. È molto difficile pianificare e eseguire tutto la prima volta, scoprirete andando avanti che alcuni aspetti della vostra applicazione saranno difficili da gestire unicamente una volta scritti. Rendendo le cose più difficili, le interfaccie grafiche non sono portabili tra la varietà di macchine esistenti. Gnome funziona sotto X Window, ma se la vostra applicazione è molto utile, non passerà molto tempo prima che qualcuno la voglia portare su un altro tipo di sistema, oppure avere una versione a linea di comando, oppure una interfaccia web. Potrete anche avere due interfaccie nella stessa versione, una grafica e uno per un linguaggio di scripting, come ad esempio Guile.
In termini pratici, questo significa che grandi applicazioni devono essere
radicalmente divise fra i vari frontends o
interfaccie, e il backend. Il backend dovrebbe
contenere le parti "difficili": gli algoritmi e le strutture di dati, il
vero lavoro che viene svolto dalla applicazione. Si pensi a questo come ad un modello astratto che viene visualizzato e manipolato dall'utente.
Ciascun frontend dovrebbe essere un "visore" e un "controllore". Come "visore", il frontend deve rilevare qualunque cambiamento nel backend, e modificare di conseguenza la visualizzazione. Come "controllore" invece deve permettere all'utente di richiedere modifiche al backend.
Esistono svariati modi per disciplinarsi nel tenere le proprie applicazioni separate. Ecco alcune idee utili:
-
Si scriva il backend sotto forma di libreria. Se questo diventa un aspetto non desiderato, per qualunque ragione, potete sempre eseguire un link statico.
-
Si scriva almeno due frontend fin dall'inizio. Uno o entrambi posso essere orrendi, dovete soltanto avere una idea di come strutturare il backend. Ricordate, i frontend devono essere semplici, è il backend che deve essere complicato.
Se uno dei frontend è basato su Gnome o GTK+, una scelta eccellente è quella di
scrivere un terminale Guile interattivo. Gli utenti finali inesperti non ne
faranno mai uso, ma è uno stupendo strumento di debug. Si possono scrivere i
prototipi ed eseguire i test sul backend utilizzando dei bindings per Guile,
molto facili da scrivere, e aggiungere l'interfaccia grafica di controllo
solo quando si raggiunge la certezza che tutto funzioni a dovere. Una volta
terminato, si otterrá una applicazione raggiungibile via script, con il minimo sforzo possibile.
Se l'applicazione può potenzialmente essere avviata in modalità batch, una
versione a linea di comando o con interfaccia web sono, rispettivamente,
molto facili da scrivere, comode per il debug. Sarà d'aiuto nella
disciplina.
Infine, se il progetto è abbastanza grande da giustificare una complessità
rilevante, si considererá l'utilizzo di un frontend multipiattaforma per
condividere il codice fra i diversi frontend grafici. Questo approccio viene
utilizzato da Mozilla (
http://www.mozilla.org
) e
dalla suite per ufficio AbiSource (
http://www. abisource.com
). È sicuramente istruttivo e interessante andare a vedere il loro codice sorgente.
Non si scriva il codice delle vostre preferenze. GTK+
sfortunatamente permette allo sviluppatore di modificare non poche
impostazioni sull'aspetto dei widget. Ad esempio, si puó cambiare
l'apparenza del nodi di espansione di un GtkCTree: possono essere triangoli, quadrati o
cerchi. Per default sono dei quadrati. Si puó cambiare questa impostazione chiamando gtk_ctree_set_expander_style().
Non esiste una ragione valida per chiamare questa funzione all'interno di una
applicazione. Mai. Si pensi un attimo al perché è
stata chiamata: perché si preferisce quel tipo di nodo di espansione piuttosto che quello di default. È una questione puramente estetica. Se la chiamate, renderete la vostra applicazione diversa nell'aspetto da tutte le altre applicazioni esistenti. Questo è pericoloso, poiché confonde l'utente e fa credere che la vostra applicazione non sia "professionale" oppure "scritta in modo corretto".
"Miiii ma io voglio il mio nodo preferito!" reclamerete voi. Non disperatevi. Esiste un modo corretto per gestire questa situazione. Gli aspetti variabili dell'applicazioni devono essere configurabili a runtime dagli utenti. In più, deve essere configurabilie globalmente, per tutte le applicazioni. GTK+ fornisce i temi esattamente per questo scopo.
Sfortunatamente i temi non coprono tutti gli aspetti che riguardano l'uso e l'aspetto dell'interfaccia e quindi rimane la tentazione di forzare queste modifiche all'interno della propria applicazione. Dovete resistere. Se volete veramente combattere contro gli aspetti che non sono di vostra preferenza, potete sempre lavorare per rendere questi modificabili a livello di libreria, e inviare il codice agli sviluppatori di GTK+ o Gnome.
Dovete eseguire questa operazione a livello di libreria. Pensateci bene. Se fornite un modo modo particolare all'interno della vostra applicazione per configurare l'aspetto di questa, non avrete guadagnato niente in realtà.
Gnome fornisce già una soluzione a molti di questi problemi. Ad esempio, GTK+ vi permette di scegliere se un dialogo apparirà nella posizione del cursore, al centro dello schermo oppure a scelta del gestore di finestre. Invece GnomeDialog utilizza le preferenze dell'utente per posizione il dialogo. Questa preferenze può essere impostata dal Centro di Controllo di Gnome.
Le stringhe sono carine. Sono facili da digitare e evitano terribili mal di testa per gli autori di GtkObject. Digitando in modo scorretto una stringa si otterrà un errore a runtime, le macro non migliorano certo il controllo d'errore. Infine, le stringhe sono convertite internamente in un identificativo numerico, quindi l'efficienza non viene mai meno.
Considerate cosa significhi mantenere questo codice e il mal di testa che può arrecare se utilizzassimo delle enumerazioni: sia i valori e i nomi di queste enumerazioni dovrebbero essere uniche all'interno di GTK+, Gnome, e estensioni fornite da terzi. Un incubo.
Prima di tutto: porre questa domanda in una mailing list pubblica è fortemente sconsigliato. Non fatelo. Controllate gli archivi per trovare questo tipo di informazioni, sono già state discusse a lungo in precedenza.
Ecco alcune ragioni:
-
Gli autori originali scelsero di scriverlo in C, e adesso molte applicazioni in C sono basate su GTK+. Gli autori correnti sono entusiasti di C.
-
GTK+ gestisce tipi e oggetti con maggiore flessibilità rispetto a C++; è orientato al runtime, molto simile in questo a Java o Objective C piuttosto che a C++. Questo risulta conveniente per la creazione di GUI o per i linguaggi di estensione.
-
C è una lingua franca all'interno dello sviluppo di UNIX. La maggior parte delle persone sanno scrivere codice in C.
-
Esistono toolkit per linguaggi come Java e Objective C. Esistono dei wrapper C++ per GTK+. Sono molti, infatti.
-
C è più portabile di C++. il C++ ANSI non è ancora largamente implementato, quindi può essere utilizzato solo un parte di questo standard.
-
Quando iniziò lo sviluppo di GTK+, non esisteva ancora un compilatore C++ libero e funzionante.
Ancora: non ponete questa domanda su nessuna mailing list, poiché molte persone si sono stancate dell'argomento e non si rallegrano certo a sentirne ancora parlare.
Esiste una funzione di Xlib chiamata XWarpPointer() che effettua questa operazione, ma GDK non la include. È sicuramente una cattiva idea utilizzare questa chiamata (è stata progettata solo per i gestori di finestre). Scrivete a una delle mailing list di GTK+ o Gnome chiedendo un altro modo per raggiungere il vostro obbiettivo. A ogni modo, potete sempre utilizzare le funzioni di Xlib (come, ad esempio, XWarpPointer()) includendo i file gdk/gdkx.h e gdk/gdkprivate.h, quindi manipolare la parte privata delle strutture di dati di GDK. Se suona molto strana questa operazione, sicuramente lo è.
Prima di tutto: ricordate che una pixmap è una risorsa lato server, quindi è possibile che questa passi attraverso una rete ma sicuramente attraverso un qualche tipo di socket. Ad ogni modo, non dovete richiedere i suoi pixel uno alla volta. Iterare attraverso una pixmap in questo modo può richiedere svariati secondi.
GDK fornisce un wrapper di un oggetto Xlib, XImage, chiamata GdkImage. Una GdkImage è essenzialmente una copia locale dei dati di una pixmap. Potete copiare una regione di una pixmap o di una window all'interno di un GdkImage attraverso una chiamata a gdk_image_get(), quindi ottenere e impostare i pixel chiamando gdk_image_get_pixel() e gdk_image_put_pixel(). È possibile inoltre accedere la struttura di dati dell'immagine direttamente, ma questo è abbastanza complicato (a causa dei visual, depths, differenze tra i byte sulla macchina e sulla rete, e così via). Se modifica una immagine, utilizzate gdk_draw_image() per ri-copiarla sul lato server.
Copiare una pixmap in una GdkImage, oppure copiare una GdkImage in una pixmap, implica lo spostamento di svariati dati attraverso la rete. A ogni modo, dato che questa operazione viene eseguita tutta assieme, la velocità può essere tollerata nella maggior parte dei casi. Inoltre, se il client e il server si trovano sulla stessa macchina, e se l'estensione di memoria condivisa di X è disponibile, GDK creerà automaticamente un segmento di memoria condivisa per eseguire la copia dei dati.
Nella maggior parte dei casi, se pianificate di eseguire molte manipolazioni di
immagine, è consigliato usare i buffer RGB come struttura di dati
principale (si veda
la sezione Buffer RGB nel il capitolo I fondamenti di GDK
). Le funzioni in gdk/gdkrgb.h vi permettono di copiare un buffer RGB in un drawable. Queste funzioni utilizzano GdkImage internamente, ma sono ottimizzate per essere molto veloci e gestire tutti gli aspetti complessi al posto vostro.
GtkLabel è un widget senza finestra: è "transparente" e disegna sullo sfondo del contenitore/widget padre. Se desiderate impostare uno sfondo, inserite la GtkLabel all'interno di un GtkEventBox. Lo stesso procedimento si applica per altri widget senza finestra, come il GtkImage.
gtk_pippo_pluto() è tipicamente una funzione
pubblica che emette il segnale "pluto",
prendendosi cura di tutti i dettagli necessari per il prima e il dopo
l'emissione di questo (ricordate che unicamente i segnali GTK_RUN_ACTION possono essere emessi senza speciali
operazioni prima e dopo). gtk_pippo_real_pluto() è il
gestore di default di questo segnale, inserito all'interno della struttura
della classe dell'oggetto. Il
il capitolo Scrivere un GtkWidget
fornisce molti esempi.
Esistono svariate possibilità:
-
Il widget non possiede una GdkWindow (ad esempio se è impostato il flag GTK_NO_WINDOW), tale che non possa ricevere eventi.
-
L'evento che state cercando di monitorare non è nella maschera di eventi del widget GdkWindow. Utilizzate gtk_widget_add_events() per aggiungere maggiori eventi alla maschera.
-
Il widget è un contenitore, e qualcuno dei figli al suo interno stanno gestendo l'evento ritornando il valore TRUE dalla emissione del segnale dell'evento. Unicamente gli eventi che non vengono monitorati si propagano dai figli ai padri.
Maggiori dettagli sugli eventi e sulle modalità di trasferimento di questi ai widget sono disponibili a
la sezione Ricevere gli eventi di GDK in GTK+ nel il capitolo I fondamenti di GDK
.
In primo luogo, avviate il vostro applicativo con l'opzione --sync. Questa invoce la funzione XSynchronize() per disattivare il buffering degli eventi. Questo rallenterà la vostra applicazione, ma la causa degli errori verrà notificata non appena verrano generati. In alternativa, alcune implementazioni di Xlib vi permettono di disattivare la sincronica impostando la variabile globale _Xdebug a TRUE in un debugger.
Una volta che gli errori vengono riportati in sincronia, avviate la vostra applicazione in un debugger e aspettate la chiamata ad abort(). Per i messaggi di errore, impostate una interruzione a g_logv(), la funzione chiamata dalla macro g_warning().
Fate in questo modo:
|
while (gtk_events_pending())
gtk_main_iteration();
|
Questo codice gestisce tutti gli eventi in attesa, quindi vi lascia nuovamente il controllo. Potete inoltre utilizzare istanze nidificate di gtk_main(). Ciascuna chiamata a gtk_main_quit() esce una sola ed unica istanza. gnome_dialog_run() utilizza questa tecnica per bloccare l'attesa di una risposta dal parte dell'utente.
Lo stile di scrittura di GTK+ è fondamentalmente il GNU Coding Style (
http://www.gnu.org/prep/standards_toc.html
). Le librerie di Gnome sono meno coerenti, poiché seguono lo stile del codice del kernel Linux (documentato in /usr/src/linux/Documentation/CodingStyle sulla maggior parte dei sistemi Linux).
Lo stile di GTK+ usa una indentazione di due spazi, ponendo tutte le parentesi su una nuova linea, e lascia uno spazio tra gli identificatori e le parentesi aperte, in questo modo:
|
if (qualunquecosamipiaccia)
{
mangiare (arg1, arg2);
}
|
Emacs utilizza questo stile per default.
Lo stile di Gnome utilizza invece una indentazione di otto spazi e le parentesi nello stile di Kernigham e Ritchie, così:
|
if (lamammamicerca) {
scappare (arg1, arg2);
}
|
Lascia inoltre uno spazio fra gli indentificatori e l'apertura delle parentesi. Per utilizzare Emacs con lo stile di Gnome, aggiungente una linea come questa in testa ai vostri file sorgente:
|
/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8 c-style: "K&R" -*- */
|
Quando preparate una patch per qualunque tipo di software libero, è buona educazione utilizzare lo stile del codice pre-esistente. Solitamente viene aggiunto un file chiamato HACKING nella distribuzione del codice sorgente per descrivere queste modalità. Se esiste, leggetelo.
Un generatore di GUI molto promettente chiamato Glade è in fase di sviluppo. Glade può generare il codice sorgente in svariati linguaggi, oppure una descrizione in XML dei vostri widget. Un modulo aggiuntivo chiamato libglade utilizza queste descrizioni XML per generare a runtime i widget descritti al suo interno. La prossima release delle librerie di Gnome includeranno o richiederanno libglade.
GTK+ 1.2 supporta la maggior parte dei linguaggi europei e esiatici. GDK contiene una API per caricare i font e visualizzare stringhe in multibyte, ma questo libro non ne parla. I widget stock di GTK+ che gestiscono il testo utilizzano questa API e lavorano correttamente con le stringhe in multibyte. GTK+ inoltre supporta metodi di inserimento per i linguaggi asiatici. GTK+ 1.2 non supporta la scrittura da destra a sinistra, oppure la scrittura che richiede particolare legature e interruzioni di linea non usuali. Ad ogni modo, il supporto per questi linguaggi è una delle priorità di GTK 1.4. Per maggiori dettagli su questo progetto, un documento di Owen Taylor è disponibile a
http://www.gnome.org/white-papers/i18n/gtki18n/
. È una grande fonte di risposte alle vostre domande.
Sia GTK+ che Gnome utilizzano il sistema di catalogazione dei messaggi di gettext per tradurre le stringhe visualizzate nelle GUI, quindi queste sono traducibili in moltissime lingue diverse.
la sezione Internazionalizzazione nel il capitolo Fondamenti di una applicazione Gnome
descrive questo argomento.