Si propone adesso la costruzione di un programma che permette l'input di una serie di numeri e ne calcola la media aritmetica.
Quando si inserisce un nuovo valore si può premere Invio o il pulsante Aggiungi e il numero si accoda alla lista visualizzata nello spazio sottostante la casella di input. Premendo il pulsante Calcola Media viene calcolata la media aritmetica dei valori inseriti fino a quel momento. Il pulsante Inizializza permette di azzerare la lista dei numeri su cui fare la media e si può ripartire con una nuova lista.
Per prima cosa si procede alla costruzione della GUI:
Avviato FLUID si comincia selezionando Function. Il Name sarà lasciato vuoto per costruire la main.
Si aggiunge alla funzione una Window inserendo nella Label della finestra delle proprietà Media Aritmetica.
Si aggiunge un elemento di tipo Value Input come quello utilizzato nel progetto precedente. Il Name del tab C++ delle proprietà si può impostare a valInp.
Un elemento di tipo Text Display (nella fila centrale del quinto gruppo da sinistra della Widget Bin o da New, Text, Text Display) verrà utilizzato per contenere i numeri introdotti. Nel Name del tab C++ della finestra delle proprietà, inserire disp.
Aggiungere un controllo di tipo Return Button (selezione del pulsante a destra di Button nella Widget Bin o da New, Buttons, Return_Button). Nel tab GUI delle proprietà inserire Aggiungi e nel tab C++ inserire: nel Name aggiungi e in Callback cbAggiungi. La differenza fra un pulsante di tipo Return Button e uno di tipo Button consiste nel fatto che il primo è attivabile anche direttamente premendo il tasto Invio: il che è comodo se si sta usando la tastiera. Può essere la scelta migliore per l'opzione di default quando si effettuano inserimenti.
Aggiungere due elementi di tipo Button. Le Label del tab GUI delle proprietà dei pulsanti si imposteranno a: Inizializza e Calcola Media e i Name del tab C++, rispettivamente, a inizializza e calcM. Le Callback associate ai pulsanti saranno: cbInizializza, cbCalcM.
Aggiungere un elemento di tipo Value Output dove verrà visualizzato il valore medio con la proprietà Name nel tab C++ impostata a media.
Per completare la GUI è necessario aggiungere un nuovo elemento non visuale (non è disponibile nella Widget Bin e non si vede nella GUI) necessario a contenere i dati che verranno visualizzati nel Text Display che è solo un contenitore. Si tratta di un elemento di tipo Text Buffer e affinché si comporti come tutti gli altri widget è necessario fare una dichiarazione globale nel file di intestazione. Selezionare Declaration (primo pulsante a sinistra in basso nella Widget bin oppure selezionare da New, Code, Declaration)
La dichiarazione va inserita nel file di intestazioni come si selezionerà nel menù di scelta che precede, nella finestra, la casella per l'inserimento della riga della dichiarazione stessa. La dichiarazione da inserire sarà:
Fl_Text_Buffer *buf;
È necessario ora inserire il codice per associare il buffer al campo Text Display affinché quest'ultimo ne possa visualizzare il contenuto al suo interno. Selezionare Code e inserire le linee:
buf = new Fl_Text_Buffer(); disp->buffer(buf);
La GUI è completata. Resta da aggiungere l'include del file contenente il codice delle funzioni, comprese le callback, che saranno contenute in un file a parte. A tale scopo come parte finale scegliere ancora una volta Declaration con la stessa scelta nel menù a discesa, come il caso precedente, per l'inserimento nel solo file di intestazione:
#include “mediaF.cxx”
È importante inserire questa dichiarazione per ultima perché in questo modo le dichiarazioni degli elementi grafici, nel file media.h, si troveranno in righe precedenti e, dalle funzioni del file mediaF.cxx, si potrà accedere a tutti gli elementi della GUI.
La comodità dell'utilizzo dell'include consiste nel fatto che, alla fine, l'unico file che dovrà essere compilato sarà media.cxx generato da FLUID, che conterrà l'inclusione di mediaF.cxx assieme a media.h. Anche in questo caso, per ragioni di leggibilità, il nome del file che contiene il codice delle funzioni è scelto in modo da essere lo stesso del progetto ma con, aggiunta, la lettera F finale.
A lavoro concluso la GUI dovrebbe risultare definita in questo modo:
Per la scrittura del codice inserito in mediaF.cxx si può utilizzare Emacs e, alla fine, si dovrà salvare il file nella stessa directory che contiene media.fl, media.cxx e media.h.
Esame delle funzioni da definire nel file mediaF.cxx:
// callback associata al pulsante Aggiungi // aggiunge un numero alla lista di cui calcolare la media void cbAggiungi(Fl_Return_Button* o, void*) { int num; char g[10]; num = valInp->value(); /*1*/ sprintf(g,"%d",num); /*2*/ buf->append(g); /*3*/ buf->append("\n"); /*3*/ valInp->value(0); /*4*/ valInp->take_focus(); /*5*/ }
Si acquisisce (1) il valore contenuto nella casella di input, lo si trasforma (2) in formato stringa C type e lo si inserisce (3) assieme ad un carattere di new line nel buffer.
La 4 reinizializza la casella per l'input e la 5 porta il focus (cursore di inserimento) nel controllo.
// callback associata al pulsante Inizializza void cbInizializza(Fl_Button* o, void*) { media->value(0); /*1*/ buf->text(""); /*2*/ valInp->take_focus(); /*3*/ }
La callback associata al pulsante di inizializzazione è abbastanza semplice: si azzera (1) il valore della media, il contenuto del buffer (2) e si restituisce il focus (3) alla casella di input.
#include <string> /*1*/ using namespace std; // prototipo di funzione richiamata nella callback float NumEstratto(string &); // callback associata al pulsante Calcola Media void cbCalcM(Fl_Button* o, void*) { string insieme; // testo da cui estrarre valori float SomTot=0.0; // somma dei numeri int c=0; // contatore numeri float m; insieme.assign(buf->text()); /*2*/ if(!insieme.empty()){ while(!insieme.empty()){ SomTot += NumEstratto(insieme); /*3*/ c++; } m = SomTot/c; media->value(m); /*4*/ } valInp->take_focus(); /*5*/ } // estrae un numero da una stringa float NumEstratto(string &s) { float numero; string sNum; int pos; pos = s.find('\n'); /*6*/ sNum = s.substr(0,pos); /*7*/ numero = atof(sNum.c_str()); /*8*/ s = s.erase(0,pos+1); /*9*/ return numero; }
Poiché si dovranno svolgere elaborazioni sulle stringhe da trasformare in numeri ecc.. conviene (1) includere la classe string e trasformare (2) in stringa C++ type il contenuto del buffer.
Finché nella stringa ci sono caratteri, si estrae (3) un numero e si aggiunge al totale. Dopo aver effettuato il calcolo della media il risultato (4) viene inviato alla casella di output e si restituisce il focus alla casella di input (5).
Per l'estrazione di un numero dalla stringa contenuta nel buffer, prima si cerca il carattere di new line (6), poi si estraggono i caratteri che lo precedono (7) e si trasformano (8) in numero. Alla fine si eliminano tutti i caratteri fino al new line (9). In questo modo la stringa sarà vuota quando tutti i numeri saranno stati estratti.