Como fazer uma pausa por um período específico de tempo? (Excel / VBA)


103

Eu tenho uma planilha do Excel com a macro a seguir. Eu gostaria de fazer um loop a cada segundo, mas não consigo encontrar a função para fazer isso. Não é possível?

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

Loop
End Sub

Respostas:


129

Use o método Wait :

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

ou (para Excel 2010 e posterior):

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

Não tenho certeza do que você quer dizer com isso, mas imagino que você queira ver a DoEventsdemonstração aqui. Dailydoseofexcel.com/archives/2005/06/14/stopwatch
Ryan Shannon,

3
@Benoit e @Keng, você pode colocar 1 segundo diretamente, evitando transformar o texto até a data. É mais limpo para mim: Application.Wait(Now + #0:00:01#)Saúde!
A.Sommerh

29
Esse formato não funcionava no Excel 2010. Porém, funciona:Application.Wait (Now + TimeValue("0:00:01"))
ZX9

1
DateAdd ("s", 1, Now) faz a coisa certa. E pode ser generalizado para qualquer número longo de segundos: DateAdd ("s", nSec, Now) sem usar o literal de tempo. Para dormir menos de 1 segundo, use a API Sleep no kernel32
Andrew Dennison

1
@ ZX9 timevalue ("00:00:01") = 0,0000115740 ... Portanto, Application.wait (agora + 0,00001) é cerca de um segundo. Eu gosto de usar Application.wait(now + 1e-5)por um segundo, Application.wait(now + 0.5e-5)por meio segundo etc.
Some_Guy

61

Adicione isso ao seu módulo

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

Ou, para sistemas de 64 bits, use:

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

Chame-o em sua macro assim:

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

Loop
End Sub

1
Por que não usar o método VBA para fazer isso em vez de usar kernel32.dll?
Ben S,

10
Usei esse método porque ele é portátil entre os vários produtos de escritório, como o Access, e não é específico do Excel.
Buggabill,

Há também outro aplicativo VBA (ERS) que uso que tem um subconjunto muito limitado da linguagem.
Buggabill,

18
+1, porque Sleep()permite especificar tempos de espera de menos de 1 segundo. Application.Waitàs vezes é muito granular.
BradC

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

42

ao invés de usar:

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

eu prefiro:

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

porque é muito mais fácil de ler depois.


7
E funciona enquanto, no Office 2013, o formato # 0: 0: 1 # converte para 1 segundo após a meia-noite.
Julian Knight de

1
AVISO: Todas as soluções que usam 'Application.Wait' têm imprecisão de 1 segundo. Este método não considera os milissegundos já decorridos desde o início do segundo atual ... Por exemplo, se faltar apenas 1 milissegundo para a mudança dos segundos de tempo, você só dormirá por 1 milissegundo. Você está apenas comparando 2 números arredondados em vez de esperar 1 segundo!
cyberponk de

18

isso funciona perfeitamente para mim. insira qualquer código antes ou depois do loop "do until". No seu caso, coloque as 5 linhas (time1 = & time2 = & "fazer até" loop) no final dentro do seu loop 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
Gosto mais dessa solução porque não queria interromper a fila de mensagens.
Daniel Fuchs

Esta solução é precisa e não requer a declaração da função sleep-API. Eu costumava armazenar o tempo em valores duplos e usar microssegundos com o mesmo resultado.
DrMarbuse de

Esta solução funciona melhor do que as soluções semelhantes abaixo que usam Timerporque a Timerabordagem falha pouco antes da meia-noite.
vknowes

1
Esta solução não é precisa, não leva em conta os milissegundos que já passaram desde o tempo atual ... Por exemplo, se faltar apenas 1 milissegundo para a mudança dos segundos do Agora, você só dormirá por 1 milissegundo.
cyberponk de

quando outras soluções estavam demorando muito para uma macro VBA não baseada no MS Office, esta funcionou perfeitamente - obrigado!
curioso

12

A declaração de Suspensão em kernel32.dll não funciona no Excel de 64 bits. Isso seria um pouco mais geral:

#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
Falha ao compilar no Office 2013. O verificador de erros no VBA para Office 2013 parece ignorar as instruções do compilador.
Julian Knight de

2
Compila muito bem para mim no Office 2013.
Jon Peltier

A maioria das pessoas concorda que Win64 / VBA7 dwMilliseconds é Long, não LongPtr. O Excel VBA esconde erros como este fazendo correção de pilha após chamadas externas, mas erros como este irão travar a maioria das versões do Open Office
David

6

Apenas uma versão limpa do código de clemo - funciona no Access, que não tem a função 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 Timerfornecer o número de segundos desde a meia-noite, essa abordagem falhará se for executada um pouco antes da meia-noite (ou seja, sngEndse> = 86.400). À meia-noite, Timerzera para 0 e permanece menos que sngEndpara sempre.
vknowes

PS O código de @clemo acima funciona a qualquer hora do dia.
vknowes

@vknowles, o que você disse é absolutamente verdade. E também pode ser compensado no método abaixo. Como esse método lida com milissegundos, acho que é uma aposta 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

A maioria das soluções apresentadas utiliza Application.Wait, que não leva em consideração o tempo (milissegundos) já decorrido desde o início da contagem do segundo atual, portanto , possuem uma imprecisão intrínseca de até 1 segundo .

A abordagem do temporizador é a melhor solução , mas você deve levar em conta a redefinição à meia-noite, então aqui está um método muito preciso de dormir usando o 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

USE ISSO PARA TESTAR QUALQUER FUNÇÃO DE SLEEP: (abrir janela imediata de depuração: 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


AVISO: Todas as soluções que usam 'Application.Wait' têm imprecisão de 1 segundo. Este método não considera os milissegundos já decorridos desde o início do segundo atual ... Por exemplo, se faltar apenas 1 milissegundo para a mudança dos segundos de tempo, você só dormirá por 1 milissegundo. Você está apenas comparando 2 números arredondados em vez de esperar 1 segundo!
cyberponk de

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
AVISO: Todas as soluções que usam 'Application.Wait' têm imprecisão de 1 segundo. Este método não considera os milissegundos já decorridos desde o início do segundo atual ... Por exemplo, se faltar apenas 1 milissegundo para a mudança dos segundos de tempo, você só dormirá por 1 milissegundo. Você está apenas comparando 2 números arredondados em vez de esperar 1 segundo!
cyberponk

2

Aqui está uma alternativa para dormir:

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

No código a seguir, faço um efeito de "brilho" piscar em um botão giratório para direcionar os usuários a ele se estiverem "tendo problemas". Usar "sleep 1000" no loop resultou em nenhum piscar visível, mas o loop está funcionando muito bem.

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

Eu fiz isso para responder ao 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

AVISO: Todas as soluções que usam 'Application.Wait' têm imprecisão de 1 segundo. Este método não considera os milissegundos já decorridos desde o início do segundo atual ... Por exemplo, se faltar apenas 1 milissegundo para a mudança dos segundos de tempo, você só dormirá por 1 milissegundo. Você está apenas comparando 2 números arredondados em vez de esperar 1 segundo!
cyberponk

1

Eu geralmente uso a função Timer para pausar o aplicativo. Insira este código no seu

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

3
Se Timerfornecer o número de segundos desde a meia-noite, essa abordagem falhará se for executada um pouco antes da meia-noite (ou seja, se T0for menor que o número de segundos de atraso a partir da meia-noite). À meia-noite, Timerredefine para 0 antes que o limite de atraso seja atingido. Delaynunca atinge o limite de atraso, então o loop é executado para sempre.
vknowes

Como o Timer produz valores não inteiros, você deve usar Loop Until Delay >= 1, ou corre o risco de passar de 1 e nunca sair do loop.
Jon Peltier

1

As funções de espera e suspensão bloqueiam o Excel e você não pode fazer mais nada até que o atraso termine. Por outro lado, os atrasos de loop não fornecem um tempo exato de espera.

Então, fiz esta solução alternativa unindo um pouco de ambos os conceitos. Ele faz um loop até a hora que você deseja.

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

Você só precisa ligar para Waste10Sec onde você precisa do atraso


0

Experimente isto:

Threading.thread.sleep(1000)

0

Você pode usar Application.wait now + timevalue ("00:00:01") ou Application.wait now + timeserial (0,0,1)

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.