Come fare una pausa per un determinato periodo di tempo? (Excel / VBA)


103

Ho un foglio di lavoro Excel con la seguente macro. Mi piacerebbe riprodurlo in loop ogni secondo, ma è pericoloso se riesco a trovare la funzione per farlo. Non è possibile?

Sub Macro1()
'
' Macro1 Macro
'
Do
    Calculate
    'Here I want to wait for one second

Loop
End Sub

Risposte:


129

Usa il metodo Wait :

Application.Wait Now + #0:00:01#

oppure (per Excel 2010 e versioni successive):

Application.Wait Now + #12:00:01 AM#

Non sono esattamente sicuro di cosa intendi con questo, ma immagino che tu voglia DoEventscome demo
Ryan Shannon

3
@Benoit e @Keng, puoi mettere 1 secondo direttamente, evitando di trasformare il testo fino ad oggi. È più pulito per me: Application.Wait(Now + #0:00:01#)Salute!
A.Sommerh

29
Quel formato non funzionava in Excel 2010. Funziona però:Application.Wait (Now + TimeValue("0:00:01"))
ZX9

1
DateAdd ("s", 1, Now) fa la cosa giusta. E può essere generalizzato per qualsiasi numero lungo di secondi: DateAdd ("s", nSec, Now) senza utilizzare il valore letterale del tempo. Per dormire meno di 1 secondo usa l'API Sleep in kernel32
Andrew Dennison

1
@ ZX9 timevalue ("00:00:01") = 0.0000115740 ... Quindi Application.wait (ora + 0.00001) è di circa un secondo. Mi piace usare Application.wait(now + 1e-5)per un secondo, Application.wait(now + 0.5e-5)per mezzo secondo ecc.
Some_Guy

61

Aggiungilo al tuo modulo

Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

Oppure, per i sistemi a 64 bit, utilizzare:

Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr)

Chiamalo nella tua macro in questo modo:

Sub Macro1()
'
' Macro1 Macro
'
Do
    Calculate
    Sleep (1000) ' delay 1 second

Loop
End Sub

1
Perché non utilizzare il metodo VBA per farlo invece di utilizzare kernel32.dll?
Ben S,

10
Ho utilizzato questo metodo in quanto è portabile tra i vari prodotti per ufficio come Access e non è specifico per Excel.
Buggabill

C'è anche un'altra applicazione VBA (ERS) che uso che ha un sottoinsieme molto limitato della lingua.
Buggabill

18
+1, perché Sleep()consente di specificare tempi di attesa inferiori a 1 secondo. Application.Waita volte è troppo granulare.
BradC

2
@JulianKnight:Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr)
Vegard

42

invece di usare:

Application.Wait(Now + #0:00:01#)

preferisco:

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

perché è molto più facile leggere dopo.


7
E funziona mentre, in Office 2013, il formato # 0: 0: 1 # viene convertito in 1 secondo dopo la mezzanotte.
Julian Knight

1
ATTENZIONE: tutte le soluzioni che utilizzano "Application.Wait" presentano un'imprecisione di 1 secondo. Questo metodo non considera i millisecondi già trascorsi dall'inizio del secondo corrente ... Ad esempio, se rimane solo 1 millisecondo prima che i secondi cambino, dormirai solo per 1 millisecondo. Stai solo confrontando 2 numeri arrotondati invece di aspettare 1 secondo!
cyberponk

18

questo funziona perfettamente per me. inserire qualsiasi codice prima o dopo il ciclo "do until". Nel tuo caso, metti le 5 righe (time1 = & time2 = & "do until" loop) alla fine del tuo ciclo do

sub whatever()
Dim time1, time2

time1 = Now
time2 = Now + TimeValue("0:00:01")
    Do Until time1 >= time2
        DoEvents
        time1 = Now()
    Loop

End sub

2
Questa soluzione mi piace di più perché non volevo fermare la coda dei messaggi.
Daniel Fuchs

Questa soluzione è precisa e non richiede la dichiarazione della funzione API sleep. Memorizzavo il tempo in valori doppi e utilizzavo invece i microsecondi con lo stesso risultato.
DrMarbuse

Questa soluzione funziona meglio delle soluzioni simili di seguito utilizzate Timerperché l' Timerapproccio fallisce poco prima di mezzanotte.
vknowles

1
Questa soluzione non è precisa, non tiene conto dei millisecondi già trascorsi dall'ora corrente ... Ad esempio, se manca solo 1 millisecondo prima che i secondi di Now cambino, dormirai solo per 1 millisecondo.
cyberponk

quando altre soluzioni richiedevano molto tempo per una macro VBA non basata su MS Office, questa funzionava perfettamente - grazie!
curioso

12

La dichiarazione per Sleep in kernel32.dll non funzionerà in Excel a 64 bit. Questo sarebbe un po 'più generale:

#If VBA7 Then
    Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
#Else
    Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
#End If

2
Impossibile compilare in Office 2013. Il controllo degli errori in VBA per Office 2013 sembra ignorare le istruzioni del compilatore.
Julian Knight

2
Compila bene per me in Office 2013.
Jon Peltier,

La maggior parte delle persone concorda sul fatto che Win64 / VBA7 dwMilliseconds sia Long, non LongPtr. Excel VBA nasconde errori come questo eseguendo la correzione dello stack dopo le chiamate esterne, ma errori come questo bloccheranno la maggior parte delle versioni di Open Office
David

6

Solo una versione pulita del codice di clemo - funziona in Access, che non ha la funzione Application.Wait.

Public Sub Pause(sngSecs As Single)
    Dim sngEnd As Single
    sngEnd = Timer + sngSecs
    While Timer < sngEnd
        DoEvents
    Wend
End Sub

Public Sub TestPause()
    Pause 1
    MsgBox "done"
End Sub

2
Se Timerfornisce il numero di secondi trascorsi dalla mezzanotte, allora questo approccio fallisce se viene eseguito appena prima della mezzanotte (cioè, tale che sngEndsia> = 86.400). A mezzanotte, si Timerripristina a 0 e quindi rimane inferiore sngEndper sempre.
vknowles

PS Il codice di @clemo sopra funziona in qualsiasi momento della giornata.
vknowles

@vknowles, quello che hai detto è assolutamente vero. E può anche essere compensato nel metodo seguente. Poiché questo metodo gestisce i millisecondi, penso che sia una scommessa sicura. `` Public Sub Sleep (sngSecs As Single) Dim sngEnd As Single sngEnd = Timer + (sngSecs / 1000) While Timer <sngEnd DoEvents If (sngEnd - Timer)> 50000 Then sngEnd = sngEnd - 86400 End If Wend End Sub `` `
PravyNandas

5

La maggior parte delle soluzioni presentate utilizza Application.Wait, che non tiene conto del tempo (millisecondi) già trascorso dall'inizio del conteggio dei secondi correnti, quindi hanno un'imprecisione intrinseca fino a 1 secondo .

L'approccio Timer è la soluzione migliore , ma devi tenere in considerazione il ripristino a mezzanotte, quindi ecco un metodo Sleep molto preciso utilizzando Timer:

'You can use integer (1 for 1 second) or single (1.5 for 1 and a half second)
Public Sub Sleep(vSeconds As Variant)
    Dim t0 As Single, t1 As Single
    t0 = Timer
    Do
        t1 = Timer
        If t1 < t0 Then t1 = t1 + 86400 'Timer overflows at midnight
        DoEvents    'optional, to avoid excel freeze while sleeping
    Loop Until t1 - t0 >= vSeconds
End Sub

USARE QUESTO PER TESTARE QUALSIASI FUNZIONE SLEEP: (aprire la finestra di debug Immediata: CTRL + G)

Sub testSleep()
    t0 = Timer
    Debug.Print "Time before sleep:"; t0   'Timer format is in seconds since midnight

    Sleep (1.5)

    Debug.Print "Time after sleep:"; Timer
    Debug.Print "Slept for:"; Timer - t0; "seconds"

End Sub

3

Application.Wait Second(Now) + 1


ATTENZIONE: tutte le soluzioni che utilizzano "Application.Wait" presentano un'imprecisione di 1 secondo. Questo metodo non considera i millisecondi già trascorsi dall'inizio dell'attuale secondo ... Ad esempio, se rimane solo 1 millisecondo prima che i secondi cambino, dormirai solo per 1 millisecondo. Stai solo confrontando 2 numeri arrotondati invece di aspettare 1 secondo!
cyberponk

2
Function Delay(ByVal T As Integer)
    'Function can be used to introduce a delay of up to 99 seconds
    'Call Function ex:  Delay 2 {introduces a 2 second delay before execution of code resumes}
        strT = Mid((100 + T), 2, 2)
            strSecsDelay = "00:00:" & strT
    Application.Wait (Now + TimeValue(strSecsDelay))
End Function

1
ATTENZIONE: tutte le soluzioni che utilizzano "Application.Wait" presentano un'imprecisione di 1 secondo. Questo metodo non considera i millisecondi già trascorsi dall'inizio dell'attuale secondo ... Ad esempio, se rimane solo 1 millisecondo prima che i secondi cambino, dormirai solo per 1 millisecondo. Stai solo confrontando 2 numeri arrotondati invece di aspettare 1 secondo!
cyberponk

2

Ecco un'alternativa al sonno:

Sub TDelay(delay As Long)
Dim n As Long
For n = 1 To delay
DoEvents
Next n
End Sub

Nel codice seguente faccio lampeggiare un effetto "bagliore" su un pulsante di selezione per indirizzare gli utenti ad esso se stanno "avendo problemi", l'uso di "sleep 1000" nel ciclo non ha prodotto lampeggi visibili, ma il ciclo funziona alla grande.

Sub SpinFocus()
Dim i As Long
For i = 1 To 3   '3 blinks
Worksheets(2).Shapes("SpinGlow").ZOrder (msoBringToFront)
TDelay (10000)   'this makes the glow stay lit longer than not, looks nice.
Worksheets(2).Shapes("SpinGlow").ZOrder (msoSendBackward)
TDelay (100)
Next i
End Sub

1

ho fatto questo per rispondere al problema:

Sub goTIMER(NumOfSeconds As Long) 'in (seconds) as:  call gotimer (1)  'seconds
  Application.Wait now + NumOfSeconds / 86400#
  'Application.Wait (Now + TimeValue("0:00:05"))  'other
  Application.EnableEvents = True       'EVENTS
End Sub

ATTENZIONE: tutte le soluzioni che utilizzano "Application.Wait" presentano un'imprecisione di 1 secondo. Questo metodo non considera i millisecondi già trascorsi dall'inizio dell'attuale secondo ... Ad esempio, se rimane solo 1 millisecondo prima che i secondi cambino, dormirai solo per 1 millisecondo. Stai solo confrontando 2 numeri arrotondati invece di aspettare 1 secondo!
cyberponk

1

Di solito utilizzo la funzione Timer per mettere in pausa l'applicazione. Inserisci questo codice nel tuo

T0 = Timer
Do
    Delay = Timer - T0
Loop Until Delay >= 1 'Change this value to pause time for a certain amount of seconds

3
Se Timerfornisce il numero di secondi trascorsi dalla mezzanotte, allora questo approccio fallisce se viene eseguito appena prima di mezzanotte (cioè, tale che T0è inferiore al numero di secondi di ritardo dalla mezzanotte). A mezzanotte, si Timerripristina a 0 prima che venga raggiunto il limite di ritardo. Delaynon raggiunge mai il limite di ritardo, quindi il ciclo viene eseguito per sempre.
vknowles

Poiché Timer produce valori non interi, dovresti usare Loop Until Delay >= 1, altrimenti rischi di andare oltre 1 e non uscire mai dal ciclo.
Jon Peltier

1

Le funzioni di attesa e sospensione bloccano Excel e non puoi fare nient'altro fino al termine del ritardo. D'altra parte, i ritardi del ciclo non ti danno un tempo esatto di attesa.

Quindi, ho creato questa soluzione unendo un po 'di entrambi i concetti. Continua fino a quando il tempo è il tempo che desideri.

Private Sub Waste10Sec()
   target = (Now + TimeValue("0:00:10"))
   Do
       DoEvents 'keeps excel running other stuff
   Loop Until Now >= target
End Sub

Devi solo chiamare Waste10Sec dove hai bisogno del ritardo


0

Prova questo :

Threading.thread.sleep(1000)

0

Puoi utilizzare Application.wait ora + timevalue ("00:00:01") o Application.wait ora + timeserial (0,0,1)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.