Loading Animation in Python

Loading animation in python

If your output window supports the carriage return character, you can print it to make the cursor return to the beginning of the current line (provided you end your print statement with a comma, so a newline character isn't automatically printed). Then subsequent prints will overwrite what was already printed. You can use this to do very simple one line animation. Example:

import time

print "Starting program."

print "Loading ",
time.sleep(1) #do some work here...
print "\rLoading. ",
time.sleep(1) #do some more work here...
print "\rLoading.. ",
time.sleep(1) #do even more work...
print "\rLoading...",
time.sleep(1) #gratuitious amounts of work...
print "\rLoading ",

... Where time.sleep(1) is a placeholder representing the actual work you want to do.

Result:

Starting program.
Loading

Then, one second later:

Starting program.
Loading.

Then, one second later:

Starting program.
Loading..

Then, one second later:

Starting program.
Loading...

Then, one second later:

Starting program.
Loading

etc.

Compatibility note: in 3.X, print is no longer a statement, and the "end with a comma" trick no longer works. Instead, specify the end parameter:

print("\rLoading...", end="")

Waiting' animation in command prompt (Python)

Use \r and print-without-newline (that is, suffix with a comma):

animation = "|/-\\"
idx = 0
while thing_not_complete():
print(animation[idx % len(animation)], end="\r")
idx += 1
time.sleep(0.1)

For Python 2, use this print syntax:

print animation[idx % len(animation)] + "\r",

Making a loading animation when pulling data from the web in Tkinter

The reason why the program didn't work was because the functions were running in order. It worked when I ran the functions simultaneously using 'threading'.

from tkinter import*
from tkinter import ttk
import io
from PIL import Image, ImageTk
import requests,threading
from urllib.request import urlopen

class Root(Tk):
def __init__(self):
super().__init__()

self.canvas =Canvas(
width = 720, height = 1000)
self.canvas.place(relx=0,rely=0)

self.button = Button(self, text="Get The Posters",
command=self.play)
self.button.pack()

self.movie_list=["Tenet","Memento","Inception"]
self.poster_list=[]
self.x=0
self.label=Label(text="loading...please wait...")
self.pb = ttk.Progressbar(orient='horizontal',mode='indeterminate',length=480)

def animation(self):
self.button.destroy()
self.label.place(relx=0.5,rely=0.85,anchor='c')
self.pb.place(relx=0.5,rely=0.9,anchor='c')
self.pb.start()

def play(self):
self.t1 = threading.Thread(target=self.animation)
self.t2 = threading.Thread(target=self.get_posters)
self.t1.start()
self.t2.start()

def get_posters(self):

for i in self.movie_list:
self.url = "http://www.omdbapi.com/?apikey=73a4d84d&t={}".format(i)
self.response = requests.get(self.url)
self.data = self.response.json()
self.pic_url=self.data["Poster"]
self.my_page = urlopen(self.pic_url)
self.my_picture = io.BytesIO(self.my_page.read())
self.pil_img = Image.open(self.my_picture).resize((200,296))
self.tk_img = ImageTk.PhotoImage(self.pil_img)
self.poster_list.append(self.tk_img)

for n in range(len(self.poster_list)):
self.x+=30
if n==1:
self.x+=200
if n==2:
self.x+=200
self.canvas.create_image(
self.x,100,image=self.poster_list[n],anchor=NW)

self.label.destroy()
self.pb.destroy()

root = Root()
root.mainloop()

Cannot kill a loading animation when using multiprocessing

You are creating too many processes. These two lines:

foo_p = Process(target=run_script, args=(foo_path,)) # Define the processes..
bar_p = Process(target=run_script, args=(bar_path,)) # ..

create two new processes. Let's all them "A" and "B". Each process consists of this function:

def run_script(path): # Simple function that..
"""Launches python scripts.""" # ..allows me to set a..
subprocess.run(["python", path]) # ..script as a process.

which then creates another subprocess. Let's call those two processes "C" and "D". In all you have created 4 extra processes, instead of just the 2 that you need. It is actually process "C" that's producing the output on the terminal. This line:

bar_p.join()

waits for "B" to terminate, which implies that "D" has terminated. But this line:

foo_p.kill() 

kills process "A" but orphans process "C". So the output to the terminal continues forever.

This is well documented - see the description of multiprocessing.terminate, which says:

"Note that descendant processes of the process will not be terminated – they will simply become orphaned."

The following program works as you intended, exiting gracefully from the second process after the first one has finished. (I renamed "foo.py" to useless.py and "bar.py" to useful.py, and made small changes so I could run it on my computer.)

import subprocess
import os

def run_script(name):
s = os.path.join(r"c:\pyproj310\so", name)
return subprocess.Popen(["py", s])

if __name__ == "__main__":
useless_p = run_script("useless.py")
useful_p = run_script("useful.py")

useful_p.wait() # Wait for useful script to finish executing
useless_p.kill() # Kill loading animation

You can't use subprocess.run() to launch the new processes since that function will block the main script until the process completes. So I used Popen instead. Also I placed the running code under an if __name__ == "__main__" which is good practice (and maybe necessary on Windows).

Play an animated GIF in python with tkinter while loading

First using while loop and time.sleep() in the main thread of a tkinter application is not recommended because it will block tkinter mainloop() and then cause the GUI not responding.

Suggest to:

  • use .after() to show the animated GIF because it is not safe to update tkinter widgets in a thread
  • use thread on loadingAnimation() instead
import threading
import tkinter
import sys
import time

root = tkinter.Tk()
frames = [tkinter.PhotoImage(file='./myScripts/M-95.gif', format='gif -index %i'%(i)) for i in range(10)]

def center_window(win):
win.wait_visibility() # make sure the window is ready
x = (win.winfo_screenwidth() - win.winfo_width()) // 2
y = (win.winfo_screenheight() - win.winfo_height()) // 2
win.geometry(f'+{x}+{y}')

def M_95(n=0, top=None, lbl=None):
# Play GIF (file name = m95.gif) in a 320x320 tkinter window
# Play GIF concurrently with the loading animation below
# Close tkinter window after play
global process_is_alive # used in loadingAnimation()
delay = 4000 // len(frames) # make one cycle of animation around 4 secs
if n == 0:
root.withdraw()
top = tkinter.Toplevel()
lbl = tkinter.Label(top, image=frames[0])
lbl.pack()
center_window(top)
process_is_alive = True
if n < len(frames)-1:
lbl.config(image=frames[n])
lbl.after(delay, M_95, n+1, top, lbl)
else:
top.destroy()
root.deiconify()
process_is_alive = False

def loadingAnimation():
animation = ["[■□□□□□□□□□]","[■■□□□□□□□□]", "[■■■□□□□□□□]", "[■■■■□□□□□□]", "[■■■■■□□□□□]", "[■■■■■■□□□□]", "[■■■■■■■□□□]", "[■■■■■■■■□□]", "[■■■■■■■■■□]", "[■■■■■■■■■■]"]
i = 0
while process_is_alive:
sys.stdout.write("\r | Loading..." + animation[i % len(animation)])
sys.stdout.flush()
time.sleep(0.4)
i += 1

M_95() # start GIF animation
threading.Thread(target=loadingAnimation).start()

root.mainloop()

Update: animate GIF more than one cycle in around 4 secs:

def M_95(n=0, top=None, lbl=None):
# Play GIF (file name = m95.gif) in a 320x320 tkinter window
# Play GIF concurrently with the loading animation below
# Close tkinter window after play
global process_is_alive
num_cycles = 2
count = len(frames) * num_cycles
delay = 4000 // count # make required cycles of animation in around 4 secs
if n == 0:
root.withdraw()
top = tkinter.Toplevel()
lbl = tkinter.Label(top, image=frames[0])
lbl.pack()
center_window(top)
process_is_alive = True
lbl.after(delay, M_95, n+1, top, lbl)
elif n < count-1:
lbl.config(image=frames[n%len(frames)])
lbl.after(delay, M_95, n+1, top, lbl)
else:
top.destroy()
root.destroy()
process_is_alive = False

Triple dots animation while program is loading in terminal

Below is a complete solution that works in terminal

from time import sleep

def printd(text, delay=.5):
print(end=text)
n_dots = 0

while True:
if n_dots == 3:
print(end='\b\b\b', flush=True)
print(end=' ', flush=True)
print(end='\b\b\b', flush=True)
n_dots = 0
else:
print(end='.', flush=True)
n_dots += 1
sleep(delay)

flush=True forces immediate print (vs buffering)



Related Topics



Leave a reply



Submit