Azioni a tempo con OnTime, Wait e Timer

Ultima modifica: 30-05-2016

 

Tra le varie possibilità offerte da VBA c'è quella di ottenere delle temporizzazioni. In realtà sono poco usate e poco utili. Ma, alle volte, quando serve, non si sa dove andare a parare.

Per questo motivo, questa volta, a solo titolo cognitivo, vado a mostrarvi alcuni esempi in cui possiamo usare delle pause.

Vedremo come nei seguenti esempi:

Questa possibilità ci viene offerta da due metodi esposti nell'oggetto Application e da una funzione:

Nota importante:

Attenzione alla temporizzazione che andremo ad impostare. Un tempo eccessivamente lungo potrebbe costringerci ad interrompere l'esecuzione in maniera brusca e non troppo ortodossa.

Per esempio:

se sono le 9 di mattina con la seguente istruzione dovremo aspettare le 10 di sera prima di riprendere in mano il controllo dell'applicazione

Application.OnTime TimeValue("22:00:00"), "mia_Sub"

oppure se usassimo quest'altra istruzione dovremo aspettare 2 ore e mezza:

Application.OnTime Time + TimeValue("00:02:30"), "mia_Sub"

Usiamo OnTime per eseguire una determinata routine allo scadere del tempo impostato.

Tuttavia, nel frattempo, è consentito eseguire qualsiasi altra azione sul foglio, o mostrare una MsgBox, o altre istruzioni nel codice. Se, allo scadere del tempo impostato, il codice sta ancora eseguendo delle elaborazioni, o se noi stiamo usando in qualche modo il foglio, o la MsgBox è ancora aperta, l'istruzione impostata con OnTime attende pazientemente prima di entrare in funzione.

Lievemente differente è l'uso di Timer usato come temporizzatore. Allo scadere del tempo stabilito viene interrotta qualsiasi cosa stiamo facendo sul foglio di lavoro.

 

Con Wait viene sospesa l'esecuzione di una macro fino al tempo specificato nel suo argomento.

Con questa istruzione vogliamo una pausa di 5 secondi prima di continuare con le istruzioni successive

Application.Wait Now + TimeValue("00:00:05")

con questa vogliamo una pausa di 2 minuti e 5 secondi

Application.Wait Now + TimeValue("00:02:05")

Solo dopo la pausa il codice riprende il suo lavoro e l'utilizzatore può continuare il suo.

Da tenere presente che l'utilizzo della proprietà Wait impone una pausa nell'esecuzione di istruzioni ( la cui durata viene impostata nel suo argomento) e durante questa pausa non è consentito fare alcunché. Tra le applicazioni che possono usare questa proprietà posso citare la necessità di attendere l'apertura di una finestra di un applicativo per l'invio di una sequenza di tasti, o anche quella di mostrare per un breve lasso di tempo una UserForm con un messaggio

 

OnTime programma un periodo di tempo dopo il quale una routine possa essere eseguita. Nel frattempo il codice continua il suo lavoro e l'utilizzatore ha la possibilità di continuare il suo lavoro fino allo scadere del tempo. Se il lavoro del codice o quello dell'utente si protrae oltre il tempo stabilito l'esecuzione della routine indicata nella istruzione che usa OnTime attende pazientemente.

Con questa istruzione vogliamo che alle 19 e 15 venga eseguita la routine "MiaRoutine"

Application.OnTime TimeValue("19:15:00"), "MiaRoutine"

Con questa istruzione vogliamo che la routine "MiaRoutine" venga eseguita 5 secondi dal tempo attuale (Now)

Application.OnTime Now + TimeValue("00:00:05"), "MiaRoutine"

 

Timer, fra le 3, è forse la più completa e versatile. Normalmente, se usata senza argomenti, restituisce un valore Single che rappresenta il numero di secondi, o frazioni di secondo, trascorsi dalla mezzanotte. Tuttavia, se convenientemente usato, può attivare interessanti effetti di temporizzazioni. Infatti è possibile impostare una pausa prima di eseguire una qualsiasi cosa, ma utilizzando DoEvents, è possibile, durante l'attesa, restituire il controllo all'utente o all'applicazione.

Col seguente esempio impostiamo una pausa di 10 secondi durante la quale non è possibile fare nulla

Pausa = 10
OraAttuale = Timer
Do While Timer < OraAttuale + Pausa
Loop
MsgBox "Tempo scaduto"

Usando DoEvents è invece possibile permettere all'utente di lavorare sul foglio durante l'attesa programmata

Pausa = 10
OraAttuale = Timer
Do While Timer < OraAttuale + Pausa
DoEvents
Loop
MsgBox "Tempo scaduto"

 

Alcune utility

In questa circostanza presento alcune piccole utility realizzate usando una o più di queste istruzioni.

Mostrare un messaggio per un breve periodo di tempo usando Wait

Per eseguire questo Test occorre disegnare una UserForm ed in essa una Label come mostrato in queste immagini.

Nella prima è possibile vedere come la userform viene disegnata, la seconda mostra come si presenta in fase di esecuzione.

label il form

L'azione mostrata in questo esempio parte da una routine posta in un Modulo Standard, ma potrebbe partire da una qualsiasi altra parte del nostro applicativo, quando c'è l'esigenza di visualizzare un qualsiasi messaggio che normalmente mostriamo con una MsgBox. In questo caso, invece della MsgBox, usiamo la UserForm che più si adatta al caso:

Sub MostraForm()
UserForm2.Show
End Sub

Il seguito viene scritto negli eventi UserForm_Activate e UserForm_Initialize della UserForm usata per il messaggio.

Con questo viene scritto il messaggio nella label della UserForm

Private Sub UserForm_Initialize()
Me.Label1.Caption = "Attendere prego..."
End Sub

Qui viene impostato il tempo di attesa (10 secondi nel nostro caso), segue l'istruzione che blocca l'esecuzione del restante codice e, trascorso il tempo viene chiusa la UserForm

Private Sub UserForm_Activate()
Dim TempoAttesa
TempoAttesa = Now + TimeValue("00:00:10")
Application.Wait TempoAttesa
UserForm2.Hide
End Sub

Salvataggio e chiusura a tempo della cartella con Ontime e con un messaggio di avviso

In questo caso usiamo OnTime per far salvare e chiudere la nostra cartella di lavoro dopo un ragionevole lasso di tempo e mostrando prima un avviso. Per fare questo abbiamo bisogno di una UserForm come quella appena vista che ci avverte che il tempo sta per scadere. In fase di progettazione abbiamo impostato a False la sua proprietà ShowModal per avere la possibilità di continuare a lavorare sul foglio.

imposta

Per compiere tutta l'azione scriviamo alcune semplici istruzioni:

In un Modulo Standard questa è la routine chiamante (quella da cui parte l'azione)

Appena impostato un tempo di attesa di 5 secondi facciamo aprire la UserForm che ci avvisa su cosa sta succedendo

Sub OnTimeConForm()
Application.OnTime Now + TimeValue("00:00:05"), "Chiudi"
UserForm1.Show
End Sub

In quest'altra routine, scritta nello stesso Modulo Standard e che è la routine chiamata, chiudiamo la UserForm ed usciamo dall'applicazione. Questa routine entra in azione allo scadere del tempo impostato o, comunque, appena possibile, se il codice è impegnato in altre operazioni o appena l'utente cessa di eseguire le sue operazioni sul foglio di lavoro

Sub Chiudi()
Application.DisplayAlerts = False
ActiveWorkbook.Save
Application.DisplayAlerts = True
Unload UserForm1
Set UserForm1 = Nothing
Excel.Application.Quit
End Sub

Nel modulo relativo alla UserForm useremo UserForm_Activate o UserForm_Initialize (la scelta è indifferente) per scrivere nella Label il messaggio da visualizzare

Private Sub UserForm_Activate()
Label1.Caption = "Salvataggio in corso"
End Sub

Due effetti speciali su stringhe ottenuti con Timer: una scritta scorrevole ed una scritta rotante

Osservando alcune Gif animate che un giovane amico ha realizzato per un suo lavoro mi sono tornate in mente delle reminiscenze del vecchio Basic e ho deciso di applicare ad una stringa due effetti speciali: una scritta scorrevole ed una scritta rotante.

scritta scorrevole  scritta rotante

In quell'ambiente ottenevo questi effetti usando tra le istruzioni un ciclo For ... Next a vuoto con un valore finale del contatore ragionevolmente grande: For A = 1 To 1000000.

Ma questo espediente, sulle attuali macchine, fa variare il tempo di attesa dipendentemente dalla velocità della macchina su cui gira: su un computer lento impiega più tempo che non su un computer veloce. Usando opportunamente la funzione Timer, invece, la temporizzazione è esatta al centesimo di secondo in quanto la funzione legge il tempo direttamente dall'orologio del computer.

Tramite questa funzione è perciò possibile regolarne la velocità di scorrimento del testo agendo sul tempo in termini di centesimi di secondo con questa semplice sequenza di istruzioni:

Pausa = 0.05: OraAttuale = Timer
Do While Timer < OraAttuale + Pausa
Loop

In Pausa poniamo il tempo che vogliamo per la pausa

In OraAttuale catturiamo l'orario attuale

Con il ciclo successivo Do ... Loop attendiamo che Timer assuma il valore da noi impostato con OraAttuale + Pausa

In entrambi i casi, per manipolare opportunamente la stringa, facciamo uso delle parole chiavi Left e Right, e, ovviamente, di un ciclo che scorre la Stringa in tutta la sua lunghezza

Per la scritta scorrevole

Facendo uso di Left, prendiamo dalla stringa originale un numero di caratteri pari al numero indicato dal contatore del ciclo e li scriviamo nella cella indicata o, se preferiamo in una Label o qualsiasi altro oggetto disegnato su una UserForm. Per la stringa "VISUAL BASIC" il contatore indicherà i numeri che vanno da 1 a 12.

Sub MessaggioScorrevole()
Dim I, Pausa
Dim OraAttuale
Dim MiaStringa, A
Pausa = Range("A4")
MiaStringa = "Messaggio scorrevole"
For A = 1 To Len(MiaStringa)
OraAttuale = Timer
Do While Timer < OraAttuale + Pausa
DoEvents
Range("E10") = Mid(MiaStringa, 1, A)
Loop
Next
End Sub

Per la scritta Rotante

E' bene, per ottenere un effetto gradevole, porre uno spazio alla fine della stringa "Scritta Rotante ".

Quindi, facendo uso di Right, prenderemo l'ultima lettera della stringa ( )

Concateniamo il carattere appena prelevato con l'intera stringa " Scritta Rotante "

Aiutati da Left, accorceremo la stringa di un carattere " Scritta Rotante"

Il ciclo ci servirà per ripetere l'operazione tante volte quante sono le lettere della stringa originale per tornare alla stringa originale.

Sub ScrittaRotante()
Dim I
Dim Pausa, OraAttuale
Dim Scritta, NCar
Pausa = Range("A4")
For I = 1 To Len(Range("E10"))
Scritta = Range("E10")
NCar = Len(Scritta)
'qui viene modificata la stringa
Range("E10") = Right(Scritta, 1) & Left(Scritta, NCar - 1)
OraAttuale = Timer
Do While Timer < OraAttuale + Pausa
Loop
Next
End Sub

Buon divertimento