Як зробити паузу на певний час? (Excel / VBA)


103

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

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

Loop
End Sub

Відповіді:


129

Використовуйте метод Wait :

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

або (для Excel 2010 та новіших версій):

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

Я не зовсім впевнений , що ви маєте в виду, але я спекулювати ви хочете , DoEventsяк тут призначалася dailydoseofexcel.com/archives/2005/06/14/stopwatch
Райан Шеннон

3
@Benoit і @Keng ви можете поставити 1 секунду безпосередньо, уникаючи перетворення тексту на сьогоднішній день. Більш чисто для мене: Application.Wait(Now + #0:00:01#)Ура!
А.Соммерх

29
Цей формат не працював у програмі Excel 2010. Хоча це працює:Application.Wait (Now + TimeValue("0:00:01"))
ZX9

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

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 do" цикл) в кінці всередині циклу 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
Найбільше мені подобається це рішення, тому що я не хотів зупиняти чергу повідомлень.
Даніель Фукс

Це рішення є точним і не потребує декларації режиму сну-API-функції. Раніше я зберігав час у подвійних значеннях і замість цього використовував мікросекунди з тим же результатом.
DrMarbuse

Це рішення працює краще, ніж аналогічні рішення, які використовуються нижче, Timerтому що Timerпідхід не вдається лише перед півночі.
vknowles

1
Це рішення не є точним, воно не враховує мілісекунд, що минули від поточного часу ... Наприклад, якщо залишилося лише 1 мілісекунда до зміни секунд Now, ви будете спати лише 1 мілісекунда.
кіберпонк

коли інші рішення потребували довгого часу для макроса VBA, який не базується на офісі MS Office, цей відмінно працював - дякую!
цікаво

12

Декларація про сон у 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

2
Не вдалося компілювати в Office 2013. Перевірка помилок у VBA для Office 2013, здається, ігнорує заяви компілятора.
Джуліан Найт,

2
Мене штрафує в Office 2013.
Джон Пелтьє

Більшість людей сходяться на думці, що Win64 / VBA7 dwMilliseconds є довгим, а не 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

PS Код від @clemo вище працює в будь-який час доби.
vknowles

@vknowles, те, що ви сказали, абсолютно вірно. А також можна компенсувати нижче наведеним методом. Оскільки цей метод справляється з мілісекундами, я думаю, що це безпечна ставка. `` `Загальний режим сну (Sleng Sleep) (sngSecs як одиночний) Dim sngEnd As Single sngEnd = Timer + (sngSecs / 1000) У той час як Timer <sngEnd DoEvents If (sngEnd - Timer)> 50000 Потім sngEnd = sngEnd - 86400 End If Wend End Sub` ` `
PravyNandas

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


УВАГА: Усі рішення, що використовують 'Application.Wait', мають неточність 1 секунди. Цей метод не враховує мілісекунд, що минули з початку поточної секунди ... Наприклад, якщо до зміни секунд часу залишилося лише 1 мілісекунда, ви будете спати лише 1 мілісекунда. Ви просто порівнюєте 2 округлі числа, а не чекайте 1 секунди!
кіберпонк

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
УВАГА: Усі рішення, що використовують '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

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

Оскільки Timer створює не цілі значення, ви повинні використовувати Loop Until Delay >= 1або ви ризикуєте перейти через 1 і ніколи не вийти з циклу.
Джон Пелтьє

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 там, де вам потрібно затримка



0

Ви можете використовувати Application.wait зараз + timevalue ("00:00:01") або Application.cait now + timeserial (0,0,1)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.