'Python how to make simple animated loading while process is running

This is pretty much what I have right now:

import time
import sys

done = 'false'
#here is the animation
def animate():
    while done == 'false':
        sys.stdout.write('\rloading |')
        time.sleep(0.1)
        sys.stdout.write('\rloading /')
        time.sleep(0.1)
        sys.stdout.write('\rloading -')
        time.sleep(0.1)
        sys.stdout.write('\rloading \\')
        time.sleep(0.1)
    sys.stdout.write('\rDone!     ')

animate()
#long process here
done = 'false'

and I want to get it so that the "while" script would function independently, and it continues to the process, while the animation is going on until the end of the process signals the variable "done" to be 'false', stopping the animation and replacing it with "Done!". This method would be essentially running two scripts at once; is there a way to do it?



Solution 1:[1]

Use a thread:

import itertools
import threading
import time
import sys

done = False
#here is the animation
def animate():
    for c in itertools.cycle(['|', '/', '-', '\\']):
        if done:
            break
        sys.stdout.write('\rloading ' + c)
        sys.stdout.flush()
        time.sleep(0.1)
    sys.stdout.write('\rDone!     ')

t = threading.Thread(target=animate)
t.start()

#long process here
time.sleep(10)
done = True

I also made a couple of minor modifications to your animate() function, the only really important one was adding sys.stdout.flush() after the sys.stdout.write() calls.

Solution 2:[2]

Getting inspiration from the accepted answer, here's a useful class I wrote, printing a loader à la nodejs cli:

enter image description here

from itertools import cycle
from shutil import get_terminal_size
from threading import Thread
from time import sleep


class Loader:
    def __init__(self, desc="Loading...", end="Done!", timeout=0.1):
        """
        A loader-like context manager

        Args:
            desc (str, optional): The loader's description. Defaults to "Loading...".
            end (str, optional): Final print. Defaults to "Done!".
            timeout (float, optional): Sleep time between prints. Defaults to 0.1.
        """
        self.desc = desc
        self.end = end
        self.timeout = timeout

        self._thread = Thread(target=self._animate, daemon=True)
        self.steps = ["?", "?", "?", "?", "?", "?", "?", "?"]
        self.done = False

    def start(self):
        self._thread.start()
        return self

    def _animate(self):
        for c in cycle(self.steps):
            if self.done:
                break
            print(f"\r{self.desc} {c}", flush=True, end="")
            sleep(self.timeout)

    def __enter__(self):
        self.start()

    def stop(self):
        self.done = True
        cols = get_terminal_size((80, 20)).columns
        print("\r" + " " * cols, end="", flush=True)
        print(f"\r{self.end}", flush=True)

    def __exit__(self, exc_type, exc_value, tb):
        # handle exceptions with those variables ^
        self.stop()


if __name__ == "__main__":
    with Loader("Loading with context manager..."):
        for i in range(10):
            sleep(0.25)

    loader = Loader("Loading with object...", "That was fast!", 0.05).start()
    for i in range(10):
        sleep(0.25)
    loader.stop()

Also if you're willing to use an external library you might want to look into rich's console.status

from time import sleep
from rich.console import Console

console = Console()
tasks = [f"task {n}" for n in range(1, 11)]

with console.status("[bold green]Working on tasks...") as status:
    while tasks:
        task = tasks.pop(0)
        sleep(1)
        console.log(f"{task} complete")

Solution 3:[3]

I see this is a threading problem and not just an animated loading problem. Most the answers provided in this QA thread provides only a pseudocode and left the reader on their own.

Here is an answer I am trying to give using a working example of threading and animated loading.

The reader may modify in accordance to their needs.

Import python packages

import sys, time, threading

Define your process

 # Here is an example of the process function:

def the_process_function():
    n = 20
    for i in range(n):
        time.sleep(1)
        sys.stdout.write('\r'+'loading...  process '+str(i)+'/'+str(n)+' '+ '{:.2f}'.format(i/n*100)+'%')
        sys.stdout.flush()
    sys.stdout.write('\r'+'loading... finished               \n')

Define your animated characters function

def animated_loading():
    chars = "/—\|" 
    for char in chars:
        sys.stdout.write('\r'+'loading...'+char)
        time.sleep(.1)
        sys.stdout.flush() 

Define name dan target of your thread

the_process = threading.Thread(name='process', target=the_process_function)

Start the thread

the_process.start()

while the process is alive, call the animated_loading() function

while the_process.isAlive():
    animated_loading()

The main steps are outlined in the commented out line.

Solution 4:[4]

Here is my code:

import time
import sys
print("Loading:")


#animation = ["10%", "20%", "30%", "40%", "50%", "60%", "70%", "80%", "90%", "100%"]
animation = ["[??????????]","[??????????]", "[??????????]", "[??????????]", "[??????????]", "[??????????]", "[??????????]", "[??????????]", "[??????????]", "[??????????]"]

for i in range(len(animation)):
    time.sleep(0.2)
    sys.stdout.write("\r" + animation[i % len(animation)])
    sys.stdout.flush()

print("\n")

Solution 5:[5]

Try this one

import time
import sys


animation = "|/-\\"

for i in range(100):
    time.sleep(0.1)
    sys.stdout.write("\r" + animation[i % len(animation)])
    sys.stdout.flush()
    #do something
print("End!")

Solution 6:[6]

import sys, time, threading

def your_function_name() :
    # do something here

def loadingAnimation(process) :
    while process.isAlive() :
        chars = "/—\|" 
        for char in chars:
            sys.stdout.write('\r'+'loading '+char)
            time.sleep(.1)
            sys.stdout.flush()

loading_process = threading.Thread(target=your_function_name)
loading_process.start()

loadingAnimation(loading_process)
loading_process.join()

Solution 7:[7]

from time import sleep
k='#'
j=0
k='#'
def fixed_space(i,array):
    g=(' '*len(str(len(array))))
    g=g.replace(' ','',len(str(int(i))))
    return g
def ani(i,array):
    global k
    #For accessing the global variables that are defined out of the function
    global j
    per=((i+1)*100)//len(array)
    #To calculate percentage of completion of loop
    c=per//5
    #Integer division (the value 5 decides the length of the bar)
    if c!=j:
    #When ever the values of these 2 variables change add one # to the global variable k
        k+='#'
    y='['+k+'                     '+']'
    #20 empty spaces (100/5) 
    y=y.replace(' ','',len(k))
    #To make the size of the bar fixed ever time the length of k increases one ' ' will be removed
    g=fixed_space(per,array)
    #To fix at the same position
    f=fixed_space(i,array)
    print('Status : ',y,g+str(per)+'%',' ('+f+str(i+1)+' / '+str(len(array))+' ) ',end='\r')
    #That same '\r' to clear previous text
    j=c

array = range(100)
for i in array:
    ani(i,array)
    sleep(0.1)

my code is little bit messed up so feel free to update it

Solution 8:[8]

a simple one

from time import sleep #for delay
from itertools import cycle #for infinite cycling through a list
for i in cycle(["|", "/", "-", "\\"]):
    print(i,end='\r') # '\r' clears the previous output
    sleep(0.2)

Solution 9:[9]

Try it:

import threading, sys, time, webbrowser

def search_yt(inp_yt):
    webbrowser.open(f"https://www.youtube.com/results?search_query={inp_yt}")

def loading(message="", round=1, _sleep=0.7, mark='-'):
    no = round
    hyphen = mark
    space = ["", " ", "  ", "   ", "    ", "     ", "      ", "       ", "        ", "         "]
    for loop in range(0, 10):
        sys.stdout.write(f"\r{message} [{hyphen}{space[9 - loop]}]")
        time.sleep(_sleep)
        hyphen += mark
    if no != 1:
        loading(message, no -1 , _sleep, mark)

t1 = threading.Thread(target=loading, args=("hello",1, 0.5,"=",))#loading()
t2 = threading.Thread(target=search_yt, args=("python",))

t1.start()
t2.start()

Solution 10:[10]

Its all done with a few lines of code:

import time
import os

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

done = False

while done is False:
    for i in anime:
        print('Please wait while system is Loading...', i)
        os.system('clear')
        time.sleep(0.1)
        

Tested and working successfully in the terminal.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Andrew Clark
Solution 2
Solution 3 ted
Solution 4 Community
Solution 5 Alfred George
Solution 6 dileepa
Solution 7 marc_s
Solution 8
Solution 9
Solution 10 ted