What is the best way to stop a loop from outside it?

Viewed 334

I have a python script that prints a spinner. This spinner, hopefully, will last until stopped.

spinnerFrames = [
    "/",
    "-",
    "\\",
    "|",
]

def spinner():
  i = 0
  while True:
    clearScreen() #function to clear the screen
    print(spinnerFrames[i])
    i = i + 1
    if (i == 3):
      i = 0
    sleep(0.15)

spinner()
sleep(3)
# break out here
print("Done!")

I know you can do sys.stdout.write() and then only delete that line, but that's beside the point.

I can't figure out the best way to stop the loop and exit the function. (To continue on in my code)I'd like to be able to break from the loop down where you call it, as I hope to make this a Pip package.

This, I assume is possible, though I don't know how to do it. Thanks for your help!

3 Answers

You need to run it asynchronously, like how the multiprocessing library allows you to do. When you create a separate thread, you'll be left with a handle on it that you can use to kill it when you want it to stop.

from multiprocessing import Process
from time import sleep
spinnerFrames = [
    "/",
    "-",
    "\\",
    "|",
]
def spinner():
  i = 0
  while True:
    print(spinnerFrames[i], end='\r')
    i = i + 1
    if (i == 3):
      i = 0
    sleep(0.15)
    
if __name__ == '__main__':
  p = Process(target=spinner)
  p.start()
  sleep(3)
  p.terminate()
  print("Done!")

Here is a reference implementation from one of my projects. It prints dots instead of a spinner, but it is trivial to change:

import threading
import time

def indicate_wait(func): 
    active = threading.Lock() 
     
    def dot_printer(): 
        while active.locked(): 
            print('.', end='', flush=True) 
            time.sleep(1) 
             
    def wrapper(*args, **kwargs): 
        t = threading.Thread(target=dot_printer) 
        active.acquire() 
        t.start() 
        res = func(*args, **kwargs) 
        active.release() 
        return res 
     
    return wrapper 

Example:

@indicate_wait 
def test(): 
    time.sleep(5) 

record when it started, then break loop if current time - start time > duration.

import time
spinnerFrames = [
    "/",
    "-",
    "\\",
    "|",
]

def spinner():
  i = 0
  startTime = time.time()# record starting time
  duration = 3
  while True:
    clearScreen() #function to clear the screen
    print(spinnerFrames[i])
    i = i + 1
    if (i == 3):
      i = 0
    if time.time() - startTime > duration:
      break
    sleep(0.15)

spinner()
print("Done!")
Related