La definizione della geometria degli elementi di una form o di una finestra è il primo passo per la creazione di interfacce grafiche in Qt. Tuttavia, per ottenere un risultato valido e consistente è necessario definire anche il modo in cui l'interfaccia muta quando viene ridimensionata. Quando la dimensione di una finestra cambia, tutti gli elementi che la compongono devono scalare il maniera armonica ed in modo tale da preservare la loro funzionalità.
A seconda del caso specifico ci saranno elementi la cui dimensione deve rimanere immutata, come pulsanti ed etichette, e altri che devono espandersi prevalentemente in verticale, in orizzontale o in entrambi i sensi per massimizzare la fruibilità, ad esempio liste e viste ad albero, aree di testo, eccetera.
Inoltre, a parte i requisiti funzionali, può anche essere necessario aderire a canoni estetici specifici.
Il comportamento delle widget Qt al variare delle dimensioni della finestra è determinato da molteplici proprietà e dal loro contesto, inteso come insieme gerarchico di elementi.
In questa lezione esamineremo un'interfaccia sufficientemente complessa al fine di illustrare le proprietà principali che concorrono alla definizione della forma e del dimensionamento di una widget al variare delle dimensioni del suo elemento contenitore.
Consideriamo il caso di un'applicazione per la riproduzione di raccolte di file audio, che si compone di diverse viste tra cui quella che contiene i controlli per la riproduzione e l'elenco dei brani in esecuzione, che sarà oggetto di analisi.
Dato il tipo di applicazione, è ragionevole considerare due modalità di uso prevalenti:
- la dimensione della finestra è minimizzata, per consentire all'utilizzatore di visualizzare altre finestre sullo schermo in contemporanea;
- la dimensione è massimizzata, per cui occorre prestare attenzione alla simmetria ed armonia dell'interfaccia di modo da renderla esteticamente accettabile, anche se la superficie disponibile è sovradimensionata rispetto il numero di elementi da mostrare.
Infine, bisogna considerare il caso di dimensioni intermedie, in cui il layout deve risultare allo stesso modo funzionale e gradevole.
Basandoci su queste considerazioni di base, e senza pretesa di originalità o di valenza estetica, il layout che vogliamo definire per la vista in modalità di riproduzione è del tipo illustrato nell'immagine seguente:
L'interfaccia è separata in due aree principali: quella superiore che contiene la playlist corrente e l'anteprima della copertina dell'album o del brano in riproduzione, e quella inferiore in cui sono concentrati i controlli per la riproduzione.
Come si evince, queste due aree hanno politiche di ridimensionamento totalmente differenti: la parte superiore si espande principalmente in altezza perché il suo componente principale è una lista, mentre quella inferiore ha un'altezza fissa, ma cresce in larghezza per garantire la leggibilità del titolo della traccia corrente.
Inoltre, l'immagine di copertina ha una dimensione fissa e, per ragioni di simmetria, molti elementi sono centrati rispetto l'asse verticale della finestra.
Politiche di ridimensionamento
Per ottenere questo tipo di comportamento è necessario agire su molteplici parametri. Innanzi tutto, l'individuazione delle aree funzionali molto spesso è un'ottima indicazione per l'uso di elementi contenitori. Per questo motivo, la parte superiore e quella inferiore della vista sono contenute in due QFrame differenti.
I QFrame sono essenzialmente delle widget vuote che fungono da elementi contenitore, cui è possibile attribuire un layout per la disposizione degli elementi contenuti e la politica di ridimensionamento orizzontale e verticale da applicare globalmente rispetto agli altri elementi contenitore.
La classe QWidget
rappresenta la politica di ridimensionamento mediante un membro di tipo QSizePolicy
, che tra i vari dati membro e metodi, incapsula anche le due modalità di ridimensionamento verticale ed orizzontale, accessibili direttamente dal designer o mediante le apposite API C++.
I valori possibili per una size policy sono i seguenti: Fixed, Minimum, Maximum, Preferred, Expanding, MimumumExpanding e Ignored. Il loro significato è da mettere in relazione con la definizione della dimensione suggerita della widget, che in Qt prende il nome di size hint, così come essa viene determinata in base alle impostazioni correnti date da designer, prima che la widget venga effettivamente disegnata a schermo.
Quando la policy è Fixed, la widget non dovrebbe essere alterata rispetto la dimensione calcolata dal motore di layouting. Quando essa è Minimum la dimensione indicata è minimale, quindi la widget può essere estesa; Maximum, al contrario, indica che la widget ha già dimensione massima e quindi può essere rimpicciolita qualora altri elementi abbiano bisogno di espandersi. Preferred implica che la dimensione indicativa è ottimale, e non vi è alcun vantaggio nel restringere o ampliare le dimensioni della widget, tuttavia queste operazioni sono permesse al motore di layouting. Expanding e Minimum Expanding indicano che la dimensione della widget può crescere liberamente, ma Expanding implica che, se necessario, la widget può anche essere rimpicciolita, mentre con Minimum Expanding la widget non può restringersi oltre la dimensione di partenza.
In questo caso, quello che ci interessa è definire un'opportuna politica di espansione verticale che risulta essere Expanding per la parte superiore e Fixed per quella inferiore, come mostrato nella figura seguente:
L'importanza dell'attribuzione della policy corretta consiste nel fatto che essa ci evita di dover attribuire valori numerici precisi alle dimensioni di una widget. Ad esempio, un comportamento simile lo si poteva ottenere fissando l'altezza minima e massima del pannello inferiore al medesimo valore in pixel. Purtroppo però, così facendo, rischiamo di imporre vincoli di rappresentazione che possono non essere applicabili in ogni piattaforma, generando artefatti o disallineamenti. Le politiche di ridimensionamento invece lasciano che sia il motore di layouting a decidere la dimensione ottimale, considerando anche i vincoli imposti dal sistema operativo, quali margini, dimensioni minime dei controlli grafici ecc.
Un altro esempio del funzionamento delle politiche di ridimensionamento si trova all'interno del pannello inferiore, mostrato in dettaglio nella figura seguente, in cui si trova un layout orizzontale annidato (in rosso) che raggruppa le etichette usate per il titolo di traccia, il tempo residuo e lo slider per il volume, nel contesto del layout a griglia dell'elemento contenitore.
In questo caso, si suppone che l'etichetta del tempo residuo mostri sempre lo stesso numero di caratteri mentre la dimensione dello slider imposta da designer è quella massima, ma può decrescere, se necessario, fino alla dimensione minima definita. L'unico elemento che può espandersi liberamente è l'etichetta con la traccia del titolo, la cui size policy orizzontale può essere indifferentemente Expanding o Preferred, anche se la prima risulterebbe formalmente più corretta.
Elementi distanziatori
Alcune delle caratteristiche di questa interfaccia sono state ottenute mediante l'uso di elementi distanziatori orizzontali e verticali. All'interno del pannello inferiore, ad esempio, sono stati inseriti degli elementi distanziatori a sinistra e a destra del gruppo di pulsanti che controllano la riproduzione dei file audio con lo scopo di centrarne la posizione rispetto l'asse verticale della finestra.
La rappresentazione degli elementi distanziatori nel designer ricorda il disegno di una molla, come è visibile anche nella figura precedente. In effetti, tali elementi hanno il solo scopo di comprimere in modo "elastico" gli altri elementi presenti nel layout, secondo la loro politica di ridimensionamento che per default è impostata come Expanding.
Il medesimo espediente è stato usato per centrare la posizione del gruppo costituito dall'immagine di copertina e la playlist, come risulta evidente nell'immagine seguente:
In questo caso, è visibile anche l'elemento distanziatore verticale che viene usato per mantenere in alto l'immagine di copertina, quando la finestra ha un'altezza superiore a quella minima.
Questa disposizione evita che la playlist si espanda eccessivamente in senso orizzontale quando le dimensioni della finestra aumentano. L'area inutilizzata a destra e a sinistra può essere destinata a contenuti grafici, ad esempio un'immagine di sfondo, come vedremo meglio descrivendo le opzioni di stile di Qt nelle prossime lezioni.
Dimensioni massime e minime
La definizione della dimensione di una widget si articola su più proprietà: dimensione effettiva, minima e massima. In questo esempio, tali vincoli sono stati usati per definire la larghezza minima e massima dell'elemento contenitore della playlist e della copertina, per garantire un'escursione controllata durante il ridimensionamento della finestra.
Inoltre, come caso particolare, i vincoli sulla dimensione massima e minima sono stati impiegati anche per rendere invariante rispetto al ridimensionamento della finestra la dimensione di dei pulsanti play, backward e forward (32x32 px) e dell'immagine di copertina (170x170 px).
Per questo caso d'uso specifico vogliamo impartire valori arbitrari e specifici. Per garantire invariabilità, minimi e massimi devono coincidere al fine di inibire il calcolo di una dimensione suggerita da parte del motore di layouting. Si ricorda infatti, che la politica di ridimensionamento Fixed è riferita a quest'ultima e pertanto può variare rispetto quella mostrata a schermo da Qt Designer.
Tuttavia, preferibilmente, l'uso di vincoli sulle dimensioni massime e minime andrebbe limitato ai soli elementi che hanno contenuti puramente grafici come icone o immagini. Nel caso di elementi con contenuti testuali, come ad esempio un etichetta di testo o un campo di input testuale a riga singola, tali impostazioni potrebbero essere in conflitto con i parametri imposti dal sistema per la rappresentazione dei caratteri come pixel o numero di punti e DPI. Aggiungere un margine di sicurezza alle dimensioni minime può ridurre il problema ma può anche alterare la percezione estetica dell'interfaccia producendo effetti differenti tra una piattaforma ed un'altra.
In conclusione, l'attribuzione di un layout alla nostra interfaccia comporta anche la definizione di vincoli di rappresentazione opportuni per la determinazione del comportamento dell'applicazione quando è soggetta a ridimensionamento. Tali vincoli sono espressi principalmente mediante la politica di ridimensionamento, l'uso di elementi distanziatori e la definizione di valori massimi e minimi per la larghezza e l'altezza di ogni widget. Tali parametri però, vanno anche rapportati al contesto nel quale essa è inserita, e aggiustati di conseguenza fino ad ottenere il risultato desiderato.