GUI Frameworks - part 2/3
July 14th, 2006 di Aaron Brancottiin GDI, gui, OpenGL | Letture: 2414
ENTRA NEL VIVO il viaggio alle radici tecniche della GUI (Graphical User Interface). Stavolta si parla della “terra di mezzo” che tutti i programmatori vorrebbero che i designer conoscessero meglio.
- per una ideale comprensione di questo articolo, fate riferimento alla puntata 1/3.
Finestre Modali e Non-Modali
Una capacità tipica di una GUI è quella di presentare all’utente finestre che possono essere modali o non modali. Una finestra è detta modale quando è bloccante: fino a quando l’utente non la chiude in qualche modo, il programma non procede. Tipiche finestre modali sono le MessageBox, solitamente dotate di uno o più bottoni di conferma e altre opzioni.
Le viste invece sono solitamente non modali, permettendo l’interazione coi dati ma anche la semplice osservazione degli stessi mentre cambiano.
La modalità di una finestra può essere specificata a molti livelli: a livello di applicazione, per cui la stessa si ferma in attesa dell’acknowledgement dell’utente, ma anche a livello di sistema, per cui tutto si blocca in attesa di input.
Look And Feel
Un componente importante delle GUI è il cosiddetto Look And Feel, ovvero la capacità di modificare più o meno pesantemente l’aspetto estetico dell’interfaccia stessa.
Si va dalla semplice capacità di cambiare spessore dei bordi delle finestre a modificare i font di sistema e a cambiare l’icona del pulsante di chiusura, fino ad arrivare allo skinning, forma di Look And Feel estremo in cui è possibile chiedere ad una applicazione di usare finestre rotonde ed altre amenità. Si noti che il Look And Feel è solitamente un concetto system-wide, mentre lo skinning è spesso implementato a livello di singola applicazione.
Portabilità
Una caratteristica molto importante e desiderabile è la possibilità di usare lo stesso framework su sistemi operativi e architetture diverse.
Si ricordi che imparare ad usare bene un framework è una faccenda lunga e cognitivamente dispendiosa e che quindi un programmatore difficilmente ha voglia di ricominciare spesso questo processo di apprendimento.
Rendering Independence
Per indipendenza dal motore di rendering si intende la capacità che un framework ha di disaccoppiarsi dalla tecnologia di visualizzazione utilizzata, che viene poi tipicamente implementata a livello di hardware.
Per un framework avere una simile capacità significa che, teoricamente, quando si vede che certe applicazioni pesantemente grafiche annaspano per la troppa complessità, si può decidere di scalare su HW più potente e schede video anche radicalmente diverse senza dover stravolgere il lavoro già fatto. Analogamente, è inutile usare macchine dotate di sottosistemi grafici ultrapotenti quando una scheda video da 100 euro può essere più che sufficiente.
In questa ottica è opportuno vedere quali sono attualmente i principali attori nel mondo dei sistemi di rendering a basso livello:
Graphic Engines
Si ricorda che vengono qui presentate solo alcune tra varie “tecnologie di rendering”, tenendo presente che queste sono librerie al più basso livello possibile: stiamo parlando della capacità di disegnare rette, punti, poligoni colorati, immagini bitmap caricate da disco e poco altro, ma di farlo a velocità strabilianti (milioni di poligoni al secondo).
Immediate e Retained Mode
In particolare le seguenti sono librerie cosiddette in Immediate Mode, che non hanno nessuna memoria di quello che abbiamo fatto al fotogramma precedente. Ad ogni fotogramma, il video viene completamente cancellato ed è a cura del programmatore emettere una sequenza di comandi che disegnino rette, punti e poligoni in modo da comporre l’immagine desiderata.
Contrapposte a queste esistono numerose librerie Retained Mode, soprattutto nel mondo 3D, dove viene garantita la “persistenza” degli oggetti renderizzati: se non si fa nulla, il frame successivo è uguale al frame precedente. È il sistema stesso che mantiene gli stati necessari a disegnare le immagini, mentre al programmatore è fondamentalmente richiesto di specificare quali siano le variazioni tra un fotogramma e il successivo.
GDI
La Graphic Device Interface di Microsoft è stata per anni l’unico motore che un programmatore Windows aveva a disposizione per costruire la grafica delle sue applicazioni. Estremamente lenta, è stata gradualmente soppiantata (nel mondo Microsoft) da DirectX, ma ancora resiste come sistema “legacy” in framework ormai vecchi ma ben lungi dall’essere abbandonati come le MFC (vedi oltre).
DirectX
Quando Bill Gates si rese conto che stava perdendo il treno dei videogiochi a causa delle prestazioni ridicole delle macchine Windows in ambito multimediale, investì cifre che solo lui avrebbe potuto investire comprando numerose tecnologie e adattandole – più o meno a martellate – all’architettura Windows. DirectX è in realtà una gigantesca accozzaglia di sottomoduli come Direct3D (a sua volta nato dalle ceneri di RenderMorphics di Servan Keondijan), DirectSound, DirectPlay, DirectVideo etc etc. Nessuno sarebbe mai andato verso una simile architettura, più volte stravolta nel corso degli anni, se a proporla non fosse stata Microsoft.
D’altro canto, DirectX è il responsabile dell’avvento di schede grafiche estremamente potenti a livello consumer, obiettivo fino a quel momento mancato da Silicon Graphics con le sue OpenGL.
OpenGL
Le OpenGL sono la versione “open” – nel senso che chiunque è libero di fornirne una implementazione mantenendo la compatibilità con le API – delle GL, Graphics Libraries nate in casa Silicon Graphics dalla ancora più antica libreria grafica PHIGS. Quando DirectX muoveva i primi passi, OpenGL era già nel pieno della maturità, ma essendo quello un ambiente professionale e high-end i costi erano decine di volte superiori.
Per questo motivo OpenGL – che oltretutto è nativamente tridimensionale – ha continuato a vivere nel mondo accademico delle super-workstation mentre DirectX arrancava in mezzo ad hardware da quattro soldi, fino a quando qualcuno si è reso conto che si poteva finalmente reimplementare OpenGL anche su schede da poche centinaia di dollari.
Oramai la scelta tra DirectX è OpenGL è più una “Guerra di Religione” che una questione tecnica, essendo entrambi i sistemi assestati su prestazioni standard che dieci anni fa richiedevano macchine da milioni di dollari.
Null Renderer
Per null renderer si intende un sottosistema grafico che non disegna nulla. Uno potrebbe a questo punto chiedersi se gli sviluppatori non siano tutti cretini. Beh, i motivi per fare una cosa così apparentemente insensata sono vari: ad esempio può essere utile in fase di test, dove tutto il funzionamento dell’interfaccia è simulato a livello di codice, compresi i click col mouse, le pressioni dei tasti etc.
Avere un renderer che al posto di disegnare un bottone premuto scrive in un file “Premuto Bottone X” permette di far girare la propria applicazione la notte in modalità automatica e di controllare il giorno dopo che non ci siano stati errori. Ma qui stiamo parlando di software testing e di Test Driven Developement, e questa è tutta un’altra storia…
Componenti
Che Classi possiamo trovare, tipicamente, in un GUI Framework?
Application Windows
Sicuramente in una GUI ci sono delle finestre. Ecco quindi che più o meno tutti i frameworks rispettano questa usanza; quasi sempre ci sono classi MainWindow, SDIWindow, MainFrame, MDIWindow, ChildWindow… molto spesso a queste, che descrivono la finestra “esterna” sono legate delle classi View e Document, che implementano la separazione vista-documento precedentemente descritta.
Solitamente la View coincide con la parte di finestra “vuota” immediatamente sotto titolo, menu ed eventuali toolbar, ovvero la cosiddetta client area a disposizione del programmatore che ci può disegnare quello che vuole.
La MainWindow si preoccupa invece di gestire per default operazioni quali ridimensionamento, spostamento, chiusura, eventuali scrollbar etc.
Si noti che in questa parte del framework si fa un grande uso dell’ereditarietà: i comportamenti di queste finestre sono molto simili, quindi è molto comune trovare un progenitore comune – ad esempio la CWnd nelle MFC – che incapsula tutti i comportamenti “primitivi” di una finestra, come l’inizializzazione e la capacità di chiudersi e distruggersi in seguito a un evento di Quit.
Widgets
Per Widgets – nome ereditato da X – o, come li chiamano altri framework, controlli, si intende quella nutrita schiera di oggetti software che ha una GUI che si rispetti e che ci permettono di scambiare dati e istruzioni col sistema. Sono centinaia, ne riportiamo qui solo alcuni insieme a qualche considerazione.
Button
Il bottone si commenta da se. Variazioni sul tema sono i CheckButtons, ovvero le “spunte” di selezione e deselezione, i RadioButtons che sono gruppi di bottoni dei quali uno solo alla volta può essere premuto (come i bottoni delle radio degli anni ’20), e i bottoni multistato, che non sono solo “schiacciati” o “non schiacciati”, ma che ogni volta che vengono schiacciati vanno in uno stato diverso informandoci con un apposito feedback (ad esempio cambiando colore o modificando una eventuale icona).
TextBox
Per tutti i casi in cui debba essere mostrato o modificato del testo esiste la TextBox, che a sua volta può diventare uno Static se solo in lettura o una EditBox se permette di scrivere o selezionare testo.
Le textbox vengono poi aggregate ad altri controlli quali bottoni o liste per creare numerosi tipi di widget, dallo Spinner alla ListBox alla ComboBox.
Una opzione importante è la possibilità di incorporare in una textbox un sistema di filtraggio e validazione che impedisca l’introduzione di dati sintatticamente incorretti o semanticamente non significativi.
Esempi sono textbox “solo numeri” o con controllo, mediante apposito algoritmo, di un codice fiscale.
Spinner
Uno spinner è solitamente un widget composto da una textbox e due bottoni di incremento/decremento associati alla textbox. Viene usato solitamente per mostrare valori numerici, spesso interi, e da la possibilità di modificare questi valori mediante i bottoni e a volte scrivendo direttamente il valore nella textbox.
WaitBar
Una WaitBar è semplicemente un elemento visuale non interattivo che ci avverte che il computer “sta lavorando”, magari dandoci una vaga idea di quanto tempo manca alla fine del task e se non ci convenga magari andare a pranzo.
Questo aspetto viene purtroppo viene molto spesso trascurato, tanto che spesso ci si ritrova davanti a computer che non sappiamo se stanno facendo qualcosa o se sono irrimediabilmente piantati.
Bisognerebbe usare più WaitBars (funzionanti, non come quelle della Copia Dei Files di Windows) e il mondo sarebbe migliore.
Menu
Un Menù lo sappiamo tutti come è fatto. Forse non sappiamo quanti tipi di menù esistono: siamo abituati a quelli che scendono dall’alto, ma nulla vieta di farli salire dal basso (anche se ci siamo poco abituati) o farli comparire in centro al monitor e che si aprono “a fiore” come quelli di Maya.
Un menù è tipicamente sensibile al contesto, presentando opzioni diverse a seconda del momento e dell’oggetto al quale il menù si riferisce.
Un menù ben organizzato è quello che fa la differenza tra una applicazione utilizzabile e uno sgorbio incomprensibile anche al suo stesso creatore.
Abstract Data Types
Dalla teoria dell’informazione più pura prendiamo a prestito il concetto di Abstract Data Type (ADT), ovvero di Strutture Dati Astratte, per mostrare come in alcuni casi si siano sviluppati dei widget ben precisi per visualizzarle.
Un ADT non è altro che un ennesimo pattern, un oggetto che organizza dati generici in maniera ben precisa e con ben precise caratteristiche.
Esistono molti ADT: vettori e matrici, liste singole e doppie, alberi, grafi, code, stack, hashmaps, strutture insiemistiche come set e bags, dizionari ovvero memorie associative… ne vediamo solo qualcuna.
List
Informazioni organizzate in records, come ad esempio quelle provenenti da un database, sono spesso organizzate in liste o tabelle.
Queste hanno solitamente una organizzazione “a colonne”, con un header che descrive il dato contenuto in ogni colonna. Una ulteriore variazione sul tema sono i fogli di calcolo, con la loro struttura a matrice.
Nelle liste che supportano il concetto di selezione di righe e colonne, una interessante caratteristica è quella di poter scegliere tra selezioni singole e multiple. Solitamente per queste ultime è necessario l’utilizzo contemporaneo di mouse e tastiera, mediante pressione di tasti come Ctrl o Shift (per Windows) che “ricordano” le selezioni operate fino a quel punto.
Tree
Gli alberi vengono utilizzati nelle situazioni in cui è necessario rendere visivamente in maniera molto diretta delle relazioni tassonomiche o gerarchiche all’interno di un insieme di dati.
Operazioni tipiche sono il folding e l’espansione dei singoli nodi, che permettono di nascondere o mostrare interi rami dell’albero in maniera immediata.
Molte variazioni sul tema esistono; in particolare vogliamo ricordare le visualizzazioni “iperboliche”, particolarmente utili quando si vogliano visualizzare alberi con centinaia o migliaia di nodi. Si noti che un menù è tipicamente un albero sotto mentite spoglie. Un altro esempio importante è un qualsiasi file XML, che per definizione può essere rappresentato sotto forma di albero (anche qualsiasi programma in qualsiasi linguaggio di programmazione imperativo può essere rappresentato sotto forma di albero).
Graph
Mentre un albero è una struttura ordinata adatta alla visualizzazione di gerarchie, un grafo si adatta quindi a quei casi in cui si vogliano mostrare delle generiche relazioni di dipendenza tra dati (ex: una “mappa di siti Internet”).
Stranamente, pochi GUI framework dispongono nativamente di widgets per la visualizzazione di grafi, ed è quindi necessario solitamente appoggiarsi a librerie esterne (come ad esempio JGraph, che va a colmare una lacuna di Java Swing).
Anche qui operazioni tipiche sono espansione e folding di sottografi che fanno capo a un nodo ben preciso, anche se, a causa della loro natura potenzialmente ciclica, i grafi implicano una serie di ulteriori complicazioni. Anche per i grafi sono state proposte implementazioni di visualizzazione in spazi iperbolici, in grado di proporre in maniera efficiente e navigabile grafi composti da decine di migliaia di nodi.
Dialogs
Allo scopo di raccogliere dall’utente più informazioni in una volta sola, spesso vari widgets vengono presentati in un colpo solo all’interno di particolari finestre dette dialogs.
Questi possono essere modali o non modali, a seconda del tipo di informazione richiesta.
Tabs o Property Pages
Quando si verifica la necessità di organizzare più widgets e dialogs in uno spazio insufficiente vengono solitamente utili i Tabs, analoghi a dei segnalibro.
La selezione di un Tab mostra una “sottopagina” della vista o del dialog. Spesso sono presenti dei widget comuni a tutte le pagine, come dei bottoni Avanti/Indietro che permettono la navigazione ordinata di tutti i tabs presenti in un certo dialog.
Wizards, Assistants
L’utilizzo combinato di dialogs e tabs ha anche una grande applicazione nei cosiddetti Wizards, ovvero dei widgets compositi che implementano delle procedure guidate e facilitate rivolte spesso a utenti meno esperti.
I programmi di installazione sono un esempio di questo concetto, dove è possibile scegliere tra installazioni di default o personalizzate.
Gli Wizard sono armi a doppio taglio: da una parte semplificano molto l’utilizzo del computer, dall’altra sono molto rigidi e spesso, quando tentano di dare una certa libertà all’utente, sbagliano a compiere il loro lavoro a causa della intrinseca difficoltà nel testare tutte le possibili combinazioni.
ToolBars e MenuBars
Vari widgets come pulsanti, listbox, textbox o menù vengono a volte raccolti in dialogs non modali visualizzabili e posizionabili liberamente.
Sono queste le cosiddette toolbars, che hanno lo scopo di associare controlli molto mnemonici a operazioni svolte frequentemente. Hanno solitamente la possibilità di comportarsi come piccole finestre (floating toolbar) o di “agganciarsi” ai bordi della main window (docking toolbar).
Anche se indubbiamente utili, un utilizzo indiscriminato di Toolbars può rendere una applicazione inutilizzabile a causa del sovraccarico informativo (si veda l’”evoluzione” di Caligari TrueSpace dalla versione 2 alla 5).
Utility Classes
È opportuno ricordare che, in qualsiasi framework, oltre alle classi relative alla costruzione di GUI, sono spesso presenti una serie di classi di utilità generale quali string o oggetti in grado di collegarsi a un DB e leggerne direttamente i dati etc, piuttosto che classi in grado di recuperare informazioni sul sistema.
In questo senso, un framework non è limitato alla creazione di GUI ma di applicazioni complete, e come tale deve fornire anche una serie di servizi aggiuntivi che vanno al di là della semplice interfaccia.
…to be continued…




