Lezione 11. Basi di interface design in C++

c__2La grafica, ed in specifico la creazione dell’interfaccia, è un esempio tipico della sfera dedicata alle applicazioni.

Quando ci ritroviamo di fronte alla pagina bianca in cui dobbiamo creare un’interfaccia il nostro primo istinto è pensare a quante cose possiamo inserirci, alla quantità di informazione che siamo in grado di rendere nella sua grafica ma, in realtà, dovremmo tenerci lontani da questa idea. Le interfaccie più effettive e ben suddivise sono, infatti, quelle che mostrano meno elementi, ben definiti così da essere in grado di non confondere l’utente. Questo tipo di regola è applicabile anche nella creazione di pagine web in cui è preferibile aiutare, in un certo senso, l’utente nella navigazione del sito non riempendolo troppo di applicazioni, scritte di vario genere e font e finestre varie (se vi interessa un discorso di questo genere dedicato alla grafica teorica date un’occhiata a questo articolo che mette a confronto due diverse pagine di cui una un po’ più “pienotta” mentre l’altra più spoglia dimostrando come la seconda sia meno dispersiva per l’occhio del friutore).

Come prima cosa, quindi, dovremo decidere che tipo di grafica/GUI vogliamo fare cercando di renderla semplice e minimale in vista del suo uso e, conseguentemente, di limitarne le opzioni. In tal modo le classi, ovvero il gruppo di oggetti appartenenti alla nostra interfaccia con le stesse caratteristiche e funzionalità, dovranno mostrare uno stile comune. Per esempio, se prendiamo tutte le funzioni con simili operazioni ma appartenenti a diverse classi cercheremo comunque di utilizzare una struttura simile e con un ordine coordinato nei loro argomenti. Le librerie GUI (Graphical User Interface) hanno centinaia di classi qui elencheremo le più semplici ed usate:

  • Color, usato per linee, testo e riempire le forme.
  • Line_style, usato per disegnare linee.
  • Point, usato per segnalare posizioni sullo schermo e in una finestra.
  • Line, è una linea sullo schermo definira dai due Points ai suoi estremi.
  • Open_polyline, una seguenza di segmenti connessi e definiti da una sequenza di Points.
  • Closed_polyline, una sequenza di segmenti connessi e definiti da una sequenza di Points in cui l’ultimo Point è anche il primo.
  • Polygon, un Closed_polyline dove non vi sono segmenti che intersecano tra loro.
  • Text, una stringa di caratteri.
  • Lines, un set di segmenti definiti da coppie di Points.
  • Rectangle, Circle, Ellipseforme comuni (ovvero rispettivamente rettangolo, circolo ed elisse) ed ottimizzate per un loro veloce utilizzo.
  • Axis, un’asse etichettata.
  • Mark, un Point marcato da un carattere come, per esempio, x oppure p.
  • Marks, una sequenza di punti indicati da vari marchi.
  • Marked_polyline, un Open_polyline con l’indicazione di punti denominati con x, b, p, etc.
  • Image, il contenuto di un file immagine.
  • Window, un area dello schermo in cui possiamo vedre i nostri oggetti grafici.
  • Simple_window, una finestra con il bottone “Next” (Continua).
  • Button, un rettangolo, normalmente etichettato, in una finestra che possiamo schiacciare per avviare una funzione.
  • In_box, un box, normalmente etichettato, in una finestra in cui un utente può scrivere una stringa.
  • Out_box, un box, normalmente etichettato, in una finestra in cui il programma, e non l’utente, può scrivere una stringa.
  • Menu, un vettore di Buttons.

Prima di tutto se una forma richiede una posizione verrà utilizzato come suo argomento iniziale una classe Point per rappresentarlo. Il Point è una delle principali e più importanti classi nel sistema grafico poiché ci permette di organizzare il nostro spazio geometrico: quando, per esempio, vogliamo disegnare qualcosa a mano libera tracciamo prima di tutto delle linee che ci permettano di suddividere lo spazio del foglio, questo avviene anche nel caso della computer grafica.

Stroustrup nel capitolo 13 del suo libro Programming Principles and Practice Using C++, utilizza l’esempio di una funzione per disegnare una linea mostrando due possibilità di scrittura:

void draw_line(Point p1, Point p2);
void draw_line(int x1, int y1, int x2, int y2);

Il primo stile utilizzato, ovvero void draw_line (Point p1, Point p2);, è preferibile rispetto al secondo per la sua semplicità ed eleganza che rendono anche la lettura del codice più veloce ed immediata sebbene anche la seconda linea funzioni comunque. In questo caso, al contrario del void* che abbiamo incontrato nella Lezione 10, scriviamo void come return type perché non ci interessa il risultato che la funzione restituisce: stiamo quindi attenti a non confondere i due comandi distinti tra loro dalla presenza o meno dell’asterisco. Il comando draw_line è semplicemente relativo alla richiesta di disegnare una linea che, come segnalato all’interno delle parentesi, vada dal punto p1 (o x1,y1) al punto p2 (o x2, y2). Inoltre, l’utilizzo di Point ci evita anche di far confusione quando ci troviamo davanti alle coordinate di altezza e larghezza come, per esempio:

draw_rectangle(Point(110,210),310,410);
draw_rectangle(110,210,310,410);

Come possiamo notare, è vero che il secondo metodo in questo caso paia più diretto e semplice ma non è così: anche in questo caso preferiamo la prima linea di comando scritta. Questo perché sappiamo che il punto è definito dalla posizione (110, 210), la sua larghezza di 310 e l’altezza di 410 che sono sempre presentate in tale ordine; nel secondo caso non abbiamo la certezza di questa suddivisione o per lo meno non è così immediata come dare un veloce sguardo alla locazione delle parentesi.

Tutte le operazioni identiche hanno lo stesso nome, per esempio add() vale ogni qualvolta vogliamo aggiungere una nuova forma. Ma, per esempio, se vogliamo aggiungere una forma ad una finestra useremo il comando attach mentre per aggiungere una linea all’interno di una forma utilizzeremo add. Le funzioni sono simili: aggiungiamo infatti qualcosa nella nostra forma o finestra ma proprio perché l’aggiunta avviene su due diversi livelli il comando utilizzato varia. Nel primo caso, infatti, dovremo per esempio scrivere:

Open_polyline opl;
opl.add(Point(110,110));
opl.add(Point(120,220));

Qui, quindi aggiungiamo due punti nella forma chiamata opl. Mentre se dovessimo aggiungere la forma opl ad una finestra utilizzeremo il seguente comando:

win.attach(opl);

Creando, così, una connessione tra la finestra denominata win e la forma opl. Qui, però, dovremo ricordarci di non uscire dallo scopo dell’opl mentre win sta usando l’opl.

Vediamo, ora, un piccolo software per creare due linee:

//Disegna due linee
#include <iostream>
#include <string>
#include <Simple_window.h>
#include <Graph.h>
using namespace std;
using namespace Graph_lib; //la struttura dei grafici si trova in Graph_lib
int main()
{
 Point tl(100, 100); //per posizionare la forma nell'angolo in alto a sinistra della finestra
 Simple_window win(tl, 600, 400, "Canvas"); //crea una finestra semplice
 Polygon poly; //crea un poligono
 poly.add(Point(300, 200)); //aggiunge un Point
 poly.add(Point(350, 100)); //aggiunge un altro Point
 poly.add(Point(400, 200)); //ed un terzo Point
 poly.set_color(Color::red); //si occupa delle proprietà del poligono
 win.attach(poly); //connette il poligono alla finestra
 win.wait_for_button();
}

Quando ci ritroveremo a tentare di usare il compiler con questo programma ci ritroveremo di fronte a parecchi problemi relativi alla libreria, ovvero a ciò che dobbiamo includere: nel libro di Stroustrup, infatti, in questo esempio vengono segnalate solo due librerie ovvero: #include <Simple_window.h> #include <Graph.h> (ricordiamoci che l’uso delle virgolette non cambia nulla per cui possiamo scrivere le librerie anche in questo modo: #include “Simple_window.h” e #include “Graph.h”) che, molto probabilmente se non sicuramente, non verranno trovate dal vostro compiler: se date un’occhiata su internet c’è parecchia gente disperata che non sa dove sbattere la testa per riuscire a compilare questo programma.

Ergo, ci sono due soluzioni al problema: usare linux oppure sbattersi un bel po’ per far funzionare il compiler su windows come spiegato di seguito.

Così il vostro compiler qualsiasi esso sia (nel mio caso Visual Studio) vi segnalerà un errore del tipo: fatal error C1083: Cannot open include file: ‘Simple_window.h’: No such file or directory. Come fare per evitare questo errore e far funzionare il nostro programmino? 

  1. Clicchiamo sul seguente link che avvierà un download in automatico: https://github.com/bewuethr/stroustrup_ppp/archive/master.zip.
  2. Cerchiamo la cartella in cui abbiamo i nostri file C++, quella per intenderci con le varie cartelle denominate ConsoleApplication ed apriamola lasciandola da parte.
  3. Apriamo il file .zip che abbiamo ottenuto e scaricato e cerchiamo la cartella lib_files. Una volta trovata tale cartella spostiamola all’interno di quella di C++ assieme a quelle chiamate ConsoleApplication.
  4. Facciamo una piccola modifica alle scritte #include <Simple_window.h> e #include <Graph.h> come di seguito mostrato:
#include <C:\Users\an\Desktop\C++\lib_files\Simple_window.h>
#include <C:\Users\an\Desktop\C++\lib_files\Graph.h> 

Ovviamente al posto di, per esempio, <C:\Users\an\Desktop\C++\lib_files\Simple_window.h> dovrete scrivere la posizione sul vostro computer della vostra cartella C++ per dar modo al compiler di sapere da dove attingere ciò che gli serve.

Una volta fatto questo però il compiler ci segnalerà simpaticamente un nuovo errore: fatal error C1083: Cannot open include file: ‘FL/Fl.H’: No such file or directory. Questo errore si riferisce alla libreria ftlk parecchio datata e per questo non inclusa nei file standard. Quindi, a questo punto dobbiamo scaricarci anche questa libreria fltk cliccando qui farete il download automatico: il file è in formato .gz e potrete aprirlo come un normale file .zip, ovvero per esempio con il software 7.zip.

Fatto ciò, andiamo nel nostro Visual Studio, per esempio, e seguiamo i passi qui descritti:

  1. Andiamo nella cartella appena creata e cerchiamo fltk.dsw aprendola. Se non la trovate ed utilizzate Windows 10 basta scriverne il nome in basso a destra accando all’icona Start.VS1
  2. Si aprirà Visual Studio, diamogli l’Ok ed attendiamo alcuni minuti che si concluda il caricamento dei files.VS2
  3. Poi, in alto a destra, nella barra delle applicazioni clicchiamo su Build e Build Solution (Costruisci soluzione).VS3
  4. Attendiamo nuovamente alcuni minuti perché il tutto si carichi come mostrato qui sotto.VS4
  5. Chiudiamo allora Visual Studio e facciamo ora, più o meno, quello che abbiamo fatto per le altre librerie copiando ed incollando la cartella lib_files presente nel download appena effettuato in una cartella raggiungibile dal nostro compiler (Stroustrup consiglia di farlo sula cartella denominata vc).
  6. Riapriamo Visual Studio e creiamo un nuovo progetto “Win32 project”, anziché il solito “Console Application”, sincerandoci di scegliere un Empty project (progetto vuoto) spuntando la casella nelle Application Settings che compariranno e cliccando poi su Finish.VS6
  7. Una volta aperto il progetto vuoto andiamo sempre in alto nella barra delle applicazioni e selezioniamo Project (Progetto) e poi Properties (Proprietà): si aprirà una nuova finestra.VS7
  8. Clicchiamo due volte su Configuration Properties (Configurazione proprietà), dopodiché su Linker ed infine su Input. Come mostrato nell’immagine sottostante, ora andiamo a cliccare accanto alla scritta Additional Dependencies (Dipendenze aggiuntive) aggiungendo alla lista quanto segue facendo attenzione di utilizzare i punti e virgola correttamente: fltkd.lib; wsock32.lib; comctl32.lib; fltkjpegd.lib; fltkimagesd.lib;VC3
  9. Sempre sulla stessa pagina, selezioniamo ora Ignore Specific Library (Ignora specifica libreria) e scriviamovi accanto libcd.lib
  10. Nel nuovo progetto appena fatto cresiamo un .cpp file su cui finalmente scrivere il nostro codice facendo attenzione di aggiungere le nuove librerie che ci servono similmente a come segue oppure utilizzando :
#include <C:\Users\an\Desktop\C++\lib_files\FL\Fl.H>
#include <C:\Users\an\Desktop\C++\lib_files\FL\Fl_Box.H>
#include <C:\Users\an\Desktop\C++\lib_files\FL\Fl_Window.H>

Ci sono altri modi, ovviamente, di inserire una libreria. Personalmente ho preferito mixare il mio modo con quello descritto da Stroustrup cosicché chi segue quel libro possa comunque ritrovarsi facilmente nelle spiegazioni che, comunque, sono presenti se non erro all’appendice D del libro.

Passiamo, ora, finalmente alla descrizione del piccolo software: qui oltre all’ormai conosciuto using namespace std; dobbiamo aggiungere anche using namespace graph_lib poiché è qui che attingono i nostri strumenti grafici. Dopodiché definiamo il punto Point tl(100,100) che segnala la posizione della nostra forma a partire dall’angolo in alto a sinistra della finestra. Poi, creiamo con Simple_window win(tl,600,400,”Canvas”); una finestra semplice sullo schermo: win è il nome nonché la variabile della nostra Simple_window mentre tl segnala la posizione del Point relativo al comando precedente; 600 e 400 sono rispettivamente la larghezza e l’altezza della finestra misurate in pixel; Canvas semplicemente da un’etichetta alla finestra e potremmo cambiarla con il nome che più ci spieffera in quel momento. 

I successiVC5vi comandi relativi a Polygon.poly definiscono la figura di un poligono ed i suoi angoli con poly.add creando praticamente un triangolo utilizzando dei Point che ne localizzano la postazione dei vertici. Mentre poly.set_color seleziona il colore del triangolo da noi disegnato; win.attach(poly) collega il poligono alla finestra da noi creata precedentemente.

Infine, per poter osservare il nostro triangolo sullo schermo come nell’immagine qui a lato dobbiamo aggiungere al nostro programma il comando win.wait_for_button().

Share the love

Comincia la discussione

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.