Numero 17 del 2020
Titolo: Gli script di Jaws 05. Il Flusso dello script.
Autore: Abramo Volpato.
Articolo:
Gli script di Jaws 05. Il Flusso dello script.
Di Abramo Volpato.
5.1. Ancora un po' di tecnica.
Le strutture di controllo, che analizzeremo in questo capitolo, sono un altro pilastro su cui si basano le tecniche di programmazione. Come nel capitolo precedente, almeno per supportare la parte teorica della spiegazione, si è attinto qua e là a quanto scritto nel già citato manuale di Kenneth A. Gould.
Le informazioni fornite sono state comunque reinterpretate, cercando di renderle più facilmente assimilabili anche da chi non avesse esperienza al riguardo. Dopo una prima parte più dedicata alla teoria, daremo poi come al solito ampio spazio agli esercizi pratici.
Tra questi, proporremo uno script tanto semplice quanto efficace. Questo codice, che andrà a leggere alcuni dati relativi alle proprietà di file e cartelle, rappresenta un po' il modello del tipo di script che ciascuno di noi potrà essere in grado di realizzare, leggendo e sperimentando queste pagine.
Nella seconda parte del capitolo analizzeremo i cicli, un sistema molto potente per far girare Jaws in modo analogo a quello che fanno le applicazioni su cui lavora. Anche qui, prima un po' di teoria, poi anche una parte pratica, nella quale prima miglioreremo uno script appena realizzato, poi ne creeremo uno di nuovo.
Prima di iniziare l'argomento vero e proprio, tuttavia, inauguriamo un appuntamento che d'ora in avanti sarà fisso agli inizi di ciascun capitolo.
5.1.1. Aggiornare i file personali.
Lo scorso capitolo abbiamo creato i nostri file personali, dedicati ciascuno a contenere i tre elementi accessori agli script:
1. "Costanti".
2. "Variabili Globali".
3. "Messaggi".
D'ora in poi, ad ogni inizio capitolo, come primi esercizi sarà chiesto di aggiornare i diversi file personali, inserendovi i contenuti che servano agli script da realizzare nel capitolo stesso.
Per questa prima volta daremo delle istruzioni dettagliate, mentre nei successivi capitoli le daremo man mano per acquisite.
Esercizio 5.1.2. Aggiornare il file Personale delle Costanti.
Iniziamo, in ordine alfabetico, dal file personale delle costanti. Quindi, seguite questi passi:
1. Richiamate l'Editor di Script, non importa da quale applicativo, e premete "Control+O" per far visualizzare la finestra dei file per l'Utente.
2. Premete Tab una volta per andare nella selezione dei tipi di file, e scegliete "File Header (*.jsh)".
3. Tornate all'elenco dei file, con tre colpi di Shift+Tab, e premete una volta il carattere Sottolineato, "_".
4. Dovreste essere posizionati sul file desiderato, "_PsConst.JSH", quindi premete Invio per aprirlo.
5. Qui, portatevi verso la fine del file, sull'unica costante sin qui impostata, "INIZIO_PERSONALE". Prima del carattere Punto e Virgola, inserite un carattere Virgola, e poi andate al termine del file assicurandovi di essere in una riga vuota.
6. Qui inserite le costanti che seguono, dotate di commento così da poterne illustrare lo scopo:
MINUTO = 60, ; numero dei secondi in un minuto
ORA = 3600, ; numero dei secondi in un'ora
_H = "H", ; carattere omonimo maiuscolo
MATTINO = 4, ; orario di inizio del mattino
POMERIGGIO = 14, ; orario di inizio del pomeriggio
SERA = 19, ; orario di inizio della sera
NOTTE = 23, ; orario di inizio della notte
PIPE = "|", ; carattere omonimo, Ascii 124
SHIFT_F10 = "Shift+F10", ; Combinazione tasti omologa
SECONDO = 10, ; numero dei decimi che formano un secondo
MAX_DECIMI = 200, ; numero dei decimi di secondo di durata massima di un ciclo
LF = "\10", ; carattere di avanzamento riga, Ascii 10
NULLO = ""; stringa vuota
7. Salvate il file e chiudetelo con "Control+F4", per tornare all'Editor.
5.1.3. Note sulle costanti inserite.
Dato che siamo agli inizi, stiamo trattando delle costanti che rappresentano un modello di quelle che troveremo anche più avanti. Quindi, possiamo dire che d'ora in poi seguiremo queste regole:
1. Quando si deve creare una costante che contenga soltanto una lettera, si userà la lettera stessa, ovviamente in maiuscolo, così come prevede lo standard per le assegnazioni delle costanti, facendola tuttavia precedere da un carattere di Sottolineato per garantirne la compatibilità con eventuali variabili locali formate da un solo carattere. In questo modo, da un lato si rende leggibile il codice, dall'altro si evita di porvi le virgolette.
2. Per registrare le combinazioni di Tasti nelle costanti, si userà la combinazione stessa in lettere maiuscole, sostituendo il carattere Più, "+", con il carattere Sottolineato, "_".
3. Dato che l'ultima delle costanti appena impostate è piuttosto neutra, "NULLO", simboleggiando la stringa vuota, si consiglia di mantenerla d'ora in poi come ultima del file, inserendo tutte le altre nuove da qui in avanti appena prima di essa. Si potrà così risparmiare l'onere di dover aggiornare di volta in volta l'ultima costante, che va lasciata senza carattere Virgola finale.
4. La costante "LF", come anticipato nel commento, sta per "LineFeed", (AvanzamentoRiga), che corrisponde al codice Ascii 10. Per capire meglio il perché di questa assegnazione, seguite il prossimo argomento.
5.1.4. Assegnare caratteri Speciali alle costanti.
Nello scorso capitolo, quando abbiamo trattato delle Costanti, avevamo detto che in questi elementi di memorizzazione si possono usare solo assegnazioni classiche, con numeri per quelle numeriche e testo per quelle testuali.
Se però quello che vogliamo registrare è un carattere non inseribile direttamente, ed in particolare quelli di controllo con codice Ascii inferiore a 32, dobbiamo assegnare alla costante una speciale stringa testuale delimitata da una coppia di virgolette. Qui dentro, si dovrà utilizzare il carattere Controbarra, "\", ponendo immediatamente dopo ad esso un valore numerico corrispondente al codice Ascii desiderato, così come abbiamo fatto nella costante "LF"impostata in precedenza.
Jaws, incontrando questa speciale sequenza, la convertirà nel corrispondente carattere Ascii del valore numerico rilevato.
In alcuni casi, sarà possibile porre anche una lettera dopo il carattere Controbarra, anziché un numero, per ottenere tale conversione, secondo lo schema di assegnazioni riportato qui di seguito:
AASCII7 = "\A",
ASCII8 ="\B",
ASCII9 ="\T",
ASCII10 ="\N",
ASCII11 ="\V",
ASCII12 ="\F",
ASCII13 ="\R",
Quindi, nel caso della costante da noi impostata, avremmo potuto anche usare questa assegnazione:
LF = "\N", ; carattere di avanzamento riga, Ascii 10
Un'ultima annotazione sull'argomento: se invece volessimo far si che JAWS assegnasse ad un testo tra virgolette il carattere Controbarra, come per esempio nell'indicare un percorso, o le stesse virgolette, che sono già utilizzate per l'assegnazione delle stringhe testuali, si potrà porre, al posto del valore numerico o della lettera speciale, una seconda Controbarra, oppure lo stesso carattere virgolette, come nei seguenti due esempi di assegnazione:
PERCORSO = "C:\\ProgramData\\Freedom Scientific\\JAWS",
FRASE = "In questo frammento memorizzo un \"testo Tra Virgolette\"",
Esercizio 5.1.5. Aggiornare il file Personale dei Messaggi.
La procedura da seguire è del tutto analoga alla precedente. In particolare:
1. Aprire la finestra dei file Utente allo stesso modo, selezionando però stavolta il tipo "File messaggi (*.jsm)".
2. Poi, tornati nella casella elenco, la pressione del carattere Sottolineato dovrebbe far selezionare il nostro file, "_PsMessages.JSM". In pratica, selezionando di volta in volta il tipo di file desiderato, il lavoro di selezione si riduce al premere Sottolineato una volta, per i file delle costanti e dei messaggi, e due volte per quello delle variabili Globali, che ha la stessa estensione di quello delle costanti ma lo segue in ordine alfabetico.
3. In ogni caso, una volta aperto il nostro file dei messaggi, portatevi verso il termine del file, sulla riga contenente l'istruzione "EndMessages", prima della quale inserite le assegnazioni che poniamo di seguito:
; base per lettura tempo dai minuti
@msgMinuti
Tempo trascorso: %1 minuti, %2 secondi e %3 centesimi.
@@
; base per lettura tempo dalle ore
@msgOre
Tempo trascorso: %1 ore, %2 minuti e %3 secondi.
@@
; elenco dei saluti
@lstSaluti
Buongiorno|Buon pomeriggio|Buonasera|Buonanotte
@@
; riga corrente vuota
@msgRigaVuota
La riga corrente è vuota!
@@
; riga corrente vuota - versione corta
@msgRigaVuota_corto
Vuota!
@@
; invito all'attesa
@msgAttesa
Attendere, prego.
@@
; termine di ricerca per lo spazio utilizzato nelle unità disco
@msgUtilizzato
Spazio utilizzato
@@
; termine di ricerca per lo spazio disponibile nelle unità disco
@msgDisponibile
Spazio disponibile
@@
; termine di ricerca per la capacità delle unità disco
@msgCapacita
Capacità
@@
; termine di ricerca dei dati sulle dimensioni di file e cartelle
@msgDimens
Dimensioni
@@
; termine di ricerca del contenuto delle cartelle
@msgContenuto
Contenuto
@@
; termine di ricerca per i dati sulla modifica dei file
@msgModifica
Ultima modifica
@@
; dimensioni non trovate
@msgNoDimensioni
Non trovo le dimensioni.
@@
; dimensioni non trovate - versione corta
@msgNoDimensioni_corto
Nessuna dimensione.
@@
; dichiarazioni di apertura nel codice
@lstApreScript
; Include "HJConst.JSH"; ; file costanti predefinito di Jaws
Include "_PsConst.JSH"; file personale delle costanti
Include "_PsGlobals.JSH"; file personale delle variabili globali
Include "_PsMessages.JSM"; file personale dei messaggi
@@
; numero righe inserite
@msgMettiRighe
%1 righe inserite.
@@
5.1.6. Il prefisso nei nomi dei Messaggi.
Se ricordate, avevamo detto che essendo i messaggi sempre di tipo testuale, l'eventuale prefisso usato nel definirne il nome era del tutto libero. Per questo, esso può anche essere sfruttato per determinare una sorta di "categoria dei messaggi", che da sola ce ne indichi il possibile contenuto, evitando di dover digitare qualche carattere in più.
Così, nelle assegnazioni di questo e dei prossimi capitoli, oltre al classico "msg", che sta ovviamente per "Messaggio", potrete trovare anche:
1. "lst", che sta per "lista", il quale indica un elenco di voci poste su un'unica riga, e in questo caso separate da un carattere speciale, oppure poste su più righe.
2. "ttl", che sta per "Titolo", il quale indica una stringa da usare come titolo in una finestra di dialogo.
***
5.2. Tipi di flusso.
Dopo questa prima parte dedicata ai file esterni, che come detto diventerà un passaggio obbligato agli inizi operativi di ciascun capitolo, affrontiamo dunque la parte più teorica dell'argomento sui flussi negli script.
Iniziamo facendo notare che gli script sinora proposti erano eseguiti in modo del tutto lineare, dalla prima all'ultima istruzione. Si può dire che essi sono stati un'eccezione, dettata dall'esigenza di renderli facilmente comprensibili a chi iniziava.
In realtà, il "flusso degli Script", ovvero il movimento di un ipotetico cursore che segue le righe di codice eseguite, può essere di tre tipi:
1. "Sequenziale", che è quello adottato sinora, il più semplice e facile da capire dei tre, quando in uno script sono eseguite tutte le istruzioni, dalla prima all'ultima.
2. "Condizionale", che è quello subordinato al verificarsi di determinate condizioni vere o false, le quali decidono come un vigile di deviare il flusso per una via o per l'altra, oppure di fermare l'esecuzione dello script.
3. "Iterativo", in cui le istruzioni contenute nello script, o solo una parte di esse, dopo essere state eseguite una prima volta tornano indietro compiendo un ciclo e ripetendosi poi finché una determinata condizione risulti vera.
Uno script può avere al suo interno uno qualsiasi, oppure due, o persino tutti e tre, di questi tipi di flusso. Avendo già conosciuto il primo tipo, in questo capitolo ci dedicheremo agli altri due, iniziando da quello "Condizionale", che è di gran lunga il tipo più usato dei tre. Prima di entrare nel dettaglio, tuttavia, c'è bisogno di analizzare dei concetti per noi indispensabili, che ciascuno deve conoscere per poter proseguire il nostro viaggio.
5.2.1. Espressioni, operatori, Condizione.
Con "Espressione", in queste pagine si indica in generale un singolo dato, oppure il risultato di una funzione, o di un'operazione matematica, che viene posta in relazione ad un'altra espressione tramite un operatore . Ad esempio, se noi avessimo da sommare due coppie di numeri controllando se la prima delle due somme ottenute è minore della seconda somma, potremmo tradurre in questa forma:
(1+2) < (3+4)
In questo caso, "(1+2)", è la prima espressione, il segno di "Minore" è invece l'operatore, mentre "(3+4)" è la seconda espressione.
In questo caso, le due espressioni assieme all'operatore formano una "Condizione".
Questa condizione, a sua volta, sulla base del fatto che la relazione tra le due espressioni sia vera oppure no, produce un risultato, "Vero" o "Falso", appunto.
In inglese, e soprattutto nella programmazione, questi due termini si traducono in "True" per Vero, e "False" per il suo contrario.
Nel caso degli Script per Jaws, tali termini non sono in realtà delle semplici parole, bensì delle costanti, definite dentro al file delle costanti predefinito, "HJConst.JSH", e che hanno nell'ordine i valori 1 e 0. In altre parole, quando dentro ad uno script si incontra il termine "TRUE", esso indicherà un valore 1, mentre quando si troverà il termine "FALSE", esso starà ad indicare un valore 0.
Dato che pure noi abbiamo un file delle costanti, potremmo a nostra volta impostare due costanti, "VERO" e "FALSO", che corrispondano a 1 e 0. Tuttavia, visto che non guadagneremmo nulla nel numero di caratteri, e poco riguardo alla leggibilità, almeno in questo caso continueremo ad usare i termini originali in inglese. Oltretutto, come analizzeremo più avanti, non sarà sempre necessario specificare tali termini per esteso, dandoli anzi il più delle volte per impliciti.
Ora, spiegato cosa si intende per "Espressione", e che cosa si debba ritenere una "Condizione", resta da illustrare nel dettaglio cosa sono gli "Operatori", iniziando dal tipo più comune.
5.2.2. Gli Operatori di Confronto.
Nello schema proposto di seguito, ad ogni operatore sarà dedicato un nuovo sottotitolo, seguito da una descrizione testuale, una spiegazione ed un breve esempio di utilizzo.
Alcuni di questi sono di uso comune, e le spiegazioni potranno sembrare inutili. Fate comunque più attenzione agli esempi, che rivelano come essi creino i risultati delle condizioni in cui sono posti.
Operatore di Uguaglianza. ==
Descrizione. Due segni di uguale insieme.
Spiegazione. Chiede se la prima espressione è uguale alla seconda espressione. Cioè se l'espressione alla sinistra dei due segni di uguale è equivalente all'espressione alla loro destra.
Esempio. La condizione A == B chiede se A è uguale a B.
Operatore di diversità. !=
Descrizione. Un punto esclamativo e un segno di uguale insieme.
Spiegazione. Chiede se la prima espressione è "non uguale" alla seconda espressione. Cioè se l'espressione alla sinistra del punto esclamativo e del segno di uguale è diversa dall'espressione alla loro destra.
Esempio. La condizione A != B è vera se A è diverso da B, altrimenti è falsa.
Operatore di Minoranza. <
Descrizione. Un segno "minore di".
Spiegazione. Chiede se la prima espressione è minore della seconda espressione. Cioè se l'espressione alla sinistra del segno < è minore dell'espressione alla sua destra.
Esempio. La condizione A < B è vera se A è minore di B, altrimenti è falsa.
Operatore Minore o Uguale. <=
Descrizione. Un segno "minore di" seguito da un segno di uguale.
Spiegazione. Chiede se la prima espressione è minore o uguale alla seconda espressione. Cioè se l'espressione alla sinistra dei segni <= è minore o uguale all'espressione che si trova alla loro destra.
Esempio. La condizione A <= B è vera se A è minore o uguale a B, altrimenti è falsa.
Operatore di Maggioranza >
Descrizione. Un segno "maggiore di".
Spiegazione. Chiede se la prima espressione è maggiore della SecondaEspressione. Cioè se l'espressione alla sinistra del segno > è maggiore dell'espressione alla sua destra.
Esempio. La condizione A > B è vera se A è maggiore di B, altrimenti è falsa.
Operatore Maggiore o Uguale. >=
Descrizione. Un segno "maggiore di" seguito da un segno di uguale.
Spiegazione. Chiede se la prima espressione è maggiore o uguale alla seconda espressione. Cioè se l'espressione alla sinistra dei segni >= è maggiore o uguale all'espressione alla loro destra.
Esempio. La condizione A >= B è vera se A è maggiore o uguale a B, altrimenti è falsa.
5.2.3. Il flusso Condizionale.
Il tipo di flusso che nella premessa abbiamo denominato "Condizionale", negli script per Jaws coincide con la struttura di controllo "If"-"Then", (Se) - (Allora), la quale è certamente la più diffusa in tutti i principali linguaggi di programmazione.
Il suo funzionamento, come accennato in precedenza, è simile all'azione di chi deve dirigere il traffico, deviando il flusso dell'esecuzione per le une o le altre vie che siano inserite nello script, sulla base di determinate condizioni, impostate nel codice o che si verificano durante il suo funzionamento.
Quando negli script per Jaws si scrive o si trova un'istruzione "If", si apre un controllo che, come in tutte gli altri casi che esamineremo, prevede anche una sua fine, che in questo caso è appunto decretata dalla parola chiave "EndIf", (FineSe).
Analizzate la struttura posta di seguito:
If PrimaEspressione () == SecondaEspressione () ; se le due espressioni coincidono,
Then; allora, la condizione viene valutata come vera,
ComandoInterno (); e si esegue quindi l'istruzione contenuta nella struttura di controllo
EndIf; qui finisce la struttura, ed il flusso passa comunque alla riga successiva,
UlterioreComando (); dove in ogni caso viene eseguita l'istruzione
I commenti dovrebbero aver chiarito come funziona questa struttura di controllo:
1. Il flusso arriva nella riga dove incontra il comando iniziale, "If",seguito dalle due espressioni, siano esse delle nostre funzioni, così come è ipotizzato nell'esempio, oppure variabili locali o globali, o anche delle costanti.
2. Il flusso procede fino all'altro comando, "Then", e dove viene analizzata la condizione, costituita dalle due espressioni confrontate tra loro tramite l'operatore di uguaglianza, "==".
3. Nel caso in cui la condizione fosse valutata come vera, allora il flusso entra nella struttura di controllo, dove esegue l'istruzione che è posta al suo interno.
4. In questo caso, sia che la condizione fosse stata vera, sia che fosse risultata falsa, il flusso arriva comunque al comando di chiusura "EndIf", passando oltre ed uscendo quindi dalla struttura di controllo ed eseguendo l'istruzione che la segue immediatamente.
Per completare quanto sin qui detto, bisogna chiarire come l'istruzione "Then", pur essendo un elemento distinto del controllo, di prassi viene posta sulla stessa riga dell'istruzione "If", soluzione che quindi adotteremo d'ora in poi.
5.2.4. Interrompere il flusso.
Per quanto semplice, questo tipo di struttura, con un solo controllo nella sequenza "If"-"Then"-"EndIf", si incontra spesso negli script originali di Jaws. A dire la verità, in molti di quei casi all'interno della struttura viene posto un fondamentale comando, "Return", (Ritorno), che interrompe in quel punto il flusso dello script.
Nell'esempio precedente avevamo chiarito, infatti, che sia in caso di condizione vera, sia nel caso contrario, il flusso dello script sarebbe comunque continuato. Utilizzando questo nuovo comando, invece, si può determinare un diverso sviluppo per le varie e possibili vie dello script, come potete analizzare nella struttura seguente:
If PrimaEspressione () == SecondaEspressione () Then; se le due espressioni coincidono,
ComandoInterno (); si esegue quindi l'istruzione contenuta nella struttura di controllo
Return; ma qui il flusso si interrompe definitivamente
EndIf; Pertanto, qui il flusso arriva solo nel caso in cui non sia entrato nella struttura,
UlterioreComando (); e questa istruzione viene eseguita solo se la condizione è falsa
Assieme a quelli posti in precedenza, crediamo che i commenti di questo esempio possano aver fatto capire la differenza tra i due funzionamenti, grazie all'inserimento di questo speciale comando.
Come detto, l'uso di "Return" è un sistema molto usato, perché consente di limitare l'azione del codice allo stretto necessario, in modo molto semplice. Quando analizzeremo nel dettaglio come realizzare delle nostre funzioni, questa parola chiave assumerà un ruolo ancora più importante.
Nel frattempo, concludiamo l'analisi di questo tipo di controlli inserendo gli altri elementi della struttura
5.2.5. Condizioni Alternative.
Quando una sola condizione non basta per svolgere i controlli richiesti da un nostro codice, una struttura di controllo può essere dotata di altri due possibili elementi:
1. "ElIf", (AltroSe), con la quale è possibile porre altre condizioni da verificare.
2. "Else", (Altrimenti), che accoglie il flusso se tutte le precedenti condizioni si rivelano false.
Riprendendo l'esempio precedente, questo è il loro possibile utilizzo:
If PrimaEspressione () == SecondaEspressione () Then; se le due espressioni coincidono,
PrimoComandoInterno (); si esegue quindi l'istruzione iniziale
ElIf TerzaEspressione () != QuartaEspressione () Then; se invece queste due sono diverse,
SecondoComandoInterno (); si esegue la seconda istruzione
Else; altrimenti, se nessun’altra condizione è vera,
UltimoComandoInterno (); si esegue l'istruzione alternativa alle altre
EndIf; qui finisce la struttura, ed il flusso passa comunque alla riga successiva
Anche qui i commenti dovrebbero essere sufficienti a chiarire come lavora la struttura di controllo. Va soltanto fatto notare che:
1. Il comando "If" può essere usato una sola volta per ciascuna struttura di controllo, alla prima condizione da verificare.
2. Di comandi "ElIf", invece, ve ne possono essere qualsiasi numero, purché posti appunto a partire dal secondo elemento in poi. Una struttura di controllo può anche concludersi con uno di questi comandi.
3. Anche il comando "Else" può essere usato una sola volta, ma in questo caso deve essere necessariamente posto come ultimo elemento di ciascuna struttura.
4. Sia con "If", sia con "ElIf", è possibile usare qualsiasi tipo di condizione, con qualsiasi espressione separata da qualsiasi operatore . Per questo, nella seconda condizione della struttura di esempio abbiamo usato l'operatore di diversità, "!=".
5. Se vi fosse una struttura come quella dell'esempio appena illustrato, sia che risultasse vera la prima condizione, sia che fosse vera la seconda, il flusso dopo aver eseguito l'istruzione passerebbe comunque alla riga successiva il comando "EndIf". Per evitare questo , è necessario servirsi dell'istruzione "Return", come nell'esempio di struttura riportato di seguito:
If PrimaEspressione () == SecondaEspressione () Then; se le due espressioni coincidono,
PrimoComandoInterno (); si esegue quindi l'istruzione iniziale,
Return; e si interrompe il flusso
ElIf TerzaEspressione () != QuartaEspressione () Then; se invece queste due sono diverse,
SecondoComandoInterno (); si esegue la seconda istruzione,
Return; e si interrompe il flusso
Else; altrimenti, se nessun’altra condizione è vera,
UltimoComandoInterno (); si esegue l'istruzione alternativa alle altre,
Return; e si interrompe il flusso
EndIf; qui il flusso non giungerà mai, perché esso si fermerà comunque prima
6. Ancora sull'istruzione "Return", essa deve essere necessariamente posta come nell'esempio, e quindi appena prima di un elemento intermedio o finale della struttura, "ElIf", "Else" o "EndIf". Qualsiasi altra posizione causerebbe un errore in fase di compilazione.
5.2.6. Miglioriamo i risultati sul tempo trascorso.
Dopo questa necessaria parte teorica, riprendiamo quella pratica partendo dal correggere un lavoro svolto nel capitolo precedente.
Se ricordate dal collaudo, o se avete avuto modo di utilizzarlo nel frattempo, il nostro script "TempoTrascorso ()" ha l'evidente limite di mostrare solo i secondi intercorsi dall'avvio del sistema. Se questo è assolutamente normale per conteggi inferiori al minuto, dopo i sessanta secondi l'aspetto comincia a diventare un problema.
Nel momento in cui lo avevamo realizzato non potevamo fare di meglio, in quanto ancora non conoscevamo le strutture di controllo. Ora, quindi, possiamo finalmente aggiungere quelle parti che operano, appunto, un controllo preventivo sul tempo intercorso dall'avvio del cronometro, decidendo quale tipo di messaggio andrà visualizzato.
Partiremo ovviamente dallo script già realizzato, intervenendo solo in quella parte dedicata al messaggio da pronunciare. Dato che ci siamo, cambieremo anche la modalità di esprimere quest'ultimo, usando la funzione "SayFormattedMessage ()", che abbiamo analizzato al termine dello scorso capitolo.
Esercizio 5.2.7. La versione finale di TempoTrascorso ().
FileScript. Default.JSS
Nome. TempoTrascorso
Novità.
1. La struttura di controllo "If"-"Then", che contiene anche le altre parole chiave "ElIf", "Else", "EndIf".
2. Le costanti "MINUTO" e "ORA", con valore rispettivamente di 60 e 3600, che corrispondono ai secondi dei due rispettivi intervalli di tempo.
3. La funzione integrata "SayFormattedMessage ()", che fonde "SayMessage ()" e "FormatString ()".
4. La costante nativa "OT_HELP", equivalente al valore 1, che serve a far pronunciare un messaggio dal tono normale.
5. La nostra costante "NULLO", equivalente ad una stringa vuota, quella prodotta da una coppia di caratteri Virgolette.
Fasi.
1. Dopo aver eseguito i calcoli di secondi e centesimi, una prima condizione, tramite "If", valuta se il numero di secondi sia inferiore al minuto, producendo in tal caso lo stesso tipo di messaggio già usato nella precedente versione.
2. Nella seconda condizione, con "ElIf", si valuterà invece se il tempo sia inferiore all'ora, ed in tal caso si dovranno calcolare anche i minuti. Sarà quindi aggiornato anche il numero dei secondi, prima di far pronunciare un messaggio che includa, oltre a secondi e centesimi, anche il nuovo dato sui minuti.
3. Nel terzo elemento della struttura, usando "Else", sarà convogliato il flusso che non preveda nessuna delle due precedenti condizioni. Qui saranno dapprima calcolate le ore, quindi i minuti residui oltre alle ore conteggiate, ed infine i secondi in eccesso rispetto ai minuti. Anche in questo caso, sarà necessario un nuovo messaggio che si prevede possa escludere i centesimi per ridurre i dati espressi a quelli più significativi.
Note.
1. Per realizzare la modifica, portatevi nelle dichiarazioni delle variabili, cancellate quella con "sMessaggio", ed inserite le due seguenti:
Int iMinuti, ; numero dei minuti trascorsi
Int iOre; numero delle ore trascorse
2. Scendete ora fino all'istruzione con "FormatString ()", e cancellate il contenuto da lì sino all'istruzione con "SayMessage ()" compresa.
3. Inserite la struttura di controllo che compare nella parte finale del codice qui sotto riportato, partendo dal primo comando "If",come al solito corredato dei necessari commenti.
Codice.
Script TempoTrascorso ()
Var
Int iValore, ; il tempo trascorso in millisecondi
Int iSecondi, ; i secondi trascorsi
String sUltimaParte, ; la parte finale dei millisecondi convertiti in testo
String sCentesimi, ; la stringa con i centesimi di secondo
Int iMinuti, ; numero dei minuti trascorsi
Int iOre; numero delle ore trascorse
Let iValore = GetTickCount () - gnTempoIniziale; sottrae il tempo di partenza all'attuale
Let iSecondi = iValore / 1000; converte il valore in secondi
Let sUltimaParte = StringRight (IntToString (iValore), 3); estrae le tre cifre a destra
Let sCentesimi = StringLeft (sUltimaParte, 2); preleva le prime due cifre a sinistra
If iSecondi < MINUTO Then; se il tempo trascorso è inferiore al minuto, legge l'avviso
SayFormattedMessage (OT_HELP, msgBaseTempo, NULLO, iSecondi, sCentesimi)
ElIf iSecondi < ORA Then; se invece siamo sotto all'ora,
Let iMinuti = iSecondi / MINUTO; calcola i minuti trascorsi,
Let iSecondi = iSecondi - (iMinuti * MINUTO); sottrae ai secondi quelli relativi ai minuti,
SayFormattedMessage (OT_HELP, msgMinuti, NULLO, iMinuti, iSecondi, sCentesimi);
Else; se invece siamo sopra all'ora di tempo trascorso,
Let iOre = iSecondi / ORA; calcola le ore trascorse suddividendo i secondi,
; quindi calcola i minuti dividendo i secondi rimasti dopo aver tolto quelli delle ore,
Let iMinuti = (iSecondi - (iOre * ORA)) / MINUTO
; e infine aggiorna i secondi togliendo quelli compresi nelle ore e nei minuti
Let iSecondi = iSecondi - ((iOre * ORA) + (iMinuti * MINUTO))
SayFormattedMessage (OT_HELP, msgOre, NULLO, iOre, iMinuti, iSecondi); legge l'avviso
EndIf; fine controllo tempo
EndScript
Collaudo.
1. Se la compilazione ha avuto buon esito, per testare il vostro lavoro iniziate col far partire il cronometro, premendo "Shift+Control+Windows+Invio".
2. Attendete qualche secondo, quindi premete i tasti di attivazione dello script, "Shift+Control+Invio", e vi saranno pronunciati come al solito i secondi ed i centesimi
3. Attendete circa un minuto, eseguite ancora lo script, e vi dovrebbero essere pronunciati anche i minuti, oltre ai secondi ed ai centesimi.
4. Non sarà necessario testare anche il funzionamento dopo i sessanta minuti, perché il conteggio anche con le ore dovrebbe essere comunque garantito. Se volete, e se avete il computer acceso da almeno un'ora, sarà possibile inserire all'inizio dello script, dopo le dichiarazioni delle variabili, la seguente istruzione:
Let gnTempoIniziale = 0
5. Dopo aver compilato, se premerete ancora una volta "Shift+Control+Invio", vi sarà pronunciato il tempo trascorso questa volta dall'avvio del sistema, e quindi iniziando dalle ore per poi leggere minuti e secondi, senza quindi i centesimi. Ovviamente, se fate questa prova, ricordatevi di cancellare l'istruzione appena aggiunta e ricompilare, per poter tornare ad essere voi ogni volta ad impostare il punto d'inizio dei conteggi tramite "Shift+Control+Windows+Invio".
5.2.8. Strutture di controllo annidate.
Vi è spesso la necessità che una struttura di controllo debba essere inserita dentro ad un'altra, quando vi siano più condizioni che vanno verificate in controlli successivi. In questo caso, come abbiamo analizzato per le funzioni, anche per le strutture si usa il termine "annidate".
In pratica, dopo la prima condizione "If"-"Then", la seconda viene verificata solo se la prima è vera.
Analizziamo lo schema seguente:
If PrimaCondizione () == TRUE Then; se la prima condizione è vera,
PrimoComando (); esegue la prima azione
If SecondaCondizione == TRUE Then; se anche la seconda condizione è vera,
SecondoComando; esegue la seconda azione
Else; altrimenti, se la seconda condizione è falsa,
SecondaAlternativa (); esegue il secondo comando alternativo
EndIf; fine secondo controllo
Else; altrimenti, se la prima condizione è falsa,
PrimaAlternativa (); esegue il primo comando alternativo
EndIf; fine primo controllo
Nelle strutture annidate, bisogna prestare molta attenzione al fatto che il controllo inserito dentro all'altro deve essere obbligatoriamente concluso con l'istruzione "EndIf" prima che possa riprendere il controllo iniziale della struttura.
In altre parole, la seconda condizione "If"-"Then" deve essere a sé stante, ed il controllo iniziale non deve subire alcun cambiamento, a prescindere dal fatto che al suo interno vi sia o meno un ulteriore struttura annidata.
Come esempio pratico, vi rimandiamo più avanti nel capitolo, dove i prossimi script avranno strutture annidate sino a tre livelli.
Un'annotazione importante: in programmazione, per orientarsi nei diversi livelli del flusso, si consiglia di porre un carattere, spesso di tabulazione, prima di strutture o istruzioni di livello inferiore. Questa tecnica si chiama "Indentazione", e di seguito poniamo lo schema appena proposto, dove però viene osservata tale regola:
If Condizione1 () == TRUE Then; se la prima condizione è vera,
PrimoComando (); esegue la prima azione
If Condizione2 == TRUE Then; se anche la seconda condizione è vera,
SecondoComando; esegue la seconda azione
Else; altrimenti, se la seconda condizione è falsa,
SecondaAlternativa (); esegue il secondo comando alternativo
EndIf; fine secondo controllo
Else; altrimenti, se la prima condizione è falsa,
PrimaAlternativa (); esegue il primo comando alternativo
EndIf; fine primo controllo
Pur essendo uno standard sul modo di proporre il codice, in questa opera divulgativa non faremo uso di questa tecnica, soprattutto per motivi di impaginazione. L'uso di caratteri suppletivi, infatti, rischia di far terminare a capo riga dei commenti che invece sarebbero più comprensibili se rimanessero sulla stessa riga del codice a cui si riferiscono.
Per ovviare a questo problema, si farà largo uso dei commenti ad ogni inizio e fine struttura di controllo, in modo da poterla individuare anche solo leggendo il codice riga per riga.
Un altro sistema per non creare troppa confusione, annidando il minor numero possibile di strutture, è quello largamente usato negli script originali di Jaws, dove si utilizza l'istruzione "Return" per concludere il flusso dopo una struttura "If"-"Then"-"EndIf".
A ciascuno, quindi, la libertà di usare l'indentazione oppure no. Già a partire dall'argomento successivo, ed ancor più Nel prossimo capitolo, analizzeremo i metodi per rendere il codice più semplice e leggibile, e quindi facendo diventare anche meno indispensabili queste tecniche per orientarci tra le nostre righe di codice.
***
5.3. Combinare più condizioni nello stesso controllo.
Quando una condizione "If"-"Then" non deve per forza essere posta dentro ad un'altra, ma è comunque necessario controllare due o più condizioni contemporaneamente, servono un
altro tipo di operatori, che adesso andremo ad analizzare. Grazie ad essi, sarà possibile creare qualsiasi tipo di condizione multipla, rendendo i nostri script molto più efficaci e precisi.
Come al solito, iniziamo l'argomento con una parte descrittiva e teorica, dove parleremo di un secondo tipo di operatori. Per ciascuno di essi, sarà proposta una descrizione estesa ed una spiegazione d'utilizzo.
5.3.1. Gli operatori logici.
Operatore di Congiunzione &&
Descrizione: Doppia Ampersand
Spiegazione: Viene collocato tra due o più condizioni per unirle nel loro risultato, creandone in pratica una sola, la quale risulterà vera soltanto se tutte le condizioni unite da questo operatore sono vere. Se solo una delle condizioni collegate è falsa, il risultato della condizione sarà falso.
Operatore di Disgiunzione. ||
Descrizione. Doppio Pipe.
Spiegazione. Viene collocato anch'esso tra due o più condizioni per unirle nel risultato creandone una sola. Qui la condizione, diversamente, per risultare vera basta che anche una sola delle condizioni unite da questo operatore sia vera. La condizione creata dal doppio Pipe sarà falsa solo se tutte le condizioni unite sono false.
Operatore di Negazione. ! oppure Not
Descrizione: Punto Esclamativo, oppure la parola chiave Not.
Spiegazione. Viene collocato all'inizio di una condizione per invertirne il risultato, quindi in una condizione vera facendola diventare falsa, e viceversa.
5.3.2. Estrarre più dati da una singola stringa.
Prima di passare alla prova pratica sugli operatori logici, apriamo una piccola parentesi per presentare un sistema molto utile di Jaws: ricavare dei dati prelevandoli direttamente da una speciale stringa testuale dotata di un carattere separatore.
Per far capire di cosa stiamo parlando, facciamo subito un esempio, analizzando il contenuto della riga seguente:
Buongiorno|Buon pomeriggio|Buonasera|Buonanotte
Come potete notare, essa comprende quattro formule di saluto, legate a vari momenti della giornata, separate tra loro dal carattere "Pipe", Ascii 124. Quest'ultimo carattere, tra l'altro, l'abbiamo appena incontrato come operatore logico di Disgiunzione, anche se in quel caso formato da una coppia di caratteri affiancati.
Data quella stringa con i quattro saluti, ipotizziamo di volerne estrarre uno, ad esempio il terzo, "Buonasera". Per svolgere tale compito Jaws ci offre la funzione "StringSegment", (SottostringheTestuali), che restituisce il dato testuale corrispondente al numero progressivo di quel dato all'interno dell'intera stringa.
Tale funzione ha tre parametri:
1. Il nome della stringa generale in cui sono presenti i diversi dati.
2. Il carattere usato come separatore tra i vari dati del testo.
3. Il numero progressivo corrispondente al dato da estrarre.
Così, avendo come obiettivo il terzo saluto, noi dovremmo esprimere questa istruzione, che per maggiore chiarezza abbiamo posto su più righe:
StringSegment ( ; estrae il dato
lstSaluti, ; parametro1, l'elenco dei dati
PIPE, ; parametro2, il separatore dei dati, sotto forma di costante
3); parametro3, il numero progressivo del dato da estrarre
Il risultato di tale funzione sarà il terzo saluto dell'elenco, che noi potremo quindi assegnare ad una variabile oppure, come nel caso del prossimo script, pronunciarlo direttamente ponendolo come parametro in una funzione di lettura.
Esercizio 5.3.3. Lo script Saluto.
FileScript. Default.JSS
Nome. Saluto
Sommario. Esprime un saluto.
Descrizione. Rileva l'ora corrente e la utilizza per scegliere quale saluto pronunciare.
TastiAttivazione. Alt+Windows+Invio
Novità.
1. La costante "_H", che contiene il relativo carattere in maiuscolo, come stringa di formattazione della funzione "SysGetTime ()" per estrarre l'ora corrente.
2. L'operatore logico di Congiunzione, il doppio Ampersand, "&&".
3. L'operatore logico di Disgiunzione, il doppio Pipe, "||".
4. Le costanti "MATTINO", "POMERIGGIO", "SERA" e "NOTTE", impostate, nell'ordine, con i valori 4, 14, 19 e 23. Esse sono utilizzate come limiti minimi e massimi per confrontare le varie espressioni.
5. La funzione "StringSegment ()", appena illustrata.
6. La costante "PIPE", corrispondente all'omonimo carattere Ascii 24, in questo caso usato come separatore dei dati nell'elenco.
Fasi.
1. Rileva l'ora corrente, tramite la funzione "SysGetTime ()".
2. Sottopone l'ora rilevata ad una struttura di controllo, che ne individua la fase tra mattino, pomeriggio, sera o notte, impostando un valore corrispondente alla fase. Stessa.
3. Ottiene il messaggio di saluto corrispondente al valore impostato, estraendolo da un elenco di dati tramite la funzione "StringSegment ()", e pronunciandolo con "SayMessage ()".
Note.
1. Nella struttura di controllo, le prime tre condizioni multiple, quelle con l'operatore di Associazione "&&", sono necessarie per poter intercettare il flusso ed impostare il valore relativo. La quarta, invece, è stata prevista soltanto per fare un esempio di utilizzo del l'operatore di Disgiunzione "||", poiché, essendo l'ultima possibilità rimasta, si sarebbe ottenuto lo stesso risultato anche ponendo al suo posto la sola istruzione "Else".
2. Il carattere separatore utilizzato per l'elenco dei dati, "PIPE", viene spesso usato anche da Jaws negli elenchi generati da script e funzioni. Pertanto, lo useremo anche noi come separatore predefinito, quando avremo da assemblare elenchi da cui estrarre dati.
Codice.
Script Saluto ()
Var
Int iOra, ; ora corrente
Int iValore; dato da estrarre
Let iOra = StringToInt (SysGetTime (_H)); rileva l'ora corrente
If iOra >= MATTINO && iOra < POMERIGGIO Then; se si è nel mattino,
Let iValore = 1; imposta il numero del dato da estrarre
ElIf iOra >= POMERIGGIO && iOra < SERA Then; se invece si è nel pomeriggio,
Let iValore = 2; imposta il dato relativo
ElIf iOra >= SERA && iOra < NOTTE Then; se si è in orario serale,
Let iValore = 3; imposta il dato
ElIf iOra >= NOTTE || iOra < MATTINO Then; se infine si è nell'orario notturno,
Let iValore = 4; imposta il dato
EndIf; fine controllo ora corrente
SayMessage (OT_HELP, StringSegment (lstSaluti, PIPE, iValore)); legge il saluto estratto
EndScript
Collaudo.
1. Se la compilazione va a buon fine, la pressione dei tasti di attivazione, "Alt+Windows+Invio", dovrebbe esprimere un saluto diverso nelle varie fasi della giornata. Se volete subito fare la prova per tutte le fasi possibili, aggiungete un'impostazione dell'ora alla funzione che la determina, come nell'esempio posto di seguito, il quale andrà poi aggiornato con i vari limiti d'orario stabiliti:
Let iOra = StringToInt (SysGetTime (_H, 14, 0, 0)); rileva l'ora corrente
2. Pur avendo in questo caso una validità solo dimostrativa, questo codice si può utilizzare per inserirlo in propri script come formula di analisi di opzioni multiple, non solo come formula di apertura. A tal fine, sarà sufficiente creare altri elenchi con dati diversi, ed altre condizioni per determinare quale dato di volta in volta estrarre.
***
5.4. Il confronto senza operatori.
Esiste un modo di esprimere le espressioni, che potremo definire "confronto implicito", il quale punta a conoscere se un dato, una variabile o una funzione, sia stato impostato o abbia un valore positivo, e comunque non nullo, senza tuttavia fare uso degli operatori. Si tratta di una modalità molto usata, sia nel caso di valori numerici, sia nel caso di quelli testuali, perché consente una notevole semplificazione nel codice.
Per realizzare un primo esempio di tale confronto, bisogna precisare che una buona parte delle funzioni di Jaws, che potremo chiamare "Informative", restituiscono:
- TRUE, cioè 1, qualora siano vere.
- FALSE, cioè 0, nel caso contrario.
Una di queste è la funzione "InTable", (DentroTabella), che serve proprio per sapere se il cursore attivo sia all'interno di una tabella, o di un foglio di calcolo.
Questa funzione, che non ha parametri, la inseriremo in un nostro elemento di codice tra qualche capitolo. Nel frattempo, ce ne serviremo soltanto per produrre alcuni esempi.
In particolare, se si volesse avere la conferma che ci si trova in una tabella, si può usare una forma estesa che preveda l'operatore di uguaglianza, "==", seguito dalla costante TRUE, come proposto di seguito:
If InTable () == TRUE Then
ma la prassi consiglia di dare l'operatore e la costante come implicite, utilizzando la seguente forma abbreviata:
If InTable () Then
In modo analogo, se noi volessimo invece avere la conferma sul fatto che invece non ci si trovi in una tabella, la forma estesa prevedrebbe l'operatore di uguaglianza seguito dalla costante FALSE, come proposto di seguito:
If InTable () == FALSE Then
In questo caso, alla forma senza operatore e costante di confronto bisogna anteporre l'operatore logico di "Negazione", il Punto Esclamativo, "!", come illustrato di seguito:
If !InTable () Then
Questi confronti che abbiamo definito "Impliciti", si possono usare non solo quando una funzione restituisce TRUE o FALSE, ma anche quando c'è da controllare del testo.
Nell'esempio che segue, ci serviremo della funzione integrata "GetLine", (OttieniRiga), che restituisce il contenuto della riga sul cursore attivo, la quale non ha parametri. Per il confronto useremo anche la costante "NULLO", che ricordiamo rappresenta una coppia di virgolette, simulando una stringa vuota.
If GetLine () != NULLO Then
La condizione proposta, nella sua forma contratta, è del tutto analoga a quella usata in precedenza:
If GetLine () Then
Allo stesso modo, nella forma estesa posta di seguito, la condizione controlla che davvero non ci sia del testo nella riga corrente:
If GetLine () == NULLO Then
Anche in questo caso, la sua forma è la stessa usata per la funzione sulla presenza nella tabella, con il nome della funzione preceduto dal Punto Esclamativo:
If !GetLine () Then
Quindi, ricapitolando, servirsi in modo implicito di operatori ed espressioni di confronto, usando sempre le forme contratte, rende molto più scorrevoli i nostri script, oltre a farci risparmiare alla fine una buona quantità di codice.
Proporremo tra poco degli script che prevedono degli esempi di controlli impliciti, sia in negativo che in positivo. Tali elementi di codice aggiungeranno una nuova funzionalità ai normali comandi di Jaws per copiare o aggiungere testo agli appunti, ma non sarà l'unica novità che vi troveremo.
5.4.1. Le versioni corte dei messaggi.
Lo scorso capitolo abbiamo trattato dei livelli di prolissità, e della loro applicazione nelle funzioni di lettura sulla base dei Tipi di Output. Da allora, abbiamo abbandonato le tradizionali funzioni che avevano solo un tipo di testo da leggere, per dedicarci a "SayMessage ()" ed a "SayFormattedMessage ()".
Rispettando la strategia di inserire le novità poco per volta, sinora non avevamo sfruttato la possibilità di specificare la versione corta di un messaggio, che in entrambe le funzioni appena citate va posta come terzo parametro. Sinora, infatti, o non avevamo specificato niente, nel caso di "SayMessage ()", oppure in questa posizione avevamo inserito la costante "NULLO", nel caso di "SayFormattedMessage ()".
Negli script originali di Jaws, per differenziare i nomi delle due versioni dei messaggi, si usa spesso lo stesso nome del messaggio, cui viene aggiunto un suffisso di una lettera preceduta dal carattere di sottolineatura. Nel dettaglio, tale suffisso è "_l" che sta per "Long", (Lungo), per la versione lunga, e "_s", che sta per "Short", (Corto), per l'altra versione.
Ad inizio capitolo, quando abbiamo aggiornato il file dei messaggi, avevamo ovviamente predisposto anche questa doppia versione. Poiché come detto i nomi da assegnare ai messaggi sono liberi, nel nostro caso abbiamo scelto di usare il nome intero per i messaggi lunghi, e lo stesso nome con il suffisso "_corto" per la seconda versione, sempre preceduto dal carattere Sottolineato.
Ecco l'estratto della nostra prima doppia assegnazione:
; riga corrente vuota
@msgRigaVuota
La riga corrente è vuota!
@@
; riga corrente vuota - versione corta
@msgRigaVuota_corto
Vuota!
@@
In generale , prevedere fin dall'inizio la presenza dei due possibili formati conviene comunque perché, quando certe informazioni diventano inutili, ci si può limitare a modificare i livelli di prolissità, passando da "Principiante" a "Intermedio", o anche ad "Avanzato", anziché essere costretti a modificare il contenuto dei messaggi nel codice.
Esercizio 5.4.2. Lo script CopiaRiga.
FileScript. Default.JSS
Nome. CopiaRiga
Sommario. Copia la riga corrente.
Descrizione. Verifica la presenza di contenuto nella riga sul cursore. Se non lo trova, pronuncia un avviso, altrimenti seleziona tale contenuto per copiarlo negli appunti di Windows tramite lo script nativo.
TastiAttivazione. Control+TastoJaws+FrecciaSu
Novità.
1. La funzione integrata "StringTrimLeadingBlanks", (TagliaDalTestoSpaziVuotiIniziali). Oltre allo scopo chiarito dalla traduzione, essa elimina anche gli spazi in eccesso eventualmente presenti nel corpo della stringa testuale, riducendoli ad uno soltanto. Il suo unico parametro è il testo da passare alla funzione per verificarlo, ed il risultato sarà lo stesso testo eventualmente modificato.
2. La funzione integrata "GetLine ()", già illustrata.
3. L'istruzione "Return", per la prima volta utilizzata in un nostro script.
4. La funzione integrata "JAWSEnd ()", già incontrata nel secondo capitolo, che porta il cursore a fine riga. Senza parametri.
5. La funzione integrata "SelectFromStartOfLine", (SelezionaDaInizioRiga), che serve appunto per selezionare la riga dove è posto il cursore attivo dal suo inizio sino al cursore stesso. Senza parametri.
6. Lo script nativo "CopySelectedTextToClipboard", (CopiaTestoSelezionatoNegliAppunti), quello che si attiva quando si preme la classica combinazione di copia "Control+C".
Fasi.
1. Una struttura di controllo verifica se non vi sia del testo nella riga sul cursore, servendosi delle funzioni annidate "StringTrimLeadingBlanks ()" e "GetLine ()". Se così è, il flusso entra nella struttura, viene pronunciato un messaggio d'errore tramite "SayMessage ()", ed il flusso viene quindi interrotto.
2. Se il flusso invece continua, viene dapprima spenta la sintesi, poi spostato il cursore a fine riga con "JAWSEnd ()", selezionato il contenuto della riga con "SelectFromStartOfLine ()", quindi riattivata la sintesi.
3. Per ridurre la possibilità che ci siano riletture inutili, della riga corrente o del testo selezionato , utilizziamo anche in questo caso la funzione integrata "Refresh ()".
4. Con il testo ora selezionato, viene eseguito lo script nativo "CopySelectedTextToClipboard ()", per copiare il testo negli appunti.
Note.
1. Nella struttura è posto un controllo implicito, che non abbisogna quindi di operatori relazionali per verificare la condizione. Poiché tale confronto è in negativo, allora si usa l'operatore logico di negazione, il Punto Esclamativo, davanti alla coppia di funzioni annidate che deve fornire o meno un risultato testuale.
2. Per selezionare il contenuto della riga si è scelto di spostare il cursore alla sua fine e da lì attivare la selezione dal suo inizio. Sarebbe stato possibile anche fare il contrario, poiché esistono ovviamente anche i comandi di Jaws per compiere l'azione opposta, ma si è scelto di fare questo per ridurre la possibilità che Jaws emetta un inutile segnale acustico, qualora lo si voglia spostare in un punto in cui il cursore si trovi già.
3. I tasti di attivazione proposti, "Control+TastoJaws+FrecciaSu", sono simili a quelli che si utilizzerebbero normalmente per leggere la riga corrente, in quanto si aggiunge a questi il solo tasto "Control".
Codice.
Script CopiaRiga ()
If !StringTrimLeadingBlanks (GetLine ()) Then; se nella riga non è presente del testo,
SayMessage (OT_HELP, msgRigaVuota, msgRigaVuota_corto); avvisa dell'errore,
Return; e interrompe il flusso
EndIf; fine controllo riga corrente
SpeechOff (); spegne la sintesi
JAWSEnd (); porta il cursore a fine riga
SelectFromStartOfLine (); seleziona la riga corrente
SpeechOn (); riattiva la sintesi
Refresh (); resetta la schermata
PerformScript CopySelectedTextToClipboard (); esegue lo script nativo
EndScript
Collaudo.
1. Dopo la sua compilazione positiva, entrate in un documento non vuoto, in un applicativo qualsiasi.
2. Posizionatevi in una riga senza contenuto, quindi provate a premere "Control+TastoJaws+FrecciaSu", e lo script dovrebbe informarvi che la riga è vuota.
3. Se invece vi posizionate in una riga con del testo, premendo i tasti di attivazione, dovrebbe esservi pronunciata la classica risposta di Jaws durante la copia negli appunti.
Esercizio 5.4.3. Lo script AggiungeRiga.
FileScript. Default.JSS
Nome. AggiungeRiga
Sommario. Aggiunge la riga corrente.
Descrizione. Verifica la presenza di contenuto nella riga sul cursore. Se non lo trova, pronuncia un avviso, altrimenti seleziona tale contenuto per aggiungerlo agli appunti di Windows tramite lo script nativo.
TastiAttivazione. Shift+Control+TastoJaws+FrecciaSu
Novità.
1. Lo script nativo "AppendSelectedTextToClipboard", (AggiungiTestoSelezionatoAgliAppunti), collegato alla combinazione di aggiunta agli appunti, "Windows+TastoJaws+C".
Note.
Le fasi e le annotazioni relative a questo script sarebbero in pratica le stesse di quelle proposte per lo script precedente, con la sola differenza del nome dello script nativo eseguito, che stavolta è quello di aggiunta e non di copia. Proprio per tale motivo, anche se l'esempio di utilizzo dei controlli impliciti è già stato fatto, si propone comunque di realizzarlo seguendo questi passi:
1. Copiate il contenuto dello script "CopiaRiga ()", portandovi al suo interno e premendo la scelta rapida "Control+R", per poi copiare il testo negli appunti.
2. Spostatevi al termine del file script ed incollate il contenuto copiato.
3. Portatevi sull'intestazione dello script appena copiato, e manualmente modificate il nome "CopiaRiga" con "AggiungeRiga".
4. Aprite la schermata di modifica, con "Control+D",premete subito "Alt+A" per portarvi sul campo dove inserire i tasti di attivazione, quindi premete la combinazione "Shift+Control+TastoJaws+FrecciaSu", ed infine Invio. A margine di questa indicazione, per lo stesso motivo esposto nell'altro script, la combinazione proposta è del tutto simile a quella dello script di copia, con l'aggiunta del tasto "Shift".
5. Tornati al file script, e di nuovo dentro al codice, portateti sull'istruzione con "PerformScript", e nel nome dello script richiamato sostituite il prefisso "Copy" con "Append".
6. Se avete dubbi circa le modifiche, controllate la forma proposta di seguito.
Codice.
Script AggiungeRiga ()
If !StringTrimLeadingBlanks (GetLine ()) Then; se nella riga non è presente del testo,
SayMessage (OT_HELP, msgRigaVuota, msgRigaVuota_corto); avvisa dell'errore,
Return; e interrompe il flusso
EndIf; fine controllo riga corrente
SpeechOff (); spegne la sintesi
JAWSEnd (); porta il cursore a fine riga
SelectFromStartOfLine (); seleziona la riga corrente
SpeechOn (); riattiva la sintesi
Refresh (); resetta la schermata
PerformScript AppendSelectedTextToClipboard (); esegue lo script nativo
EndScript
Collaudo.
Se la compilazione va a buon fine, anche il test per questo elemento può essere effettuato nello stesso modo in cui era stato illustrato per il precedente script.
***
5.5. Script del sistema operativo.
Oltre che nei vari applicativi che lavorano in Windows, anche nell'ambiente del sistema operativo vi sono delle funzionalità che sono migliorabili grazie agli script per Jaws. Una di queste è certamente la finestra "Proprietà" di file o cartelle, dove una volta entrati, il cursore PC offre poche possibilità di movimento, e gran parte delle informazioni le possiamo leggere entrando in esplorazione con il cursore Jaws.
Lo script che si propone per l'argomento, nella sua forma quasi integrale, presenta parecchie novità. Per questo, anticipiamo in queste righe le due più importanti, lasciando poi all'omonima sezione successiva il compito di analizzare nel dettaglio le restanti.
Innanzi tutto, in questo script si chiama la funzione "InvisibleCursor",(CursoreInvisibile), la quale attiva l'omonimo cursore, senza parametri. Se vi ricordate, quando avevamo parlato dei tipi di cursori di Jaws, il cursore Invisibile aveva la particolarità di spostarsi senza tirarsi dietro nemmeno il puntatore del mouse, che in questo script, appunto, non ci serve.
Proprio perché dovremmo muoverci con questo tipo di cursore, utilizzeremo anche la funzione collegata, "RouteInvisibleToPc", (AllineaCursoreInvisibileACursorePC), senza parametri, la cui traduzione ne illustra lo scopo.
Il cuore dello script è poi la funzione "FindString", (TrovaDatoTestuale), che cerca nello schermo un testo specificato come parametro. A proposito di parametri, questa funzione ne ha ben quattro, che illustreremo di seguito, nell'ordine in cui andranno inseriti, ciascuno con le necessarie note informative:
1. L'elemento dove cercare il testo, anticipando di qualche pagina l'argomento delle "Finestre", alle quali ci dedicheremo nel prossimo capitolo. Per il momento basterà dire che saranno usate due diverse funzioni, "GetCurrentWindow", (OttieniFinestraCorrente), che restituisce l'elemento identificativo della finestra dove si trova il cursore attivo, e "GetFocus", (OttieniFocus), che appunto restituisce la finestra dove è presente il focus del sistema.
2. Il testo da ricercare.
3. La direzione della ricerca, immettendo un valore di una costante da scegliere tra S_TOP, che ha il valore 0 e indica la ricerca dall'l'alto, oppure "S_BOTTOM", che ha valore 1 e che rappresenta la ricerca dal basso. Queste due costanti fanno parte di quelle presenti nel relativo file predefinito di Jaws, "HJConst.JSH".
4. L'ambito della ricerca, se limitarla all'elemento corrente, oppure se allargarla anche alle finestre di livello inferiore. Chiarendo che anche questo argomento sarà illustrato nel prossimo capitolo, la scelta sarà anche qui ancora tra due costanti, tratte sempre dal file predefinito, che sono: "S_UNRESTRICTED", dal valore 0, che estende la ricerca; "S_RESTRICTED", dal valore 1, che la restringe.
5.5.1. Predisponiamo il file script.
Il file dove
realizzare il nostro codice si chiama "ExplorerFrame.JSS", e si richiama premendo "TastoJaws+0" dentro ad una finestra di esplorazione delle Risorse di Windows.
Esso dovrebbe essere già dotato di propri script, ma noi è la prima volta che lo apriamo. Per questo, bisogna inizializzarlo, provando a salvarlo per compilarlo, prima di effettuare una qualsiasi modifica.
Se tutto è a posto, solo per scrupolo, scendete di qualche riga partendo dall'inizio del file per controllare che sia già presente la seguente istruzione:
Include "HJConst.JSH"
Se così fosse, andate al termine del file, ponete la nostra etichetta Personale con "Shift+Control+1", quindi inserite manualmente le altre tre dichiarazioni iniziali dei nostri script:
Include "_PsConst.JSH"; File personale delle costanti
Include "_PsGlobals.JSH"; file personale delle variabili globali
Include "_PsMessages.JSM"; file personale dei messaggi
Esercizio 5.5.2. Lo script ProprietaFileCartelle.
FileScript. ExplorerFrame.JSS
Nome. ProprietaFileCartelle
Sommario. Legge le proprietà di dischi, file e cartelle.
Descrizione. Fa pronunciare a Jaws le principali informazioni relative alle proprietà di dischi, file e cartelle, in modo diretto ed automatico.
TastiAttivazione. Alt+Invio
Novità.
1. La funzione "SaveCursor", (SalvaCursore), che appunto registra quale sia il cursore attivo e la sua posizione in quel momento sullo schermo. Senza parametri.
2. La costante "SHIFT_F10", che equivale alla scelta rapida omologa, che in questo caso attiva il menu contestuale.
3. La funzione "PriorLine", (RigaPrecedente), che sposta il cursore alla riga sopra. Avrebbe un parametro, che serve a specificare da quale periferica arrivi il comando di spostamento, ma in quanto opzionale si può lasciare il valore predefinito FALSE.
4. La funzione "Delay", (Ritarda), che serve principalmente a ritardare l'esecuzione dello script. A differenza di "Pause ()", questa funzione fa riprendere il flusso dello script dopo il periodo di tempo specificato, a prescindere dal fatto che l'applicazione sottostante stia ancora eseguendo i propri compiti. Essa possiede due parametri:
- Il primo, obbligatorio, serve ad impostare il numero dei decimi di secondo per i quali sospendere il flusso.
- Il secondo, opzionale, che se impostato con TRUE, disattiva alcune funzioni di lettura automatica, per non far leggere dati che non siano quelli richiesti.
5. Le costanti "SECONDO" e "TRUE", poste come parametri alla funzione appena citata, che equivalgono ai valori numerici 10 ed 1. Tali valori a loro volta corrispondono, nell'ordine, al numero di decimi che compongono l'omonima unità di tempo, ed al valore di confronto "Vero".
6. Le funzioni "InvisibleCursor ()" e "RouteInvisibleToPc ()", descritte nella premessa.
7. La funzione "FindString", illustrata nell'introduzione assieme agli altri elementi da essa usati, le funzioni "GetCurrentWindow ()" e "GetFocus ()", e le costanti "S_TOP" e "S_UNRESTRICTED".
8. La funzione "EscapeKey", (TastoEscape), la quale simula la pressione dell'omonimo tasto. Senza parametri.
9. La funzione "RestoreCursor", (ripristinaCursore), senza parametri, la quale riattiva il cursore salvato con l'ultima istruzione "SaveCursor ()", posizionandolo anche nel punto dove si trovava prima.
Fasi.
1. Dopo aver aperto la schermata delle proprietà, nello script dapprima si registra il cursore corrente, poi si passa a quello Invisibile.
2. Sono quindi eseguite in sequenza le varie operazioni di ricerca delle apposite stringhe, per riuscire a capire a quale tipo di elemento siano riferite quelle informazioni sulle proprietà, iniziando a cercare un termine presente solo nelle schermate relative ai dischi.
3. In caso positivo, cerca ancora altri tipi di dati. Se li trova tutti, interrompe lo script.
4. Se invece la ricerca dei dischi fallisce, si passa alla ricerca di cartelle o file dove, qualora si trovi l'elemento identificativo comune, si proseguirà a seconda che si tratti delle proprietà delle une o degli altri.
5. In ogni caso, una volta rilevata nello schermo la stringa voluta, il cursore Invisibile si sposta alla nuova destinazione, leggendo infine i dati nella riga corrente.
Note.
1. Se per realizzare il codice avviate la procedura di creazione script, va chiarito che nei nomi degli script non si possono specificare lettere accentate o caratteri speciali. Quindi, ad esempio il nome da dare a questo script, che riguarda la proprietà di file e cartelle, deve essere "ProprietaFileCartelle", con la "a" normale e non accentata. Questo particolare è importante perché la procedura di creazione guidata, di per sé, consentirebbe lo stesso di assegnare e di salvare uno script con caratteri non validi nel nome, ma il problema si presenterebbe solo al momento della compilazione, che causerebbe un errore proprio per questo motivo.
2. Come tasti attivazione, è da utilizzare la scelta rapida che apre la schermata delle proprietà in Esplora Risorse. Per questo, il suggerimento è utilizzare"Alt+Invio", anche se per chi utilizza Windows 10 potrebbe essere più ovvio servirsi di "Alt+1".
Codice.
Script ProprietaFileCartelle ()
SayMessage (OT_ERROR, msgAttesa); legge un messaggio di attesa
SpeechOff (); spegne la sintesi
SaveCursor (); salva tipo e posizione del cursore attivo
TypeKey (SHIFT_F10); attiva il menu contestuale
PriorLine (); sale di una riga
EnterKey (); simula la pressione di Invio
; sospende il flusso per il tempo indicato, disattivando alcune funzioni di lettura automatica
Delay (SECONDO, TRUE)
InvisibleCursor (); passa al cursore Invisibile
RouteInvisibleToPc (); porta il cursore Invisibile alla posizione di quello PC
; cerca un dato relativo alle unità disco, lo spazio utilizzato
If FindString (GetCurrentWindow (), msgUtilizzato, S_TOP, S_UNRESTRICTED ) Then
SpeechOn (); se lo trova, riattiva la sintesi,
SayLine (); e legge il dato rilevato
; qui cerca un termine per il dato sullo spazio disponibile
If FindString (GetFocus (), msgDisponibile, S_TOP, S_UNRESTRICTED ) Then
SayLine (); se lo trova, legge il dato rilevato
; qui cerca il dato sulla capacità dell'unità disco
If FindString (GetFocus (), msgCapacita, S_TOP, S_UNRESTRICTED ) Then
SayLine (); se lo trova, legge il dato rilevato,
EscapeKey (); poi simula la pressione del tasto Esc per uscire dalla schermata,
RestoreCursor (); e ripristina il cursore salvato
Return; solo se è stata rilevata la capacità dell'unità disco, interrompe lo script
EndIf; fine del terzo controllo, quello sulla capacità
EndIf; fine del secondo controllo, quello sullo spazio disponibile
EndIf; fine del primo controllo, quello sullo spazio utilizzato
; inizia la seconda serie di controlli, cercando le dimensioni di file e cartelle
If FindString (GetCurrentWindow (), msgDimens, S_TOP, S_UNRESTRICTED ) Then;se le trova,
SpeechOn (); riattiva la sintesi,
SayLine (); e legge il dato definitivo
; qui verifica se si tratti di una cartella, cercando un dato sul contenuto
If FindString (GetCurrentWindow (), msgContenuto, S_TOP, S_UNRESTRICTED ) Then
SayLine (); se così è, legge il dato rilevato
; se non vi sono dati per cartelle, cerca la data di modifica per i file
ElIf FindString (GetCurrentWindow (), msgModifica, S_TOP, S_UNRESTRICTED ) Then
SayLine (); se la trova, legge il dato rilevato
EndIf; fine controllo presenza dato sul contenuto
Else; se invece non sono stati rilevati dei dati sulle dimensioni,
SpeechOn (); riattiva la sintesi,
SayMessage (OT_HELP, msgNoDimensioni, msgNoDimensioni_corto); e legge l'avviso
EndIf; fine controllo presenza dati
EscapeKey (); simula la pressione del tasto Esc per uscire dalla schermata,
RestoreCursor (); e ripristina il cursore salvato
EndScript
Collaudo.
1. Entrate in "Esplora Risorse", e selezionate un qualsiasi elemento, disco, cartella o file.
2. Premete i tasti di attivazione che avete scelto per lo script, e le principali informazioni relative alle sue proprietà vi dovrebbero essere lette.
3. In particolare, Jaws dovrebbe limitarsi a leggere le informazioni, fermando le letture automatiche al termine dello script. Tali letture riprenderanno normalmente dopo aver premuto un qualsiasi ulteriore tasto.
Per quanto riguarda il funzionamento, dopo le note nella premessa ed i commenti nel codice, restano da evidenziare soltanto le diverse istruzioni per riattivare la sintesi, dopo quella iniziale dello script che la disattiva. Tali istruzioni sono poste quando Jaws deve riprendere a parlare, all'inizio delle tre possibili vie che può prendere il flusso dello script, che sono le seguenti:
1. Trovati i dati per un disco.
2. Trovati i dati per file o cartelle.
3. Nessun dato trovato.
***
5.6. Il flusso Iterativo.
Questo tipo di flusso è costituito da dei cicli in cui si eseguono le istruzioni poste all'interno di particolari strutture di codice, che si ripetono fino al verificarsi di una certa condizione. I tipi di ciclo che andremo ad analizzare sono due, che tratteremo in modo distinto producendo per ciascuno alcuni esempi di utilizzo.
5.6.1. Il ciclo While.
Un ciclo "While", (Mentre), è una sequenza di istruzioni che si ripetono quando una determinata condizione è vera, e finché tale condizione rimane vera.
Il ciclo è costituito da due parole chiave:
1. "While", che ne costituisce l'inizio, dopo di cui andranno specificate le condizioni che regolano il funzionamento e la durata del ciclo.
2. "EndWhile", (FineMentre), che fissa il punto di conclusione del ciclo.
Tutte le istruzioni all'interno di queste prime due verranno eseguite ripetutamente, finché le condizioni stabilite dopo "While" non siano più soddisfatte.
Quando il ciclo si interrompe, il flusso dello script invece continua, riprendendo dalla riga di codice successiva all'istruzione "EndWhile".
In generale, i cicli "While" si usano quando non si conosce il numero di ripetizioni che si devono compiere, o quando la condizione preveda il confronto di stringhe testuali, o di funzioni che abbiano comunque un ritorno testuale.
Per completare l'argomento, va detto che il solo pericolo nell'uso del ciclo While è che le condizioni poste per ripeterlo non cambino mai, facendo continuare la sua ripetizione all'infinito. In questo caso, si dice che il ciclo vada in "loop", (CicloContinuo), bloccando di fatto le funzionalità della sintesi vocale e del PC.
Per evitare questo pericolo, è necessario innanzi tutto studiare bene le condizioni da porre alla ripetizione, casomai aggiungendo più condizioni correlate tra loro tramite gli operatori logici.
Un altro sistema efficace è quello di inserire all'interno del ciclo un "contatore", una variabile che si aggiorna ad ogni ripetizione, e che raggiunto un determinato limite possa interrompere il suo lavoro.
Un'ultima avvertenza: all'interno di un ciclo "While", non si può usare la funzione "Pause ()", poiché anch'essa causerebbe un "loop", bloccando il pc ed impedendo a Jaws di proseguire. Se si dovesse avere la necessità di ritardare il flusso all'interno di un tale ciclo, si dovrà perciò usare "Delay ()", così come faremo tra poco.
5.6.2. Mettiamo un ciclo While dentro a ProprietaFileCartelle ().
Come esempio di utilizzo, riprendiamo in mano l'ultimo script realizzato, "ProprietaFileCartelle". La forma di quello script era stata lasciata incompleta, proprio per potervi intervenire ora, momento in cui possiamo usare i cicli While.
In quella versione dello script, il problema sorgerebbe quando ci si trova a dover leggere le proprietà di cartelle di grandi dimensioni, dell'ordine di decine o centinaia di Gigabyte, magari su supporti un po' datati. La lettura dei dati relativi alle dimensioni, in quei casi, non è immediata, ed a video il dato sulle dimensioni viene continuamente aggiornato, almeno finché non si stabilizza su quello definitivo.
Nella forma attuale dello script, in particolare, la lettura avviene una volta sola, all'ingresso nella schermata di riepilogo dati, e le dimensioni lette sono quelle che appaiono in quel momento.
Noi dovremo fare in modo che lo script sospenda la lettura del dato finché lo stesso non sarà stabile, e per fare questo ci serviremo proprio di un ciclo While.
Per evitare il pericolo delle ripetizioni infinite, di cui si accennava, doteremo il ciclo di un contatore che controlli il numero di ripetizioni e, con esse, il tempo massimo consentito di lavoro, che abbiamo fissato in circa 20 secondi nella costante MAX_DECIMI ad inizio capitolo. Qualora tale limite fosse superato,una struttura di controllo porrà quindi fine al ciclo.
Esercizio 5.6.3. La versione finale di ProprietaFileCartelle ().
FileScript. ExplorerFrame.JSS
Nome. ProprietaFileCartelle
Novità.
1. Aggiungere un ciclo "While"-"EndWhile" che effettui due letture della riga corrente a distanza di alcuni decimi di secondo, impostando i valori in due variabili. Il ciclo continuerà finché il contenuto delle due variabili testuali sarà diverso, e quindi terminerà quando, al contrario, le due variabili saranno uguali, oppure quando venisse superato un determinato numero di decimi trascorsi dal suo inizio.
2. La costante "MAX_DECIMI", equivalente al valore 200 che corrisponde ai venti secondi stabiliti come limite massimo per il funzionamento del ciclo.
Note.
1. Portatevi nella riga sotto all'intestazione dello script "ProprietaFileCartelle ()", ed inserite le dichiarazioni di variabili, ed una prima assegnazione, poste di seguito:
Var
int iAttesa, ; decimi di secondo per la sospensione
Int i, ; contatore del ciclo
String sLettura1, ; prima lettura del dato
String sLettura2; seconda lettura del dato
Let iAttesa = 5; imposta i decimi di intervallo tra una lettura e l'altra
2. Spostatevi ora sui controlli delle dimensioni, all'inizio della riga immediatamente sotto alla seguente istruzione:
If FindString (GetCurrentWindow (), msgDimens, S_TOP, S_UNRESTRICTED ) Then
3. Qui, prima dell'istruzione che riattiva la sintesi, inserite il commento ed il ciclo While che si pone di seguito, che ritroverete poi nel codice sottostante separato dal resto tramite delle righe vuote:
; avvia un ciclo che continua se la prima lettura è vuota oppure se i due dati restano diversi
While !sLettura1 || sLettura1 != sLettura2
Let i = i + 1; aggiorna il contatore
Let sLettura1 = GetLine (); aggiorna la prima lettura
; sospende il flusso per il tempo indicato, disattivando delle funzioni di lettura automatica
Delay (iAttesa, TRUE)
Let sLettura2 = GetLine (); aggiorna la seconda lettura
; se i decimi impostati, moltiplicati per le ripetizioni, superano il valore massimo,
If (iAttesa * i) > MAX_DECIMI Then
Let sLettura1 = GetLine (); unifica la prima lettura alla seconda, causando l'uscita dal ciclo
EndIf; fine controllo decimi trascorsi
EndWhile; fine del ciclo
4. Per il funzionamento del ciclo While, abbiamo dovuto impostare un certo numero di decimi che il ciclo stesso deve attendere prima di ripetersi, e questo ci consente di calcolare con buona approssimazione il numero di decimi trascorsi dall'inizio del suo lavoro. Per questo, la struttura inserita all'interno del ciclo può eseguire un controllo su questo dato, verificando che non si superi il limite massimo di decimi da noi fissato.
Codice.
Script ProprietaFileCartelle ()
Var
int iAttesa, ; decimi di secondo per la sospensione
String sLettura1, ; prima lettura del dato
String sLettura2, ;seconda lettura del dato
Int i; contatore del ciclo
Let iAttesa = 5; imposta i decimi di intervallo tra una lettura e l'altra
SayMessage (OT_ERROR, msgAttesa); legge un messaggio di attesa
SpeechOff (); spegne la sintesi
SaveCursor (); salva tipo e posizione del cursore attivo
TypeKey (SHIFT_F10); attiva il menu contestuale
PriorLine (); sale di una riga
EnterKey (); simula la pressione di Invio
; sospende il flusso per il tempo indicato, disattivando alcune funzioni di lettura automatica
Delay (SECONDO, TRUE)
InvisibleCursor (); passa al cursore Invisibile
RouteInvisibleToPc (); porta il cursore Invisibile alla posizione di quello PC
; cerca un dato relativo alle unità disco, lo spazio utilizzato
If FindString (GetCurrentWindow (), msgUtilizzato, S_TOP, S_UNRESTRICTED ) Then
SpeechOn (); se lo trova, riattiva la sintesi,
SayLine (); e legge il dato rilevato
; qui cerca un termine per il dato sullo spazio disponibile
If FindString (GetFocus (), msgDisponibile, S_TOP, S_UNRESTRICTED ) Then
SayLine (); se lo trova, legge il dato rilevato
; qui cerca il dato sulla capacità dell'unità disco
If FindString (GetFocus (), msgCapacita, S_TOP, S_UNRESTRICTED ) Then
SayLine (); se lo trova, legge il dato rilevato,
EscapeKey (); poi simula la pressione del tasto Esc per uscire dalla schermata,
RestoreCursor (); e ripristina il cursore salvato
Return; solo se è stata rilevata la capacità dell'unità disco, interrompe lo script
EndIf; fine del terzo controllo, quello sulla capacità
EndIf; fine del secondo controllo, quello sullo spazio disponibile
EndIf; fine del primo controllo, quello sullo spazio utilizzato
; inizia la seconda serie di controlli, cercando le dimensioni di file e cartelle
If FindString (GetCurrentWindow (), msgDimens, S_TOP, S_UNRESTRICTED ) Then;se le trova,
; avvia un ciclo che continua se la prima lettura è vuota oppure se i due dati restano diversi
While !sLettura1 || sLettura1 != sLettura2
Let i = i + 1; aggiorna il contatore
Let sLettura1 = GetLine (); aggiorna la prima lettura
; sospende il flusso per il tempo indicato, disattivando alcune funzioni di lettura automatica
Delay (iAttesa, TRUE)
Let sLettura2 = GetLine (); aggiorna la seconda lettura
; se i decimi impostati, moltiplicati per le ripetizioni, superano il valore massimo,
If (iAttesa * i) > MAX_DECIMI Then
Let sLettura1 = GetLine (); unifica la prima lettura alla seconda, causando l'uscita dal ciclo
EndIf; fine controllo decimi trascorsi
EndWhile; fine del ciclo
SpeechOn ();riattiva la sintesi,
SayLine (); e legge il dato definitivo
; qui verifica se si tratti di una cartella, cercando un dato sul contenuto
If FindString (GetCurrentWindow (), msgContenuto, S_TOP, S_UNRESTRICTED ) Then
SayLine (); se così è, legge il dato rilevato
; se non vi sono dati per cartelle, cerca la data di modifica per i file
ElIf FindString (GetCurrentWindow (), msgModifica, S_TOP, S_UNRESTRICTED ) Then
SayLine (); se la trova, legge il dato rilevato
EndIf; fine controllo presenza dato sul contenuto
Else; se invece non sono stati rilevati dei dati sulle dimensioni,
SpeechOn (); riattiva la sintesi,
SayMessage (OT_HELP, msgNoDimensioni, msgNoDimensioni_corto); e legge l'avviso
EndIf; fine controllo presenza dati
EscapeKey (); simula la pressione del tasto Esc per uscire dalla schermata,
RestoreCursor (); e ripristina il cursore salvato
EndScript
Collaudo.
Provate a salvare compilando, se non altro per accertarvi che il codice inserito sia corretto.
Quello che può far funzionare bene in particolare il ciclo While, è il numero di decimi che si assegnano alla variabile "iAttesa", che nell'esempio abbiamo impostato a 5. Poiché essa decide quanto tempo intercorra tra una lettura e l'altra dei dati a video, tale numero di decimi non deve essere troppo basso, perché rischia di far rilevare allo script due contenuti uguali anche prima della fine reale della scansione. D'altro canto, esso non può risultare neppure troppo alto perché, in tutti quei casi in cui il dato sia fin da subito quello definitivo, vi è comunque da attendere almeno una volta per i decimi di secondo impostati.
Per questo, il valore proposto di cinque potrebbe essere un giusto compromesso, che ciascuno potrà poi tarare sulla propria configurazione.
Il collaudo del sistema Anti-Loop.
Normalmente il ciclo While non fa sentire i suoi effetti, perché già al primo passaggio si conclude. A maggior ragione, il controllo anti ripetizioni infinite che abbiamo inserito potrebbe teoricamente neppure mai servire, anche perché ciò vorrebbe dire che un'operazione di lettura delle dimensioni sia durata più di venti secondi.
C'è però un modo molto semplice per simulare questo inconveniente, e testare comunque il nostro lavoro. Se volete provarlo, seguite questi passi:
1. Dentro a"ExploreFrame.JSS", portatevi all'interno dello script"ProprietaFileCartelle".
2. Qui posizionatevi all'inizio della riga contenente l'istruzione "sLettura1 = GetLine ()", e inseritevi un carattere Punto e Virgola, così da non far più eseguire quella riga di codice.
3. Salvate compilando, e lasciate aperto l'Editor.
4. Entrate in "Esplora Risorse", selezionate una cartella o un file, e premete i tasti di attivazione dello script.
5. Dopo il solito messaggio di attesa, dovrebbero passare circa una ventina di secondi senza che succeda nulla, quindi saranno letti i dati sulle proprietà dell'elemento selezionato. Questo è dovuto dal fatto che, avendo disabilitato l'assegnazione di una delle due stringhe testuali da confrontare, le due risulteranno quindi sempre diverse ed il ciclo andrebbe in "loop" se non ci fosse il nostro sistema, che appunto fa uscire dal ciclo dopo il tempo massimo stabilito per il suo funzionamento.
6. Per far tornare tutto come prima, sarà sufficiente cancellare il carattere Punto e Virgola appena inserito, e salvare nuovamente compilando.
5.6.4. I cicli For.
Un ciclo "For", (Per), è una sequenza di istruzioni che si ripetono per il numero di volte specificate, partendo da un valore numerico ed arrivando ad un altro.
Esso è costituito da due parole chiave: l'istruzione "For", dopo della quale è necessario stabilire il valore iniziale e finale delle ripetizioni, e l'altro termine "EndFor", (FinePer), che fissa il punto di conclusione del ciclo. Tutte le istruzioni all'interno dei confini di queste due parole chiave verranno eseguite ripetutamente, per il numero di volte stabilito.
In generale, i cicli For si usano quando si conoscono il numero di ripetizioni da far eseguire al codice in esso inserito, diversamente da quello che accade con i cicli "While".
Il valore iniziale deve essere impostato tramite una variabile che sarà dichiarata prima dell'inizio del ciclo, a livello locale o globale. Per impostazione predefinita, tale valore viene incrementato di una unità ad ogni ripetizione del ciclo, fino a raggiungere il valore finale. Quest'ultimo può essere impostato direttamente o appartenere ad una variabile, ed è separato da quello iniziale tramite la preposizione "To", che significa semplicemente (A).
Se dopo il valore finale si pone la parola chiave "Descending", (Discendente), il valore iniziale sarà invece diminuito di un'unità ad ogni ripetizione.
Analizzate ora il seguente schema:
Var Int i; contatore del ciclo
For i = 1 To 10; ripete il ciclo per 10 volte
EseguoUnComando ()
EndFor;qui il ciclo torna all'inizio, aggiungendo una unità al contatore i
In questo esempio di ciclo, la variabile contatore "i" viene aggiornata per dieci volte, tante saranno le volte che il comando contenuto nel ciclo sarà eseguito.
Quando, al decimo passaggio in cui il suo valore sarà incrementato di una unità, arrivando ad 11, essendo tale limite superiore al valore specificato dopo il termine "To", il ciclo si interrompe.
Il flusso dello script, invece, non si fermerà, e riprenderà a partire dalla riga successiva all'istruzione "EndFor".
Da notare che il contatore "i" all'uscita dal ciclo avrà l'ultimo valore impostato , cioè 11. Se servisse, potrebbe quindi essere usato come variabile numerica nel resto dello script.
Se invece la forma del ciclo fosse questa:
Var Int i; contatore del ciclo
For i = 10 To 1 Descending; ripete il ciclo per 10 volte all'indietro
EseguoUnComando ()
EndFor;qui il ciclo torna all'inizio, sottraendo una unità al contatore i
In questa versione, invece, il ciclo compie ancora dieci ripetizioni, stavolta però sottraendo ogni volta una unità al valore del contatore, anziché aggiungerla.
Quindi, in questo caso, al decimo passaggio il contatore sarà tornato a 0, valore inferiore del limite di 1, quindi il ciclo avrà termine.
Il flusso dello script, così come nel primo esempio, non si fermerà, e all'uscita il contatore rimarrà con il valore 0.
nel ciclo "For", sono molto ridotti i pericoli che vada in Loop, diversamente che nel ciclo "While", perché fin dall'inizio bisogna stabilirne un preciso limite.
Si dovrà soltanto evitare la modifica la variabile che funge da contatore all'interno del ciclo, nel nostro script di esempio quella composta da una sola lettera, la "i", azzerandone gli incrementi.
Così come con "While", anche all'interno di un ciclo "For" non si può usare la funzione "Pause ()", perché causerebbe il blocco del pc. Anche qui, dunque, si dovrà casomai servirsi di "Delay ()", per ritardare il flusso al suo interno.
Circa altri possibili inconvenienti, resta solo da dire che, se il valore finale fosse minore di quello iniziale, e non fosse stato impostato il verso discendente, il ciclo neppure partirebbe.
In conclusione, anticipiamo che il ciclo For ci tornerà utile in particolare per le funzioni personalizzate. Quando impareremo a costruircele, a partire dal prossimo capitolo, lo vedremo più volte tirato in ballo.
Nel frattempo, come esempio pratico, realizziamo un piccolo script, che può aiutarci in tutte le occasioni nelle quali si incontra un nuovo file script in cui poter operare. Esso, nel dettaglio, ci eviterà di scrivere manualmente le righe con le istruzioni per includere i file esterni con le impostazioni personali.
Esercizio 5.6.5. Lo script AperturaCodice.
FileScript. Default.JSS
Nome. AperturaCodice
Sommario. Inserisce le dichiarazioni del codice personale.
Descrizione. Scrive su un eventuale documento aperto le dichiarazioni con l'inclusione dei file personali per Variabili Globali, Costanti e Messaggi, precedute dall'analoga dichiarazione per il file costanti predefinito.
TastiAttivazione. Shift+Control+2
Novità.
1. Il ciclo "For"-"To"-"EndFor".
2. La funzione "StringSegmentCount", (ContaSottostringheTestuali), che restituisce un valore con il numero di sottostringhe, (Segment), presenti in una stringa nella quale il testo sia separato da un carattere speciale. Questa funzione ha due parametri:
- Il nome della stringa complessiva con il testo.
- Il carattere usato come separatore delle varie sottostringhe.
3. La costante "LF", corrispondente al carattere di Nuova Linea, Ascii 10.
Fasi.
1. Avviare un ciclo, da ripetere per il numero di sottostringhe presenti nella stringa testuale complessiva.
2. Ad ogni ripetizione del ciclo, scrivere nel documento corrente la sottostringa corrispondente al numero del contatore del ciclo.
3. Simulare la pressione di Invio, per creare una riga vuota, quindi sospendere per circa un decimo di secondo il flusso dello script, così da dare il tempo all'applicazione sottostante di scrivere materialmente il testo nel documento.
4. Al termine del ciclo, creare un'ulteriore riga vuota, quindi pronunciare in un certo tono un messaggio con il numero delle righe inserite, mentre con un altro tono leggere poi il contenuto della riga su cui si è fermato il cursore alla conclusione dello script.
Note.
1. Questo tipo di script consente di usare, come origine delle diverse sottostringhe, un'assegnazione nel file Messaggi in cui, molto semplicemente, ciascuna riga scritta nel file Messaggi diventerà una riga anche nel documento in cui si attiva lo script. Se volete, andate a controllare la penultima assegnazione nell'attuale forma del file Messaggi, dal nome "lstApreScript", e troverete in sequenza le quattro righe da trascrivere che corrispondono ad altrettante dichiarazioni "Include", nella forma già illustrata nel capitolo precedente, e che costituiscono, assieme all'etichetta di inizio codice Personale, le teoriche righe iniziali di ogni nostro nuovo file script.
2. Quanto citato al punto precedente, è reso possibile dal fatto che Jaws legge un'assegnazione nel file Messaggi come fosse un'unica stringa di testo. Se un'assegnazione è posta su più righe, ciascuna riga costituisce una sottostringa separata dalle altre tramite il carattere di controllo "LineFeed", l'Ascii 10, che Jaws interpreta appunto come carattere di fine riga nei file di testo. Tale carattere di controllo è stato da noi preventivamente assegnato alla costante "LF" nel nostro file personale delle costanti, potendolo così ora indicare come separatore nelle due funzioni citate, "StringSegmentCount" e "StringSegment".
3. Circa i tasti di attivazione, si è consigliato di usare "Shift+Control+2", se non altro, per abbinarla a quella che usiamo per inserire nei documenti il codice di inizio personale, "Shift+Control+1". Solitamente, quando si vorrà inizializzare un nostro file script, si potranno anche usare una dietro l'altra tutte e due queste combinazioni: l'averle tenute separate, in ogni caso, serve a distinguerne gli utilizzi, perché non sempre sarà necessario servirsi di entrambe.
4. Per quel che riguarda la presenza dell'inclusione del file costanti predefinito, "HJConst.JSH", ricordiamo che essa in genere non è indispensabile, e per questo la si è prevista con la protezione di un carattere Punto e Virgola ad inizio riga. Qualora la compilazione dello script desse un errore proprio relativo alla mancanza di una costante predefinita, come ad esempio quelle poste come primo parametro nelle funzioni "SayMessage ()", sarà sufficiente togliere il carattere di protezione e ricompilare.
Codice.
Script AperturaCodice ()
Var
Int i, ; contatore del ciclo
String sRiga; singolo segmento da scrivere
SpeechOff (); spegne la sintesi
; esegue un ciclo, ripetendolo per il numero di segmenti nella stringa
For i = 1 To StringSegmentCount (lstApreScript, LF)
Let sRiga = StringSegment (lstApreScript, LF, i); estrae il testo equivalente ad una riga,
TypeString (sRiga); e lo trascrive nel documento
EnterKey (); simula la pressione di Invio, per creare una nuova riga
Pause (); sospende temporaneamente lo script
EndFor; fine ciclo
EnterKey (); crea un'ulteriore riga vuota
SpeechOn (); riattiva la sintesi
SayFormattedMessage (OT_ERROR, msgMettiRighe, NULLO, i); legge l'esito
Pause (); sospende momentaneamente il flusso
SayLine (); legge la riga corrente
EndScript
Collaudo.
1. Quando la compilazione va a buon fine, per provare l'efficacia del nostro lavoro, aprite il file script per il Blocco Note, "Notepad.JSS".
2. Una volta entrati, portatevi sulla nostra etichetta di inizio codice, poi scendete fino a dove comincia l'intestazione del primo script da noi realizzato, quindi testate lo script, premendo "Shift+Control+2".
3. Se tutto funziona, dovrebbero essersi inserite le righe con le dichiarazioni iniziali, e Jaws dovrebbe avervi letto, con un certo tono, "5 righe inserite.", oppure, "5 inserite.", a seconda di quale lunghezza di messaggi sia attiva, poiché quattro sono le righe di dichiarazioni ed una quella vuota che abbiamo voluto aggiungere. Quindi, con un'altra tonalità, dovrebbe esservi stata letta la riga nel codice dalla quale avete eseguito lo script.
4. Per concludere la modifica, se il file script conteneva già del codice originale di Jaws, controllate che l'inclusione del file costanti Predefinito sia stata prevista tra le sue righe iniziali. Altrimenti, tornate alla prima dichiarazione, cancellate il carattere Punto e Virgola predisposto ad inizio riga, e compilate nuovamente.
***
5.10. Riepilogo.
Un'altra bella dose di tecnica, ma ormai siamo a buon punto. La "cassetta degli attrezzi" è quasi completata, e con il prossimo capitolo l'acquisizione degli strumenti principali potrà dirsi conclusa.
Ci manca prima di tutto il fondamentale tema delle finestre, e dei controlli che le riguardano, uno dei cardini su cui gira il funzionamento di Jaws, nel suo difficile ruolo di rendere accessibili i programmi del PC.
Poi, le prove pratiche legate alle finestre ci porteranno a capire come scrivere le funzioni, quelle definite dall'Utente, che tanta parte avranno nel lavoro da svolgere da qui in avanti.
5.10.1. Elementi di Codice Personale.
TempoTrascorso. File script: Predefinito. Shift+Control+Enter.
- Informa del tempo trascorso dall'attivazione del cronometro.
Saluto. File script: Predefinito. Alt+Windows+Enter.
- Esprime un saluto.
CopiaRiga. File script: Predefinito. Control+JAWSKey+UpArrow.
- Copia la riga corrente.
AggiungeRiga. File script: Predefinito. Shift+Control+JAWSKey+UpArrow.
- Aggiunge la riga corrente.
ProprietaFileCartelle. File script: Esplora Risorse. Alt+Enter.
- Legge le proprietà di dischi, file e cartelle.
AperturaCodice. File script: Predefinito. Shift+Control+2.
- Inserisce le dichiarazioni del codice personale.
5.10.2. Script, Funzioni e Comandi di Jaws.
StringSegment. (SottostringheTestuali).
- Restituisce il dato testuale che corrisponde alla posizione specificata, all'interno di un elenco composto da stringhe separate da uno speciale carattere.
InTable. (DentroTabella).
- Determina se il cursore attivo si trova all'interno di una tabella, o di un foglio di calcolo.
GetLine. (OttieniRiga).
- Restituisce il contenuto della riga sul cursore attivo.
StringTrimLeadingBlanks. (TagliaDalTestoSpaziVuotiIniziali).
- Rimuove dal testo specificato gli spazi posti all'inizio, riducendo ad uno anche eventuali spazi consecutivi posti al suo interno.
SelectFromStartOfLine. (SelezionaDaInizioRiga).
- Seleziona il testo dall'inizio della riga fino al cursore.
CopySelectedTextToClipboard. (CopiaTestoSelezionatoNegliAppunti).
- Copia il testo o gli elementi selezionati negli appunti.
AppendSelectedTextToClipboard. (AggiungiTestoSelezionatoAgliAppunti).
- Accoda il testo o gli elementi selezionati negli Appunti.
RouteInvisibleToPc. (AllineaCursoreInvisibileACursorePC).
- Riposiziona il cursore Invisibile, portandolo nel punto dove è posto il Cursore PC.
FindString. (TrovaDatoTestuale).
- Cerca una stringa di testo nella finestra specificata. Se il testo viene trovato, il Cursore JAWS viene collocato all’inizio della stringa di testo.
GetCurrentWindow. (OttieniFinestraCorrente).
- Determina l’handle della finestra che contiene il cursore attivo.
GetFocus. (OttieniFocus).
- Restituisce l’handle della finestra che ha il focus del sistema, a prescindere dalla posizione del cursore attivo.
SaveCursor. (SalvaCursore).
- Salva il cursore attivo e la sua posizione.
Delay. (Ritarda).
- Sospende il flusso dello script per il periodo indicato. Trascorso questo periodo, il flusso viene ripreso, a prescindere se le altre applicazioni abbiano completato i loro compiti.
EscapeKey. (TastoEscape).
- Simula la pressione di Esc.
RestoreCursor. (ripristinaCursore).
- Riattiva il cursore salvato e lo ripristina nella sua posizione originaria.
StringSegmentCount. (ContaSottostringheTestuali).
- Restituisce un valore con il numero di dati presenti in un elenco testuale, composto da stringhe separate da uno speciale carattere.
5.10.3. Voci di Glossario.
Variabili Globali.
- Variabili che possono essere usate da tutti gli elementi di codice che appartengono ai file script per le quali sono state dichiarate.
LineFeed. (AvanzamentoRiga).
- Carattere che corrisponde al codice Ascii 10.
msg.
- Prefisso per i nomi dei messaggi comuni.
lst.
- Prefisso per i nomi degli elenchi di voci, memorizzati tramite messaggi.
ttl.
- Prefisso per i nomi dei titoli delle finestre di dialogo, memorizzati tramite messaggi.
flusso degli Script.
- Movimento di un ipotetico cursore che segue il susseguirsi delle istruzioni nelle righe di codice.
Sequenziale.
- Tipo di flusso degli script quando sono eseguite tutte le istruzioni, dalla prima all'ultima.
Condizionale.
- Tipo di flusso degli script, subordinato al verificarsi di determinate condizioni vere o false, che deviano il flusso per una via o per l'altra, oppure che ne interrompono l'esecuzione.
Iterativo.
- Tipo di flusso degli script, nel quale le istruzioni contenute nello script, o solo una parte di esse, dopo essere state eseguite una prima volta tornano indietro compiendo un ciclo e ripetendosi poi finché una determinata condizione risulti vera.
Espressione.
- Un singolo dato, oppure il risultato di una funzione, o di un'operazione matematica, che viene posta in relazione ad un'altra espressione tramite un operatore.
TRUE. (VERO).
- Costante di Jaws che equivale al valore 1.
FALSE. (FALSO).
- Costante di Jaws che equivale al valore 0.
Operatori.
- Elementi matematici che mettono in relazione due o più espressioni, per ottenerne un risultato TRUE o FALSE.
If. (Se).
- Comando che avvia una struttura di controllo.
Then. (Allora).
- Elemento di chiusura della condizione, o della serie di condizioni, in una struttura di controllo.
EndIf. (FineSe).
- Istruzione che definisce la chiusura in una struttura di controllo.
Return. (Ritorno).
- Comando che, se usato da solo, interrompe il flusso di uno script, altrimenti, restituisce gli eventuali dati o valori specificati alla destra del comando stesso.
ElIf. (AltroSe).
- Elemento di una struttura con la quale è possibile porre altre condizioni da controllare.
Else. (Altrimenti).
- Elemento di una struttura di controllo che accoglie il flusso, qualora tutte le precedenti condizioni si rivelino false.
Indentazione.
- Tecnica che consiste nel creare degli spazi per far rientrare il codice di tanti spazi o tabulazioni quanti sono i livelli in cui si articola.
Pipe. (BarraVerticale).
- Carattere Ascii 124, usato singolarmente per separare le voci di un elenco orizzontale, mentre una coppia di caratteri affiancati costituisce l'operatore logico di disgiunzione.
confronto implicito.
- Tipo di confronto nel quali si punta a conoscere se un dato, una variabile o una funzione, sia presente, o abbia un valore positivo, e comunque non nullo, senza tuttavia fare uso degli operatori.
Proprietà.
- Caratteristiche o dati di un oggetto.
While. (Mentre).
- Ciclo nel quale una sequenza di istruzioni si ripete quando una determinata condizione è vera, e finché tale condizione rimane vera.
EndWhile. (FineMentre).
- Istruzione che determina il punto di conclusione dell'omonimo ciclo.
loop. (CicloContinuo).
- Ciclo che si ripete all'infinito, bloccando le funzionalità del pc.
contatore.
- Nei cicli For, valore che determina il numero di ripetizioni dello stesso. In generale, variabile che si aggiorna ad ogni passaggio del flusso per quel punto nel codice.
For. (Per).
- Ciclo nel quale una sequenza di istruzioni si ripete per il numero di volte specificate, partendo da un valore numerico ed arrivando ad un altro.
EndFor. (FinePer).
- Istruzione che determina il punto di conclusione del ciclo.
To. (A).
- Congiunzione da porre in un ciclo For tra il valore d'inizio e quello di fine.
Descending. (Discendente).
- In un ciclo For, parola chiave che determina di sottrarre un'unità al contatore delle iterazioni.
***
Per ulteriori spiegazioni, scrivere a:
Abramo Volpato, oppure, a: Nunziante Esposito