Comment faire une pause pendant une durée spécifique? (Excel / VBA)

103

J'ai une feuille de calcul Excel contenant la macro suivante. Je voudrais le faire en boucle toutes les secondes, mais je suis dangé si je peux trouver la fonction pour le faire. N'est-ce pas possible?

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

Loop
End Sub
Keng
la source

Réponses:

129

Utilisez la méthode Wait :

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

ou (pour Excel 2010 et versions ultérieures):

Application.Wait Now + #12:00:01 AM#
Ben S
la source
Je ne sais pas exactement ce que vous entendez par là, mais je supposeDoEvents que vous voulez une démonstration
Ryan Shannon
3
@Benoit et @Keng, vous pouvez mettre 1 seconde directement, en évitant de transformer du texte à ce jour. Est plus propre pour moi: Application.Wait(Now + #0:00:01#)Cheers!
A.Sommerh
29
Ce format ne fonctionnait pas dans Excel 2010. Cela fonctionne cependant:Application.Wait (Now + TimeValue("0:00:01"))
ZX9
1
DateAdd ("s", 1, Now) fait la bonne chose. Et peut être généralisé pour un long nombre de secondes: DateAdd ("s", nSec, Now) sans utiliser le littéral time. Pour dormir moins d'une seconde, utilisez l'API Sleep dans kernel32
Andrew Dennison
1
@ ZX9 timevalue ("00:00:01") = 0.0000115740 ... Donc Application.wait (maintenant + 0.00001) est d'environ une seconde. J'aime utiliser Application.wait(now + 1e-5)pendant une seconde, Application.wait(now + 0.5e-5)pendant une demi-seconde etc.
Some_Guy
61

Ajoutez ceci à votre module

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

Ou, pour les systèmes 64 bits, utilisez:

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

Appelez-le dans votre macro comme ceci:

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

Loop
End Sub
Buggabill
la source
1
Pourquoi ne pas utiliser la méthode VBA pour ce faire plutôt que d'utiliser kernel32.dll?
Ben S
10
J'ai utilisé cette méthode car elle est portable entre les différents produits de bureau tels qu'Access et n'est pas spécifique à Excel.
Buggabill
Il y a aussi une autre application VBA (ERS) que j'utilise qui a un sous-ensemble très limité de la langue.
Buggabill
18
+1, car Sleep()vous permet de spécifier des temps d'attente inférieurs à 1 seconde. Application.Waitest parfois trop granuleux.
BradC
2
@JulianKnight:Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr)
Vegard
42

à la place d'utiliser:

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

je préfère:

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

car c'est beaucoup plus facile à lire par la suite.

Achaibou Karim
la source
7
Et cela fonctionne alors que, dans Office 2013, le format # 0: 0: 1 # est converti en 1 seconde après minuit.
Julian Knight
1
AVERTISSEMENT: toutes les solutions utilisant 'Application.Wait' ont une imprécision d'une seconde. Cette méthode ne prend pas en compte les millisecondes qui se sont déjà écoulées depuis le début de la seconde en cours ... Par exemple, s'il ne reste plus que 1 milliseconde avant que les secondes ne changent, vous ne dormirez que pendant 1 milliseconde. Vous comparez simplement 2 nombres arrondis au lieu d'attendre 1 seconde!
cyberponk
18

cela fonctionne parfaitement pour moi. insérez n'importe quel code avant ou après la boucle «faire jusqu'à». Dans votre cas, mettez les 5 lignes (time1 = & time2 = & boucle "do until") à la fin de votre boucle 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
la source
2
J'aime plus cette solution parce que je ne voulais pas arrêter la file d'attente des messages.
Daniel Fuchs
Cette solution est précise et ne nécessite pas la déclaration de la fonction Sleep-API. J'avais l'habitude de stocker le temps en valeurs doubles et d'utiliser des microsecondes à la place avec le même résultat.
DrMarbuse
Cette solution fonctionne mieux que les solutions similaires ci-dessous qui utilisent Timercar l' Timerapproche échoue juste avant minuit.
vknowles
1
Cette solution n'est pas précise, elle ne prend pas en compte les millisecondes qui se sont déjà écoulées depuis l'heure actuelle ... Par exemple, s'il ne reste que 1 milliseconde jusqu'à ce que les secondes Maintenant changent, vous ne dormirez que pendant 1 milliseconde.
cyberponk
alors que d'autres solutions prenaient beaucoup de temps pour une macro VBA non basée sur MS Office, celle-ci fonctionnait parfaitement - merci!
curieux
12

La déclaration pour Sleep dans kernel32.dll ne fonctionnera pas dans Excel 64 bits. Ce serait un peu plus général:

#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
la source
2
Échec de la compilation dans Office 2013. Le vérificateur d'erreurs dans VBA pour Office 2013 semble ignorer les instructions du compilateur.
Julian Knight
2
Compile bien pour moi dans Office 2013.
Jon Peltier
La plupart des gens conviennent que Win64 / VBA7 dwMilliseconds est Long, pas LongPtr. Excel VBA cache des erreurs comme celle-ci en corrigeant la pile après des appels externes, mais des erreurs comme celle-ci planteront la plupart des versions d'Open Office
David
6

Juste une version nettoyée du code de clemo - fonctionne dans Access, qui n'a pas la fonction 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
la source
2
Si Timerdonne le nombre de secondes depuis minuit, alors cette approche échoue si elle est exécutée juste avant minuit (c'est-à-dire telle que sngEnd> = 86 400). À minuit, se Timerremet à 0 et reste donc moins que sngEndpour toujours.
vknowles
PS Le code de @clemo ci-dessus fonctionne à tout moment de la journée.
vknowles
@vknowles, ce que vous avez dit est absolument vrai. Et peut également être compensé dans la méthode ci-dessous. Puisque cette méthode gère les millisecondes, je pense que c'est une valeur sûre. `` `` 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 plupart des solutions présentées utilisent Application.Wait, qui ne prend pas en compte le temps (millisecondes) déjà écoulé depuis le début du comptage actuel des secondes, elles ont donc une imprécision intrinsèque pouvant aller jusqu'à 1 seconde .

L'approche Timer est la meilleure solution , mais il faut tenir compte de la réinitialisation à minuit, voici donc une méthode de veille très précise utilisant 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

UTILISEZ CECI POUR TESTER TOUTE FONCTION DE SOMMEIL: (ouvrir la fenêtre Immédiat de débogage: 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
la source
3

Application.Wait Second(Now) + 1

gt
la source
AVERTISSEMENT: toutes les solutions utilisant 'Application.Wait' ont une imprécision d'une seconde. Cette méthode ne prend pas en compte les millisecondes qui se sont déjà écoulées depuis le début de la seconde en cours ... Par exemple, s'il ne reste plus que 1 milliseconde avant que les secondes ne changent, vous ne dormirez que pendant 1 milliseconde. Vous comparez simplement 2 nombres arrondis au lieu d'attendre 1 seconde!
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
la source
1
AVERTISSEMENT: toutes les solutions utilisant 'Application.Wait' ont une imprécision d'une seconde. Cette méthode ne prend pas en compte les millisecondes qui se sont déjà écoulées depuis le début de la seconde en cours ... Par exemple, s'il ne reste plus que 1 milliseconde avant que les secondes ne changent, vous ne dormirez que pendant 1 milliseconde. Vous comparez simplement 2 nombres arrondis au lieu d'attendre 1 seconde!
cyberponk
2

Voici une alternative au sommeil:

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

Dans le code suivant, je fais clignoter un effet "lueur" sur un bouton de rotation pour diriger les utilisateurs vers celui-ci s'ils "ont des problèmes", l'utilisation de "sleep 1000" dans la boucle n'a entraîné aucun clignotement visible, mais la boucle fonctionne très 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
la source
1

J'ai fait faire ceci pour répondre au problème:

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
la source
AVERTISSEMENT: toutes les solutions utilisant 'Application.Wait' ont une imprécision d'une seconde. Cette méthode ne prend pas en compte les millisecondes qui se sont déjà écoulées depuis le début de la seconde en cours ... Par exemple, s'il ne reste plus que 1 milliseconde avant que les secondes ne changent, vous ne dormirez que pendant 1 milliseconde. Vous comparez simplement 2 nombres arrondis au lieu d'attendre 1 seconde!
cyberponk
1

J'utilise généralement la fonction Minuterie pour mettre l'application en pause. Insérez ce code dans le vôtre

T0 = Timer
Do
    Delay = Timer - T0
Loop Until Delay >= 1 'Change this value to pause time for a certain amount of seconds
Anastasiya-Romanova 秀
la source
3
Si Timerdonne le nombre de secondes depuis minuit, alors cette approche échoue si elle est exécutée juste avant minuit (c'est-à-dire, de telle sorte que ce T0soit inférieur au nombre de secondes de retard à partir de minuit). À minuit, se Timerréinitialise à 0 avant que la limite de délai ne soit atteinte. Delayn'atteint jamais la limite de délai, donc la boucle s'exécute indéfiniment.
vknowles
Puisque Timer produit des valeurs non entières, vous devez utiliser Loop Until Delay >= 1, sinon vous risquez de dépasser 1 et de ne jamais quitter la boucle.
Jon Peltier
1

Les fonctions d'attente et de mise en veille verrouillent Excel et vous ne pouvez rien faire d'autre avant la fin du délai. D'un autre côté, les délais de boucle ne vous donnent pas un temps d'attente exact.

Donc, j'ai fait cette solution de contournement en joignant un peu des deux concepts. Il boucle jusqu'à ce que l'heure soit celle que vous souhaitez.

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

Il vous suffit d'appeler Waste10Sec là où vous avez besoin du délai

Maycow Moura
la source
0

Essaye ça :

Threading.thread.sleep(1000)
DEV
la source
0

Vous pouvez utiliser Application.wait now + timevalue ("00:00:01") ou Application.wait now + timeserial (0,0,1)

Tuan Nguyen
la source