Sviluppo

Come faccio? - parte XIV

Gianni Ceccarelli
 

stesura originale in inglese di Eric Slaats - liberamente tradotto da Gianni Ceccarelli
comparso per la prima volta su Os/2 e-Zine! volume 3 numero 1

Salve a tuti. Cosa faremo questo mese? Penso di aver scritto abbastanza nello scorso anno per mettere il lettore medio un po' più a suo agio nello scrivere qualcosa di più interessante. Stiamo per scrivere la prima applicazione! (ZIP, 22K) Sì, in questo e nei prossimi articoli scriveremo un'applicazione realmente funzionante. Con questo mese avremo le funzioni di base. Basi che comunque saranno pienamente funzionanti.

Ho ricevuto molti suggerimenti su questa applicazione (grazie).Mi piacerebbe usarne alcuni nel futuro, ma per ora useremo solo controlli semplici. Questo mese cominceremo a scrivere una semplice calcolatrice (mi manca da OS/2 2.1). Vedrete quanto è semplice. Contiene in pratica solo due tipi di controlli: una casella di testo e un mucchio di pulsanti. Con un occhio ai prossimi mesi, includeremo anche un MLE. Il tutto apparirà all'incirca così:

Calculator Screenshot Dopo la lezione precedente dovrebbe essere abbastanza facile da creare. Una calcolatrice ha alcuni aspetti fastidiosi che possono dare ai programmatori dei gran mal di testa. Molti possono essere risolti essendo furbi e pigri (vi ricordate di fare le cose semplici?). Lasceremo che il dialog faccia gran parte del lavoro per noi.

1) Se lasciamo che l'utente scriva quello che vuole nella casella di testo, dovremo scrivere un mucchio di codice per gestire i messaggi WM_CONTROL, per evitare che scriva lettere o altri caratteri che non vogliamo. Beh, come risolviamo? Semplice: non permettiamo all'utente di scrivere nella casella. Fatela di sola lettura nel dialog editor (usate lo stile ES_READONLY).

2) Questo è stato abbastanza semplice. Ma come fa l'utente a inserire i numeri? Altrettanto semplice: usa i pulsanti. Hmmmm, non molto soddisfacente. Mi piacerebbe usare la tastiera per inserire i numeri e fare i calcoli. Assumiamo ci sia già il codice che mette i numeri dai pulsanti nella casella di testo. Sono gestiti anche il meno e il punto decimale (ci arriviamo tra poco). Come possiamo far usare la tastiera all'utente? In un articolo precedente ho citato i tasti di scelta rapida. Sono le lettere sottolineate in una voce di menu, premendo le quali si può scegliere la voce. Possiamo fare lo stesso con un pulsante: basta mettere una tilde (~) davanti al carattere che volete usare. Se lo facciamo per tutti i pulsanti, l'utente potrà usare la tastiera. Questo attiverà i pulsanti, e il codice associato a questi metterà il testo nella casella.

Cosa abbiamo ottenuto? Un semplice filtro per i tasti. L'utente può immettere nella casella di testo solo i caratteri per cui abbiamo creato i pulsanti (con tanti saluti al codice speciale per filtrare l'input).

Fatta questa mossa, ci manca il codice per gestire i pulsanti. Ma prima di occuparcene, decidiamo un po' come dovrà funzionare internamente la calcolatrice. Ho definito le due semplici regole seguenti:

1) la calcolatrice usa due sole variabili di tipo double

2) una tiene il primo membro dell'operazione, l'altra il secondo.

Ora pensiamo al codice per i pulsanti. Ci sono alcune cose che merita prendere in considerazione per scrivere questo codice. Prima di tutto, ciascun pulsante fa, in principio, la stessa cosa. Quindi vogliamo scrivere il codice una sola volta e applicarlo ad ogni messaggio WM_COMMAND generato da un pulsante numerico. Come facciamo? Per fortuna il blocco switch/case che usiamo per gestire i messaggi risolve già una parte del problema.

Se lo scriviamo così:

switch (number)

        {

        case 1:

        case 2:

        case 3:

                AZIONI

        break;

        

        case 4:

        case 5:

        case 6:

                AZIONI

        break;

        }

quando il numero è 1,2 o 3 saranno eseguite le stesse azioni. Stessa cosa per 4,5 e 6. Quindi possiamo scrivere quella parte del gestore di WM_COMMAND del dialog in modo che per tutti i pulsanti numerici vengano eseguite le stesse linee di codice.

Prima di pensare a cosa deve fare quel codice, dobbiamo trovare un modo semplice per capire quale pulsante sia stato premuto. C'è il piccolo problema che abbiamo appena deciso di usare lo stesso codice per tutti i pulsanti, per cui non possiamo usare codice diverso per i vari pulsanti. Di nuovo, essere pigri e pensare semplice ci aiuta (assieme al dialog editor). Il trucco è di assegnare a ciascun pulsante un identificatore pari al valore che il pulsante stesso rappresenta. Quindi il pulsante 0 avrà ID 0, il pulsante 1 avrà ID 1 ecc. Ciò si riflette nelle seguenti linee del file header.

        #define BUTTON1         1

        #define BUTTON2         2

        #define BUTTON3         3

        #define BUTTON4         4

        #define BUTTON5         5

        #define BUTTON6         6

        #define BUTTON7         7

        #define BUTTON8         8

        #define BUTTON9         9

        #define BUTTON0         0

Se i pulsanti numerici usano questi ID possiamo sapere quale è stato premuto. Risolto questo, possiamo pensare a cosa fare quando un pulsante viene premuto. Dobbiamo tenere conto di diverse cose.

1) il numero premuto deve essere aggiunto alla casella di testo

2) il punto 1 non è sempre vero. Se il valore nella casella di testo è il risultato di un calcolo precedente, dovremmo svuotare la casella prima di inserire il numero perché sta iniziando un nuovo calcolo.

Per il punto 2 facciamo la seguenti ipotesi: quando viene eseguito un calcolo, il risultato va nel membro di sinistra. Quindi:

        RightMember (operazione) Leftmember = Leftmember

In questo modo possiamo aggiugnere, sottrarre ecc. un nuovo numero al risultato precedente (e questo è un comportamento che di sicuro vogliamo da una calcolatrice).

Risolviamo i problemi uno alla volta. Dobbiamo aggiungere il numero premuto alla casella di testo. Ricordate che il contenuto della casella è una stringa di caratteri, e il valore del pulsante è un numero. Come lo convertiamo? Il modo più semplice è convertire il numero in un carattere e concatenarlo alla stringa della casella di testo. Possiamo farlo con sprintf. Questa è una funzione standard C che crea una stringa. Ricordate di includere <stdio.h>. La stringa per contenere il numero deve essere lunga almeno 2 caratteri, poiché ogni stringa contiene un NULL come terminatore. Quindi il valore occupa il primo carattere, il terminatore occupa il secondo.

A questo punto possiamo concatenare il numero al contenuto della casella di testo. Per il momento, assumiamo che quest'ultimo sia contenuto nella stringa achValue (ci torneremo in un attimo). Per concatenarle useremo la funzione strcat. Questa è un'altra funzione standard del C (ricordate di includere <string.h>).

char achButton[2];

sprintf(achButton, "%d", SHORT1FROMMP(mp1));

strcat(achValue, achButton);

WinSetDlgItemText(hwndDlg, ENTRYFIELD1, achValue);

Ora che ci siamo occupati del punto 1, proseguiamo. Il secondo punto non è così difficile da implementare. Sappiamo che se un risultato è stato calcolato, sta nel membro di sinistra. In questo caso, il membro di destra dovrebbe contenere 0 (ce ne occuperemo dopo). Quindi il secondo punto si risolve così:

if (flRightMember == 0)

        strcpy(achNumber, "");

In questo modo il numero premuto verrà concatenato ad una stringa vuota, e sarà quindi la prima cifra nella casella di testo. A questo punto dobbiamo inserire nel membro di destra un valore diverso da 0, per sapere che è stato inserito qualcosa. Tutto questo porta al codice seguente:

//-------------------------------------------------------------

// Gestisci i pulsanti numerici

//-------------------------------------------------------------

case BUTTON1:   case BUTTON2:   case BUTTON3:

case BUTTON4:   case BUTTON5:   case BUTTON6:

case BUTTON7:   case BUTTON8:   case BUTTON9:

case BUTTON0:

        {

        if (flRightMember == 0)

                strcpy(achNumber, "");



        char achButton[2];

        sprintf(achButton, "%d", SHORT1FROMMP(mp1));

        strcat(achNumber, achButton);

        WinSetDlgItemText(hwndDlg, ENTRYFIELD1, achNumber);

        flRightMember = 1;      // impostiamo il membro di destra

        }

return(0);

C'è un'altra cosa da inserire nella casella (del segno meno ce ne occuperemo tra poco): il punto decimale. Dovremmo permettere di inserire il punto solo se non ce n'è già uno. Possiamo usare ancora una funzione dichiarata in string.h, strchr. Questa funzione ritorna TRUE quando un dato carattere è presente in una data stringa. Questo porta al codice seguente per gestire il messaggio WM_COMMAND per l'ID BUTTONDOT.

if (!strchr(achValue, '.'))

        {

        strcat(achValue, ".");

        WinSetDlgItemText(hwndDlg, ENTRYFIELD1, achValue);

        }

Prima di pensare a come gestire i pulsanti di azione come '-', '+', '/' e '*' vediamo come gestire un'azione. Oltre a doverla calcolare alla pressione di un pulsante di azione, dobbiamo farlo anche alla pressione del pulsante '='. Immaginate il caso seguente: inseriamo un 10, seguito da un '+', poi un 5, e infine un '-'. Alla pressione del '-', dobbiamo calcolare il '+'. Quindi dobbiamo ricordare l'ultima azione premuta. Per questo introduciamo la variabile LastAction. Così quando dobbiamo fare un calcolo, sappiamo quale in base al valore di LastAction. Possiamo definire una funzione basata su questo. Sappiamo che il risultato deve essere messo nel membro di sinistra, abbiamo quindi la seguente procedura:

//---------------------------------------------------------------------------------

// Calcola il risultato in base all'azione precedente

//---------------------------------------------------------------------------------

void processlastaction( void )

        {

        switch (chLastAction)

                {

                case '-': flLeftMember -= flRightMember; break;

                case '+': flLeftMember += flRightMember; break;

                case '*': flLeftMember *= flRightMember; break;

                case '/': flLeftMember /= flRightMember; break;

                }

        }

Quando viene premuto un pulsante di azione, dobbiamo prima vedere se il membro di sinsitra è vuoto. Nel caso, copiamo il valore del membro di destra in quello di sinistra, azzeriamo il membro di destra e ricordiamo l'azione nella variabile LastAction. In questo modo quando il membro di destra ha un valore e premiamo '=' possiamo calcolare il risultato.

Quando il membro di sinistra contiene un valore valido e premiamo un tasto di azione, il risultato deve essere calcolato e messo nel membro di sinistra. Di nuovo il membro di destra deve essere azzerato (ricordate il codice che gestisce i pulsanti numerici!)

Dopo aver gestito l'azione precedente, dobbiamo inserire il valore dell'ultima azione in LastValue. Abbiamo il seguente codice:

//-------------------------------------------------------------

// Gestisce i codice di azione (+, -, /, *)

//-------------------------------------------------------------

case BUTTONSUB:

case BUTTONADD:

case BUTTONMUL:

case BUTTONDEV:

        {

        flRightMember = atof(achValue);         // Prendi il valore dalla casella di testo



        if (flLeftMember == 0)

                {

                flLeftMember  = flRightMember;

                flRightMember = 0;

                WinSetDlgItemText(hwndDlg, ENTRYFIELD1, "");

                }

        else

                {

                //---------------------------------------------------

                // gestisci azione precedente

                //---------------------------------------------------

                processlastaction( );

                flRightMember = 0;

                sprintf(achValue, "%f", flLeftMember);

                WinSetDlgItemText(hwndDlg, ENTRYFIELD1, achValue);

                }

        //--------------------------------------------------------

        // ricorda nuova azione

        //--------------------------------------------------------

        switch (SHORT1FROMMP(mp1))

                {

                case BUTTONSUB: chLastAction = '-'; break;

                case BUTTONADD: chLastAction = '+'; break;

                case BUTTONMUL: chLastAction = '*'; break;

                case BUTTONDEV: chLastAction = '/'; break;

                }

        }

return(0);

C'è un'eccezione ai pulsanti di azione. Quando viene premuto un '-', può essere che si voglia inserire un valore negativo. Questo è vero solo se il '-' viene premuto come primo carattere. Nel caso, dobbiamo inserire il '-' come carattere nella casella di testo e impostare il membro di destra con qualche valore per essere sicuri che la pressione di un tasto numerico non lo cancelli. Otteniamo il codice seguente:

//--------------------------------------------------------

// Imposta il segno negativo

//--------------------------------------------------------

if (flRightMember == 0)

        {

        WinSetDlgItemText(hwndDlg, ENTRYFIELD1, "-");

        flRightMember = 1;      // Impostato il membro di destra

        return(0);

        } 

Ricordate che abbiamo detto prima che il contenuto della casella di testo è copiato nella stringa achValue. Visto che ne abbiamo bisogno per gestire la pressione di quasi ogni tasto, la impostiamo all'inizio della gestione del messaggio WM_COMMAND:

//------------------------------------------------------------------

// Copia il valore della casella di testo e lo mette in achValue

//------------------------------------------------------------------

char achValue[32];

WinQueryDlgItemText(hwndDlg , ENTRYFIELD1, 32, achValue);

Siamo quasi alla fine. Cosa resta? I tasti '=', 'C' e 'CA'. Questi ultimi due li lascio alla vostra vivida immaginazione (date un'occhiata all'esempio). Il tasto '=' richiede un po' di attenzione. Dopo un '=' il risultato non dovrebbe essere usato per il calcolo successivo (se vogliamo fare calcoli in cascata, basta premere uno dei tasti di operazione).

Perciò dopo un '=' dobbiamo azzerare tutti i valori interni della calcolatrice e mostrare il risultato.

//-------------------------------------------------------------

// Gestisce il pulsante '='

//-------------------------------------------------------------

case BUTTONEQ:

        {

        flRightMember = atof(achValue); // recupera valore dalla casella di testo

        proceslastaction( );                    // esegui azione precedente

        sprintf(achValue, "%f", flLeftMember);

        WinSetDlgItemText(hwndDlg, ENTRYFIELD1, achValue);

        //-----------------------------------------------------

        // Azzera

        //-----------------------------------------------------

        flRightMember = 0;                              // Il membro di destra diventa 0

        flLeftMember  = 0;                              // Il membro di sinistra 0

        chLastAction  = ' ';                    // Nessuna azione precedente

        }

return(0);

Bene, sono contento... Non c'è poi molta programmazione PM, ma abbiamo posto una buona base per la prima applicazione di "Come Faccio?". Per ora abbiamo una calcolatrice funzionante (con molto da migliorare!).

Il mese prossimo vedremo come migliorare l'interfaccia. Possiamo farla molto più bella, e ricordate, una bella interfaccia rende molto più divertente usare l'applicazione. Renderemo utilizzabile anche il foglio dei risultati (il controllo MLE in alto sulla calcolatrice).

Fatemi sapere cosa ne pensate. Inoltre, ogni idea per miglioramenti è la benvenuta.


[Pagina precedente] [Sommario] [Pagina successiva]