Как сделать паузу на определенное время? (Excel / VBA)

103

У меня есть лист Excel со следующим макросом. Я бы хотел зацикливать это каждую секунду, но, черт возьми, я могу найти функцию для этого. Разве это невозможно?

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

Loop
End Sub
Кенг
источник

Ответы:

129

Используйте метод ожидания :

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

или (для Excel 2010 и новее):

Application.Wait Now + #12:00:01 AM#
Бен С
источник
Я не совсем уверен, что вы имеете в виду, но предполагаю, что вы хотите, DoEventsкак показано
Райан Шеннон
3
@Benoit и @Keng, вы можете указать 1 секунду напрямую, избегая преобразования текста в текущий. Для меня Application.Wait(Now + #0:00:01#)чище : Ура!
A.Sommerh
29
Этот формат не работал в Excel 2010. Но это работает:Application.Wait (Now + TimeValue("0:00:01"))
ZX9, 04
1
DateAdd ("s", 1, Now) поступает правильно. И может быть обобщен на любое длинное количество секунд: DateAdd ("s", nSec, Now) без использования литерала времени. Чтобы спать менее 1 секунды, используйте Sleep API в kernel32
Эндрю Деннисон,
1
@ ZX9 timevalue ("00:00:01") = 0.0000115740 ... Итак, Application.wait (теперь + 0.00001) составляет около секунды. Мне нравится использовать Application.wait(now + 1e-5)секунду, Application.wait(now + 0.5e-5)полсекунды и т. Д.
Some_Guy
61

Добавьте это в свой модуль

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

Или для 64-битных систем используйте:

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

Назовите это в своем макросе так:

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

Loop
End Sub
Buggabill
источник
1
Почему бы не использовать для этого метод VBA вместо kernel32.dll?
Ben S,
10
Я использовал этот метод, поскольку он переносится между различными офисными продуктами, такими как Access, и не относится к Excel.
Buggabill
Я также использую другое приложение VBA (ERS), которое имеет очень ограниченное подмножество языка.
Buggabill
18
+1, потому что Sleep()позволяет указать время ожидания менее 1 секунды. Application.Waitиногда бывает слишком гранулированным.
BradC
2
@JulianKnight:Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr)
Вегард,
42

Вместо того, чтобы использовать:

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

я предпочитаю:

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

потому что после этого читать намного легче.

Ачайбу Карим
источник
7
И это работает, тогда как в Office 2013 формат # 0: 0: 1 # преобразуется в 1 секунду после полуночи.
Джулиан Найт,
1
ВНИМАНИЕ! Все решения, использующие Application.Wait, имеют неточность в 1 секунду. Этот метод не учитывает миллисекунды, которые уже прошли с момента начала текущей секунды ... Например, если до изменения секунд времени остается всего 1 миллисекунда, вы будете спать только в течение 1 миллисекунды. Вы просто сравниваете 2 округленных числа вместо того, чтобы ждать 1 секунду!
cyberponk
18

у меня это работает безупречно. вставьте любой код до или после цикла «делать до». В вашем случае поместите 5 строк (time1 = & time2 = & "do until" 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
Clemo
источник
2
Мне больше всего нравится это решение, потому что я не хотел останавливать очередь сообщений.
Даниэль Фукс,
Это решение является точным и не требует объявления функции sleep-API. Раньше я сохранял время в двойных значениях и использовал вместо этого микросекунды с тем же результатом.
DrMarbuse
Это решение работает лучше, чем аналогичные решения, которые используются ниже, Timerпотому что этот Timerподход не работает незадолго до полуночи.
vknowles 03
1
Это решение не является точным, оно не учитывает миллисекунды, которые уже прошли с текущего времени ... Например, если до изменения значения Now seconds осталась всего 1 миллисекунда, вы будете спать только 1 миллисекунду.
cyberponk
когда другие решения занимали много времени для макроса VBA, отличного от MS Office, этот работал отлично - спасибо!
любопытный
12

Объявление Sleep в kernel32.dll не будет работать в 64-битном Excel. Это было бы немного более общим:

#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
источник
2
Не удается скомпилировать в Office 2013. Средство проверки ошибок в VBA для Office 2013, похоже, игнорирует операторы компилятора.
Джулиан Найт,
2
Компиляция отлично подходит для меня в Office 2013.
Джон Пельтье,
Большинство людей согласны с тем, что Win64 / VBA7 dwMilliseconds - это Long, а не LongPtr. Excel VBA скрывает подобные ошибки, выполняя коррекцию стека после внешних вызовов, но подобные ошибки приведут к сбою большинства версий Open Office
Дэвид,
6

Просто очищенная версия кода clemo - работает в Access, в котором нет функции 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
Если Timerдает количество секунд, прошедших с полуночи, то этот подход не работает, если он выполняется непосредственно перед полуночью (т. sngEndЕ. Когда> = 86 400). В полночь Timerсбрасывается до 0 и, таким образом, остается меньше, чем sngEndнавсегда.
vknowles 03
PS Код из @clemo выше работает в любое время суток.
vknowles 03
@vknowles, то, что вы сказали, абсолютно верно. А также может быть скомпенсировано в методе ниже. Поскольку этот метод обрабатывает миллисекунды, я думаю, что это безопасная ставка. `` 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 02
5

Большинство представленных решений используют Application.Wait, которое не учитывает время (в миллисекундах), уже прошедшее с момента начала текущего подсчета секунд, поэтому они имеют внутреннюю неточность до 1 секунды .

Подход с использованием таймера - лучшее решение , но вы должны учитывать сброс в полночь, поэтому вот очень точный метод сна с использованием таймера:

'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

ИСПОЛЬЗУЙТЕ ЭТО ДЛЯ ПРОВЕРКИ ЛЮБОЙ ФУНКЦИИ СНА: (открыть окно немедленной отладки: 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

gt
источник
ВНИМАНИЕ! Все решения, использующие Application.Wait, имеют неточность в 1 секунду. Этот метод не учитывает миллисекунды, которые уже прошли с момента начала текущей секунды ... Например, если до изменения секунд времени остается всего 1 миллисекунда, вы будете спать только в течение 1 миллисекунды. Вы просто сравниваете 2 округленных числа вместо того, чтобы ждать 1 секунду!
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
источник
1
ВНИМАНИЕ! Все решения, использующие Application.Wait, имеют неточность в 1 секунду. Этот метод не учитывает миллисекунды, которые уже прошли с момента начала текущей секунды ... Например, если до изменения секунд времени остается всего 1 миллисекунда, вы будете спать только в течение 1 миллисекунды. Вы просто сравниваете 2 округленных числа вместо того, чтобы ждать 1 секунду!
cyberponk
2

Вот альтернатива сну:

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

В следующем коде я заставляю мигать эффект «свечения» на кнопке вращения, чтобы направлять пользователей к нему, если у них «проблемы», использование «сна 1000» в цикле не привело к видимому миганию, но цикл работает отлично.

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
источник
1

я сделал это, чтобы ответить на проблему:

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
Дэйв
источник
ВНИМАНИЕ! Все решения, использующие Application.Wait, имеют неточность в 1 секунду. Этот метод не учитывает миллисекунды, которые уже прошли с момента начала текущей секунды ... Например, если до изменения секунд времени остается всего 1 миллисекунда, вы будете спать только в течение 1 миллисекунды. Вы просто сравниваете 2 округленных числа вместо того, чтобы ждать 1 секунду!
cyberponk
1

Я обычно использую функцию таймера, чтобы приостановить приложение. Вставьте этот код в свой

T0 = Timer
Do
    Delay = Timer - T0
Loop Until Delay >= 1 'Change this value to pause time for a certain amount of seconds
Анастасия-Романова 秀
источник
3
Если Timerдает количество секунд с полуночи, то этот подход не работает, если он выполняется непосредственно перед полуночью (то есть так, что T0меньше, чем количество секунд задержки от полуночи). В полночь Timerсбрасывается на 0 до того, как будет достигнут предел задержки. Delayникогда не достигает предела задержки, поэтому цикл выполняется бесконечно.
vknowles 03
Поскольку Timer производит нецелочисленные значения, вы должны использовать Loop Until Delay >= 1, иначе вы рискуете превысить 1 и никогда не выйти из цикла.
Jon Peltier
1

Функции ожидания и сна блокируют Excel, и вы не можете ничего делать, пока не закончится задержка. С другой стороны, задержка цикла не дает вам точного времени ожидания.

Итак, я сделал это обходное решение, объединив немного обеих концепций. Он повторяется до тех пор, пока время не станет нужным вам.

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

Вам просто нужно позвонить в Waste10Sec, где вам нужна задержка

Maycow Moura
источник
0

Вы можете использовать Application.wait now + timevalue ("00:00:01") или Application.wait now + timeserial (0,0,1)

Туан Нгуен
источник