¿Cómo hacer una pausa durante un tiempo específico? (Excel / VBA)

103

Tengo una hoja de cálculo de Excel que tiene la siguiente macro. Me gustaría hacer un bucle cada segundo, pero peligroso si puedo encontrar la función para hacer eso. ¿No es posible?

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

Loop
End Sub
Keng
fuente

Respuestas:

129

Utilice el método de espera :

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

o (para Excel 2010 y posteriores):

Application.Wait Now + #12:00:01 AM#
Ben S
fuente
No estoy exactamente seguro de lo que quiere decir con eso, pero supongo que quiere ver la DoEventsdemostración aquí dailydoseofexcel.com/archives/2005/06/14/stopwatch
Ryan Shannon
3
@Benoit y @Keng, puedes poner 1 segundo directamente, evitando transformar el texto a la fecha. Es más limpio para mí: Application.Wait(Now + #0:00:01#)¡Salud!
A.Sommerh
29
Ese formato no funcionaba en Excel 2010. Sin embargo, esto funciona:Application.Wait (Now + TimeValue("0:00:01"))
ZX9
1
DateAdd ("s", 1, Now) hace lo correcto. Y se puede generalizar para cualquier número largo de segundos: DateAdd ("s", nSec, Now) sin usar el literal de tiempo. Para dormir menos de 1 segundo, use la API de suspensión en kernel32
Andrew Dennison
1
@ ZX9 timevalue ("00:00:01") = 0.0000115740 ... Entonces Application.wait (ahora + 0.00001) es aproximadamente un segundo. Me gusta usar Application.wait(now + 1e-5)por un segundo, Application.wait(now + 0.5e-5)por medio de un segundo etc.
Some_Guy
61

Agrega esto a tu módulo

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

O, para sistemas de 64 bits, use:

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

Llámalo en tu macro así:

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

Loop
End Sub
Buggabill
fuente
1
¿Por qué no usar el método VBA para hacer esto en lugar de usar kernel32.dll?
Ben S
10
He utilizado este método porque es portátil entre los diversos productos de oficina como Access y no es específico de Excel.
Buggabill
También hay otra aplicación VBA (ERS) que utilizo y que tiene un subconjunto muy limitado del idioma.
Buggabill
18
+1, porque le Sleep()permite especificar tiempos de espera de menos de 1 segundo. Application.Waita veces es demasiado granular.
BradC
2
@JulianKnight:Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr)
Vegard
42

En lugar de usar:

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

yo prefiero:

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

porque es mucho más fácil de leer después.

Achaibou Karim
fuente
7
Y funciona mientras que, en Office 2013, el formato # 0: 0: 1 # se convierte a 1 segundo después de la medianoche.
Julian Knight
1
ADVERTENCIA: Todas las soluciones que utilizan 'Aplicación. Espere' tienen 1 segundo de imprecisión. Este método no considera los milisegundos que ya pasaron desde que comenzó el segundo actual ... Por ejemplo, si solo queda 1 milisegundo hasta que cambien los segundos de tiempo, solo dormirá 1 milisegundo. ¡Solo está comparando 2 números redondeados en lugar de esperar 1 segundo!
cyberponk
18

esto funciona perfectamente para mí. inserte cualquier código antes o después del ciclo "hacer hasta". En su caso, coloque las 5 líneas (time1 = & time2 = & "do until" loop) al final dentro de su do loop

sub whatever()
Dim time1, time2

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

End sub
clemo
fuente
2
Me gusta más esta solución porque no quería detener la cola de mensajes.
Daniel Fuchs
Esta solución es precisa y no requiere la declaración de la función sleep-API. Solía ​​almacenar el tiempo en valores dobles y usar microsegundos en su lugar con el mismo resultado.
DrMarbuse
Esta solución funciona mejor que las soluciones similares a continuación que se utilizan Timerporque el Timerenfoque falla justo antes de la medianoche.
vknowles
1
Esta solución no es precisa, no toma en cuenta los milisegundos que ya transcurrieron desde el tiempo actual ... Por ejemplo, si solo queda 1 milisegundo hasta que cambie el Ahora segundos, solo dormirás 1 milisegundo.
cyberponk
cuando otras soluciones tomaban mucho tiempo para una macro VBA no basada en MS Office, esta funcionó perfectamente, ¡gracias!
curioso
12

La declaración de suspensión en kernel32.dll no funcionará en Excel de 64 bits. Esto sería un poco más general:

#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
dbuskirk
fuente
2
No se puede compilar en Office 2013. El comprobador de errores en VBA para Office 2013 parece ignorar las declaraciones del compilador.
Julian Knight
2
Compila bien para mí en Office 2013.
Jon Peltier
La mayoría de la gente está de acuerdo en que Win64 / VBA7 dwMilliseconds es Long, no LongPtr. Excel VBA oculta errores como este al realizar una corrección de pila después de llamadas externas, pero errores como este bloquearán la mayoría de las versiones de Open Office
David
6

Solo una versión limpia del código de clemo: funciona en Access, que no tiene la función 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
Brian Burns
fuente
2
Si Timerda el número de segundos desde la medianoche, entonces este enfoque falla si se ejecuta justo antes de la medianoche (es decir, sngEndes> = 86,400). A medianoche, se Timerrestablece a 0 y, por lo tanto, permanece menos de sngEndsiempre.
vknowles
PD: El código de @clemo anterior funciona en cualquier momento del día.
vknowles
@vknowles, lo que dijiste es absolutamente cierto. Y también se puede compensar con el método siguiente. Dado que este método maneja milisegundos, creo que es una apuesta segura. `` `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 mayoría de las soluciones presentadas utilizan Application.Wait, que no tiene en cuenta el tiempo (milisegundos) ya transcurrido desde que comenzó la cuenta de segundos actual, por lo que tienen una imprecisión intrínseca de hasta 1 segundo .

El enfoque del temporizador es la mejor solución , pero debe tener en cuenta el reinicio a la medianoche, por lo que aquí hay un método de suspensión muy preciso que usa el temporizador:

'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

UTILICE ESTO PARA PROBAR CUALQUIER FUNCIÓN DE SUEÑO: (abrir la ventana de depuración inmediata: 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
cyberponk
fuente
3

Application.Wait Second(Now) + 1

gt
fuente
ADVERTENCIA: Todas las soluciones que utilizan 'Aplicación. Espere' tienen 1 segundo de imprecisión. Este método no considera los milisegundos que ya pasaron desde que comenzó el segundo actual ... Por ejemplo, si solo queda 1 milisegundo hasta que cambien los segundos de tiempo, solo dormirá 1 milisegundo. ¡Solo está comparando 2 números redondeados en lugar de esperar 1 segundo!
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
ITI
fuente
1
ADVERTENCIA: Todas las soluciones que utilizan 'Aplicación. Espere' tienen 1 segundo de imprecisión. Este método no considera los milisegundos que ya pasaron desde que comenzó el segundo actual ... Por ejemplo, si solo queda 1 milisegundo hasta que cambien los segundos de tiempo, solo dormirá 1 milisegundo. ¡Solo está comparando 2 números redondeados en lugar de esperar 1 segundo!
cyberponk
2

Aquí tienes una alternativa para dormir:

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

En el siguiente código, hago parpadear un efecto "resplandor" en un botón giratorio para dirigir a los usuarios hacia él si "tienen problemas", el uso de "dormir 1000" en el ciclo no dio como resultado un parpadeo visible, pero el ciclo funciona muy bien.

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
Reverus
fuente
1

Hice esto para responder 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
Dave
fuente
ADVERTENCIA: Todas las soluciones que utilizan 'Aplicación. Espere' tienen 1 segundo de imprecisión. Este método no considera los milisegundos que ya pasaron desde que comenzó el segundo actual ... Por ejemplo, si solo queda 1 milisegundo hasta que cambien los segundos de tiempo, solo dormirá 1 milisegundo. ¡Solo está comparando 2 números redondeados en lugar de esperar 1 segundo!
cyberponk
1

Normalmente uso la función de temporizador para pausar la aplicación. Inserta este código en el tuyo

T0 = Timer
Do
    Delay = Timer - T0
Loop Until Delay >= 1 'Change this value to pause time for a certain amount of seconds
Anastasiya-Romanova 秀
fuente
3
Si Timerda el número de segundos desde la medianoche, entonces este enfoque falla si se ejecuta justo antes de la medianoche (es decir, que T0es menor que el número de segundos de retraso desde la medianoche). A medianoche, se Timerrestablece a 0 antes de que se alcance el límite de retraso. Delaynunca alcanza el límite de retardo, por lo que el ciclo se ejecuta para siempre.
vknowles
Dado que Timer produce valores no enteros, debe usar Loop Until Delay >= 1, o corre el riesgo de pasar de 1 y nunca salir del bucle.
Jon Peltier
1

Las funciones de espera y suspensión bloquean Excel y no puede hacer nada más hasta que finalice el retraso. Por otro lado, los retardos de bucle no le dan un tiempo exacto para esperar.

Entonces, hice esta solución uniendo un poco de ambos conceptos. Se repite hasta que llega el momento que desea.

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

Solo necesita llamar a Waste10Sec donde necesite el retraso

Maycow Moura
fuente
0

Prueba esto :

Threading.thread.sleep(1000)
DEV
fuente
0

Puede usar Application.wait now + timevalue ("00:00:01") o Application.wait now + timeserial (0,0,1)

Tuan Nguyen
fuente