1

I have a while loop that iterates for 8 seconds, and inside it is a for loop which iterates 4 times, each time executing some pyautogui press functions. However, when my loop breaks, the for loop within continues until completion. Code:

import time
import pyautogui

timeout = time.time() + 8


while time.time() < timeout:
    for i in range(4):
        pyautogui.press("1")
        pyautogui.press("1")
        pyautogui.press("1")
        pyautogui.press("1")
        pyautogui.press("1")
        pyautogui.press("1")
        pyautogui.press("1")
        pyautogui.press("1")
        pyautogui.press("1")
        pyautogui.press("1")
        pyautogui.press("4")
        pyautogui.press("enter")
print(f"{time.time() - timeout } seconds more than while loop execute time")

Print statement output:

2.4774444103240967 seconds more than while loop execute time

So, how can I get my for loop to end with my while loop? I checked around on stack overflow but found nothing.

(used pyautogui.press("") cause I got my error using them and they execute slower than print statements (can see the time difference better))

Raed Ali
  • 549
  • 1
  • 6
  • 22

2 Answers2

3

Well, your description doesn't reflect reality. The "for" loop certainly will end with the "while" loop, but you can't interrupt the "for" loop in the middle. If you want to stop in mid-keystroke, then you'll need to check the time at every keystroke:

import time
import pyautogui

timeout = time.time() + 8
msg = "11111111114*" * 4

for c in msg:
    if time.time() >= timeout:
        break
    if c == '*':
        c = 'enter'
    pyautogui.press(c)
print(f"{time.time() - timeout } seconds more than while loop execute time")

Alternatively:

import time
import pyautogui

timeout = time.time() + 8
msg = (list("11111111114")+['enter']) * 4

while time.time() < timeout:
    if msg:
        pyautogui.press(msg.pop(0))
    else:
        time.sleep(0.1)

print(f"{time.time() - timeout } seconds more than while loop execute time")
Tim Roberts
  • 48,973
  • 4
  • 21
  • 30
  • *"but you can't interrupt the "for" loop in the middle"* actually you can, but it's a bit "hacky". – Olvin Roght Mar 23 '22 at 17:54
  • If you really want to get advanced, you could use threading and timers as described here: https://stackoverflow.com/a/31667005/3280538 – flakes Mar 23 '22 at 17:56
  • Yes, but that's not helpful for this question. – Tim Roberts Mar 23 '22 at 17:57
  • Thanks very much for the answer, could you do a universal answer to this (one with a for loop in a while loop). Cause its case-specific and other users might not get much from it. Also, in my actual code, the situation is a bit different, and I need it with a for loop in a while loop format. – Raed Ali Mar 23 '22 at 23:23
  • You CANNOT do this with a "for loop in a while loop". It won't work. You could have a while loop that does `if msg:` / `pyautogui.press(msg.pop(0))`, I guess. – Tim Roberts Mar 23 '22 at 23:37
1

You can replace the body of the while loop with a call to next on an appropriately defined generator. In some sense, this is a primitive implementation of a thread; execution of the generator is interleaved with the repeated evaluations of the timeout condition. (In practice, this may only be useful as an example of how threading works.)

def my_generator():
    for i in range(4):
        yield pyautogui.press("1")
        yield pyautogui.press("1")
        yield pyautogui.press("1")
        yield pyautogui.press("1")
        yield pyautogui.press("1")
        yield pyautogui.press("1")
        yield pyautogui.press("1")
        yield pyautogui.press("1")
        yield pyautogui.press("1")
        yield pyautogui.press("1")
        yield pyautogui.press("4")
        yield pyautogui.press("enter") 
    
foo = my_generator()

while time.time() < timeout:
    next(foo)
chepner
  • 497,756
  • 71
  • 530
  • 681