Progress Bar not updating in Python container on Kubernetes - python

I am trying to get a simple progress bar working in my Python container on Kubernetes. However, it doesn't output anything until the job is done and the progress bar reaches 100%. The same exact code works perfectly in a local Docker container. So what is it that Kubernetes has in place that prevents me from seeing the progress bar updating in real time in its logs?
Progress bar code:
import sys
import time
class Color:
BOLD = '\033[1m'
ENDC = '\033[0m'
ERASE_LINE = '\x1b[2K\r'
def percentage(current, total):
percent = 100 * float(current) / float(total)
return percent
class ProgressBar:
def __init__(self, total):
self._total = total
self._current = 0
self.print()
def update(self):
self._current += 1
self.print()
def print(self):
percent = percentage(self._current, self._total)
sys.stdout.write("\r")
sys.stdout.write(Color.BOLD + "[%-50s] %d%%" % ('=' * int(percent / 2), percent) + Color.ENDC)
sys.stdout.flush()
if __name__=="__main__":
print("Ready to run soon...")
time.sleep(10)
print("Running!")
pbar = ProgressBar(255)
for i in range(255):
time.sleep(0.03)
pbar.update()

When logging, rather than displaying things in a TTY to a human, you generally need to log in complete lines ending in \n. Rather than a progress bar, I would usually recommend something like printing out 10%...\n20%...\n etc. Up to you how often you print the current state.
Update:
You can update your script to detect if the terminal is TTY, and change the behaviour accordingly
Use this:
import os, sys
if os.isatty(sys.stdout.fileno()):

Related

Print array data 1 by 1 sleeping between each iteration? [duplicate]

Is there a way to print a spinning cursor in a terminal using Python?
Something like this, assuming your terminal handles \b
import sys
import time
def spinning_cursor():
while True:
for cursor in '|/-\\':
yield cursor
spinner = spinning_cursor()
for _ in range(50):
sys.stdout.write(next(spinner))
sys.stdout.flush()
time.sleep(0.1)
sys.stdout.write('\b')
Easy to use API (this will run the spinner in a separate thread):
import sys
import time
import threading
class Spinner:
busy = False
delay = 0.1
#staticmethod
def spinning_cursor():
while 1:
for cursor in '|/-\\': yield cursor
def __init__(self, delay=None):
self.spinner_generator = self.spinning_cursor()
if delay and float(delay): self.delay = delay
def spinner_task(self):
while self.busy:
sys.stdout.write(next(self.spinner_generator))
sys.stdout.flush()
time.sleep(self.delay)
sys.stdout.write('\b')
sys.stdout.flush()
def __enter__(self):
self.busy = True
threading.Thread(target=self.spinner_task).start()
def __exit__(self, exception, value, tb):
self.busy = False
time.sleep(self.delay)
if exception is not None:
return False
Now use it in a with block anywhere in the code:
with Spinner():
# ... some long-running operations
# time.sleep(3)
A nice pythonic way is to use itertools.cycle:
import itertools, sys
spinner = itertools.cycle(['-', '/', '|', '\\'])
while True:
sys.stdout.write(next(spinner)) # write the next character
sys.stdout.flush() # flush stdout buffer (actual character display)
sys.stdout.write('\b') # erase the last written char
Also, you might want to use threading to display the spinner during a long function call, as in http://www.interclasse.com/scripts/spin.php
For completeness I want to add the great package halo. It offers a lot of preset spinners and higher level customization options.
Extract from their readme
from halo import Halo
spinner = Halo(text='Loading', spinner='dots')
spinner.start()
# Run time consuming work here
# You can also change properties for spinner as and when you want
spinner.stop()
Alternatively, you can use halo with Python's with statement:
from halo import Halo
with Halo(text='Loading', spinner='dots'):
# Run time consuming work here
Finally, you can use halo as a decorator:
from halo import Halo
#Halo(text='Loading', spinner='dots')
def long_running_function():
# Run time consuming work here
pass
long_running_function()
A solution:
import sys
import time
print "processing...\\",
syms = ['\\', '|', '/', '-']
bs = '\b'
for _ in range(10):
for sym in syms:
sys.stdout.write("\b%s" % sym)
sys.stdout.flush()
time.sleep(.5)
The key is to use the backspace character '\b' and flush stdout.
Improved version from #Victor Moyseenko
as the original version had few issues
was leaving spinner's characters after spinning is complete
and sometimes lead to removing following output's first character too
avoids a rare race condition by putting threading.Lock() on output
falls back to simpler output when no tty is available (no spinning)
import sys
import threading
import itertools
import time
class Spinner:
def __init__(self, message, delay=0.1):
self.spinner = itertools.cycle(['-', '/', '|', '\\'])
self.delay = delay
self.busy = False
self.spinner_visible = False
sys.stdout.write(message)
def write_next(self):
with self._screen_lock:
if not self.spinner_visible:
sys.stdout.write(next(self.spinner))
self.spinner_visible = True
sys.stdout.flush()
def remove_spinner(self, cleanup=False):
with self._screen_lock:
if self.spinner_visible:
sys.stdout.write('\b')
self.spinner_visible = False
if cleanup:
sys.stdout.write(' ') # overwrite spinner with blank
sys.stdout.write('\r') # move to next line
sys.stdout.flush()
def spinner_task(self):
while self.busy:
self.write_next()
time.sleep(self.delay)
self.remove_spinner()
def __enter__(self):
if sys.stdout.isatty():
self._screen_lock = threading.Lock()
self.busy = True
self.thread = threading.Thread(target=self.spinner_task)
self.thread.start()
def __exit__(self, exception, value, tb):
if sys.stdout.isatty():
self.busy = False
self.remove_spinner(cleanup=True)
else:
sys.stdout.write('\r')
example of usage of the Spinner class above:
with Spinner("just waiting a bit.. "):
time.sleep(3)
uploaded code to https://github.com/Tagar/stuff/blob/master/spinner.py
Nice, simple, and clean...
while True:
for i in '|\\-/':
print('\b' + i, end='')
Sure, it's possible. It's just a question of printing the backspace character (\b) in between the four characters that would make the "cursor" look like it's spinning ( -, \, |, /).
I have found py-spin package on GitHub. It has many nice spinning Styles. Here are some sample about how to use, Spin1 is the \-/ style:
from __future__ import print_function
import time
from pyspin.spin import make_spin, Spin1
# Choose a spin style and the words when showing the spin.
#make_spin(Spin1, "Downloading...")
def download_video():
time.sleep(10)
if __name__ == '__main__':
print("I'm going to download a video, and it'll cost much time.")
download_video()
print("Done!")
time.sleep(0.1)
It is also possible to control the spin manualy:
from __future__ import print_function
import sys
import time
from pyspin.spin import Spin1, Spinner
# Choose a spin style.
spin = Spinner(Spin1)
# Spin it now.
for i in range(50):
print(u"\r{0}".format(spin.next()), end="")
sys.stdout.flush()
time.sleep(0.1)
Other styles in the below gif.
Grab the awesome progressbar module - http://code.google.com/p/python-progressbar/
use RotatingMarker.
I built a generic Singleton, shared by the entire application
from itertools import cycle
import threading
import time
class Spinner:
__default_spinner_symbols_list = ['|-----|', '|#----|', '|-#---|', '|--#--|', '|---#-|', '|----#|']
def __init__(self, spinner_symbols_list: [str] = None):
spinner_symbols_list = spinner_symbols_list if spinner_symbols_list else Spinner.__default_spinner_symbols_list
self.__screen_lock = threading.Event()
self.__spinner = cycle(spinner_symbols_list)
self.__stop_event = False
self.__thread = None
def get_spin(self):
return self.__spinner
def start(self, spinner_message: str):
self.__stop_event = False
time.sleep(0.3)
def run_spinner(message):
while not self.__stop_event:
print("\r{message} {spinner}".format(message=message, spinner=next(self.__spinner)), end="")
time.sleep(0.3)
self.__screen_lock.set()
self.__thread = threading.Thread(target=run_spinner, args=(spinner_message,), daemon=True)
self.__thread.start()
def stop(self):
self.__stop_event = True
if self.__screen_lock.is_set():
self.__screen_lock.wait()
self.__screen_lock.clear()
print("\r", end="")
print("\r", end="")
if __name__ == '__main__':
import time
# Testing
spinner = Spinner()
spinner.start("Downloading")
# Make actions
time.sleep(5) # Simulate a process
#
spinner.stop()
You can write '\r\033[K' to clear the current line. And the following is a example modified from #nos.
import sys
import time
def spinning_cursor():
while True:
for cursor in '|/-\\':
yield cursor
spinner = spinning_cursor()
for _ in range(1, 10):
content = f'\r{next(spinner)} Downloading...'
print(content, end="")
time.sleep(0.1)
print('\r\033[K', end="")
For anyone who interested in nodejs, I also write a nodejs example.
function* makeSpinner(start = 0, end = 100, step = 1) {
let iterationCount = 0;
while (true) {
for (const char of '|/-\\') {
yield char;
}
}
return iterationCount;
}
async function sleep(seconds) {
return new Promise((resolve) => {
setTimeout(resolve, seconds * 1000);
});
}
(async () => {
const spinner = makeSpinner();
for (let i = 0; i < 10; i++) {
content = `\r${spinner.next().value} Downloading...`;
process.stdout.write(content);
await sleep(0.1);
process.stdout.write('\r\033[K');
}
})();
curses module. i'd have a look at the addstr() and addch() functions. Never used it though.
For more advanced console manipulations, on unix you can use the curses python module, and on windows, you can use WConio which provides equivalent functionality of the curses library.
#!/usr/bin/env python
import sys
chars = '|/-\\'
for i in xrange(1,1000):
for c in chars:
sys.stdout.write(c)
sys.stdout.write('\b')
sys.stdout.flush()
CAVEATS:
In my experience this doesn't work in all terminals. A more robust way to do this under Unix/Linux, be it more complicated is to use the curses module, which doesn't work under Windows.
You probably want to slow it down some how with actual processing that is going on in the background.
Here ya go - simple and clear.
import sys
import time
idx = 0
cursor = ['|','/','-','\\'] #frames of an animated cursor
while True:
sys.stdout.write(cursor[idx])
sys.stdout.write('\b')
idx = idx + 1
if idx > 3:
idx = 0
time.sleep(.1)
Crude but simple solution:
import sys
import time
cursor = ['|','/','-','\\']
for count in range(0,1000):
sys.stdout.write('\b{}'.format(cursor[count%4]))
sys.stdout.flush()
# replace time.sleep() with some logic
time.sleep(.1)
There are obvious limitations, but again, crude.
I propose a solution using decorators
from itertools import cycle
import functools
import threading
import time
def spinner(message, spinner_symbols: list = None):
spinner_symbols = spinner_symbols or list("|/-\\")
spinner_symbols = cycle(spinner_symbols)
global spinner_event
spinner_event = True
def start():
global spinner_event
while spinner_event:
symbol = next(spinner_symbols)
print("\r{message} {symbol}".format(message=message, symbol=symbol), end="")
time.sleep(0.3)
def stop():
global spinner_event
spinner_event = False
print("\r", end="")
def external(fct):
#functools.wraps(fct)
def wrapper(*args):
spinner_thread = threading.Thread(target=start, daemon=True)
spinner_thread.start()
result = fct(*args)
stop()
spinner_thread.join()
return result
return wrapper
return external
Simple usage
#spinner("Downloading")
def f():
time.sleep(10)
import sys
def DrowWaitCursor(counter):
if counter % 4 == 0:
print("/",end = "")
elif counter % 4 == 1:
print("-",end = "")
elif counter % 4 == 2:
print("\\",end = "")
elif counter % 4 == 3:
print("|",end = "")
sys.stdout.flush()
sys.stdout.write('\b')
This can be also another solution using a function with a parameter.
I just started with python about a week ago and found this posting. I've combined a bit of what I found here with stuff I learned about threads and queues elsewhere to provide a much better implementation in my opinion. In my solution, writing to the screen is handled by a thread that checks a queue for content. If that queue has content, the cursor spinning thread knows to stop. On the flipside, the cursor spinning thread uses a queue as a lock so the printing thread knows not to print until a full pass of the spinner code is complete. This prevents race conditions and a lot of excess code people are using to keep the console clean.
See below:
import threading, queue, itertools, sys, time # all required for my version of spinner
import datetime #not required for spinning cursor solution, only my example
console_queue = queue.Queue() # this queue should be initialized before functions
screenlock = queue.Queue() # this queue too...
def main():
threading.Thread(target=spinner).start()
threading.Thread(target=consoleprint).start()
while True:
# instead of invoking print or stdout.write, we just add items to the console_queue
# The next three lines are an example of my code in practice.
time.sleep(.5) # wait half a second
currenttime = "[" + datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S") + "] "
console_queue.put(currenttime) # The most important part. Substitute your print and stdout functions with this.
def spinner(console_queue = console_queue, screenlock = screenlock):
spinnerlist = itertools.cycle(['|', '/', '-', '\\'])
while True:
if console_queue.empty():
screenlock.put("locked")
sys.stdout.write(next(spinnerlist))
sys.stdout.flush()
sys.stdout.write('\b')
sys.stdout.flush()
screenlock.get()
time.sleep(.1)
def consoleprint(console_queue = console_queue, screenlock = screenlock):
while True:
if not console_queue.empty():
while screenlock.empty() == False:
time.sleep(.1)
sys.stdout.flush()
print(console_queue.get())
sys.stdout.flush()
if __name__ == "__main__":
main()
Having said all I said and written all I've written, I've only been doing python stuff for a week. If there are cleaner ways of doing this or I missed some best practices I'd love to learn. Thanks.
import requests
import time
import sys
weathercity = input("What city are you in? ")
weather = requests.get('http://api.openweathermap.org/data/2.5/weather?q='+weathercity+'&appid=886705b4c1182eb1c69f28eb8c520e20')
url = ('http://api.openweathermap.org/data/2.5/weather?q='+weathercity+'&appid=886705b4c1182eb1c69f28eb8c520e20')
def spinning_cursor():
while True:
for cursor in '|/-\\':
yield cursor
data = weather.json()
temp = data['main']['temp']
description = data['weather'][0]['description']
weatherprint ="In {}, it is currently {}°C with {}."
spinner = spinning_cursor()
for _ in range(25):
sys.stdout.write(next(spinner))
sys.stdout.flush()
time.sleep(0.1)
sys.stdout.write('\b')
convert = int(temp - 273.15)
print(weatherprint.format(weathercity, convert, description))
if you wanna python text spinner you can look picture
Simple:print_spinner("Hayatta en hakiki mürşit ilimdir.")
Here is the simplest loading spinner for python:
import time
spin=["loading...... ", "|", "/","-", "\"]
for i in spin:
print("\b"+i, end=" ")
time.sleep (0.2)
Output:
loading...... |

python cmd line application with blessings and cmd

I would like to write cmd-line application with Python, which in the upper part of the terminal shows the status of something, I'd like to monitor, while in the lower part I (and my colleages) have the normal ipython interpreter, which allows us to manipulate the behaviour of the application.
In order to show the status display, I was thinking of using blessings. At first I thought about using cmd or cmd2 in order to allow run-time manipulation of the status display, but then I though, why should one implement a lot of do_something methods, when one can have the same functionality (including tab-completion and online help) for free, using the ipython interpreter.
Here is my first approach
## -- first_try.py
import time
import random
from threading import Thread
from blessings import Terminal
term = Terminal()
def display_func1():
print time.asctime()
print "Some int: ", random.randint(0, 10)
print "Some float: ", random.random()*10
def display_func2():
print time.asctime()
print "Some int: ", random.randint(0, 10)
print " details:", [random.randint(0, 10) for i in range(7)]
print "Some float: ", random.random()*10
print " details:", [random.random()*10 for i in range(5)]
class StatusDisplay(Thread):
def __init__(self, display_func):
self._text = ""
self._stop = False
self.display = display_func
Thread.__init__(self)
def run(self):
while not self._stop:
with term.location():
self.clear_top_lines()
print term.move(0, 0)
print 70*'-'
self.display()
print 70*'-'
self.print_help()
time.sleep(0.1)
def stop(self):
self._stop = True
def print_help(self):
print
print "In order to manipulate the status display"
print "type: "
print " sd.display = display_func2 and if you want to go back."
print " sd.display = display_func1"
print " Or implement your own display function and hook it into the display"
def clear_top_lines(self, n=10):
with term.location():
for i in range(n):
print term.move(0, i) + term.clear_eol()
time.sleep(2)
sd = StatusDisplay(display_func1)
sd.daemon = True
sd.start()
When I invoke this script, e.g. like this:
ipython -i --no-banner first_try.py
And hit a couple of times enter, then I have in the top of the page, something that looks like the status display, I'd like to have, and in the lower part, I can still work with the ipython interpreter, due to the -i parameter.
I think I am working here in the wrong direction, but I really would like to have this kind of feature in my application. Can somebody give me a push into the right direction?

multiprocessing: a progress bar for each task

I have a class that implements an iterative algorithm. Since the execution takes a while and I need to average the results over many executions, I decided to use multiprocessing. The thing is that I would like to have a progress bar (or something less fancy) for every execution. Something like:
experiment 1 [##########] 60%
experiment 2 [#############] 70%
experiment 3 [###] 20%
My class looks like this (note that I already used a progress bar and that I would like to keep it there so that I continues to work when I do not parallelize):
from __future__ import division
from time import sleep
class Algo():
def __init__(self, total_iters, arg1, arg2, name):
self.total_iters = total_iters
self.arg1 = arg1
self.arg2 = arg2
self.name = name
def step(self, iteration):
"""
One iteration of Algorithm
"""
# Progress bar
completed = 100*iteration/self.total_iters
if completed == 0: print ""
print '\r {2} [{0}] {1}%'.format('#'*(int(completed/5)), completed, self.name),
# Do some stuff
sleep(0.001)
def run(self):
for i in xrange(self.total_iters):
self.step(i)
# Output the final result in unique file
And this is my try:
import multiprocessing as mp
if __name__ == "__main__":
algo1 = Algo(200, 0,0, "test1")
pool = mp.Pool(processes=3)
for i in xrange(3):
pool.apply_async(algo1.run) # in real life run will be passed N arguments
pool.close()
pool.join()
Any ideas?
PS: I am trying to avoid curses
Quick and dirty and in python 3, but you will get the idea ;)
import random
import time
import multiprocessing
import os
import collections
class Algo(multiprocessing.Process):
def __init__(self, steps, name, status_queue):
multiprocessing.Process.__init__(self)
self.steps = steps
self.name = name
self.status_queue = status_queue
def step(self, step):
# Progress bar
self.status_queue.put((self.name, (step+1.0)/self.steps))
# Do some stuff
time.sleep(0.1)
def run(self):
for i in range(self.steps):
self.step(i)
def print_progress(progress):
# Windows:
os.system('cls')
for name, percent in progress.items():
percent = int(percent * 100)
bar = ('#' * int(percent/10)) + (' ' * (10 - int(percent/10)))
print("{}: [{}] {}%".format(name, bar, percent))
if __name__ == "__main__":
status = multiprocessing.Queue()
progress = collections.OrderedDict()
algos = [Algo(random.randrange(100, 200), "test" + str(i), status) for i in range(3)]
for a in algos:
a.start()
while any([a.is_alive() for a in algos]):
while not status.empty():
name, percent = status.get()
progress[name] = percent
print_progress(progress)
time.sleep(0.1)

How to run a function periodically in python

I have a simple metronome running and for some reason when it is at a lower bpm it is fine, but at higher bpms it is inconsistent and isnt steady.
I don't know what is going on.
I want to try using something to run it periodically. Is there a way to do that?
Here is my code:
class thalam():
def __init__(self,root,e):
self.lag=0.2
self.root=root
self.count=0
self.thread=threading.Thread(target=self.play)
self.thread.daemon=True
self.tempo=60.0/120
self.e=e
self.pause=False
self.tick=open("tick.wav","rb").read()
self.count=0
self.next_call = time.time()
def play(self):
if self.pause:
return
winsound.PlaySound(self.tick,winsound.SND_MEMORY)
self.count+=1
if self.count==990:
self.thread=threading.Thread(target=self.play)
self.thread.daemon=True
self.thread.start()
return
self.next_call+=self.tempo
new=threading.Timer(self.next_call-time.time(),self.play)
new.daemon=True
new.start()
def stop(self):
self.pause=True
winsound.PlaySound(None,winsound.SND_ASYNC)
def start(self):
self.pause=False
def settempo(self,a):
self.tempo=a
class Metronome(Frame):
def __init__(self,root):
Frame.__init__(self,root)
self.first=True
self.root=root
self.e=Entry(self)
self.e.grid(row=0,column=1)
self.e.insert(0,"120")
self.play=Button(self,text="Play",command=self.tick)
self.play.grid(row=1,column=1)
self.l=Button(self,text="<",command=lambda:self.inc("l"))
self.l.grid(row=0,column=0)
self.r=Button(self,text=">",command=lambda:self.inc("r"))
self.r.grid(row=0,column=2)
def tick(self):
self.beat=thalam(root,self.e)
self.beat.thread.start()
self.play.configure(text="Stop",command=self.notick)
def notick(self):
self.play.configure(text="Start",command=self.tick)
self.beat.stop()
def inc(self,a):
if a=="l":
try:
new=str(int(self.e.get())-5)
self.e.delete(0, END)
self.e.insert(0,new)
self.beat.settempo(60.0/(int(self.e.get())))
except:
print "Invalid BPM"
return
elif a=="r":
try:
new=str(int(self.e.get())+5)
self.e.delete(0, END)
self.e.insert(0,new)
self.beat.settempo((60.0/(int(self.e.get()))))
except:
print "Invalid BPM"
return
Playing sound to emulate an ordinary metronome doesn't require "real-time" capabilities.
It looks like you use Tkinter framework to create the GUI. root.after() allows you to call a function with a delay. You could use it to implement ticks:
def tick(interval, function, *args):
root.after(interval - timer() % interval, tick, interval, function, *args)
function(*args) # assume it doesn't block
tick() runs function with given args every interval milliseconds. Duration of individual ticks is affected by root.after() precision but in the long run, the stability depends only on timer() function.
Here's a script that prints some stats, 240 beats per minute:
#!/usr/bin/env python
from __future__ import division, print_function
import sys
from timeit import default_timer
try:
from Tkinter import Tk
except ImportError: # Python 3
from tkinter import Tk
def timer():
return int(default_timer() * 1000 + .5)
def tick(interval, function, *args):
root.after(interval - timer() % interval, tick, interval, function, *args)
function(*args) # assume it doesn't block
def bpm(milliseconds):
"""Beats per minute."""
return 60000 / milliseconds
def print_tempo(last=[timer()], total=[0], count=[0]):
now = timer()
elapsed = now - last[0]
total[0] += elapsed
count[0] += 1
average = total[0] / count[0]
print("{:.1f} BPM, average: {:.0f} BPM, now {}"
.format(bpm(elapsed), bpm(average), now),
end='\r', file=sys.stderr)
last[0] = now
interval = 250 # milliseconds
root = Tk()
root.withdraw() # don't show GUI
root.after(interval - timer() % interval, tick, interval, print_tempo)
root.mainloop()
The tempo osculates only by one beat: 240±1 on my machine.
Here's asyncio analog:
#!/usr/bin/env python3
"""Metronome in asyncio."""
import asyncio
import sys
async def async_main():
"""Entry point for the script."""
timer = asyncio.get_event_loop().time
last = timer()
def print_tempo(now):
nonlocal last
elapsed = now - last
print(f"{60/elapsed:03.1f} BPM", end="\r", file=sys.stderr)
last = now
interval = 0.250 # seconds
while True:
await asyncio.sleep(interval - timer() % interval)
print_tempo(timer())
if __name__ == "__main__":
asyncio.run(async_main())
See Talk: Łukasz Langa - AsyncIO + Music
Doing anything needing time precision is very difficult due to the need for the processor to share itself with other programs. Unfortunately for timing critical programs the operating system is free to switch to another process whenever it chooses. This could mean that it may not return to your program until after a noticeable delay. Using time.sleep after import time is a more consistent way of trying to balance the time between beeps because the processor has less "reason" to switch away. Although sleep on Windows has a default granularity of 15.6ms, but I assume you will not need to play a beat in excess 64Hz. Also it appears that you are using multithreading to try and address your issue, however, the python implementation of threading sometimes forces the threads to run sequentially. This makes matters even worse for switching away from your process.
I feel that the best solution would be to generate sound data containing the metronome beep at the frequency desired. Then you could play the sound data in a way the OS understands well. Since the system knows how to handle sound in a reliable manner your metronome would then work.
Sorry to disappoint but timing critical applications are VERY difficult unless you want to get your hands dirty with the system you are working with.
I would like to tell you that you can't be precise with threads in case of timing because of race conditions (even when you are using locks and semaphores!). Even I have faced the problem.

How to create a spinning command line cursor?

Is there a way to print a spinning cursor in a terminal using Python?
Something like this, assuming your terminal handles \b
import sys
import time
def spinning_cursor():
while True:
for cursor in '|/-\\':
yield cursor
spinner = spinning_cursor()
for _ in range(50):
sys.stdout.write(next(spinner))
sys.stdout.flush()
time.sleep(0.1)
sys.stdout.write('\b')
Easy to use API (this will run the spinner in a separate thread):
import sys
import time
import threading
class Spinner:
busy = False
delay = 0.1
#staticmethod
def spinning_cursor():
while 1:
for cursor in '|/-\\': yield cursor
def __init__(self, delay=None):
self.spinner_generator = self.spinning_cursor()
if delay and float(delay): self.delay = delay
def spinner_task(self):
while self.busy:
sys.stdout.write(next(self.spinner_generator))
sys.stdout.flush()
time.sleep(self.delay)
sys.stdout.write('\b')
sys.stdout.flush()
def __enter__(self):
self.busy = True
threading.Thread(target=self.spinner_task).start()
def __exit__(self, exception, value, tb):
self.busy = False
time.sleep(self.delay)
if exception is not None:
return False
Now use it in a with block anywhere in the code:
with Spinner():
# ... some long-running operations
# time.sleep(3)
A nice pythonic way is to use itertools.cycle:
import itertools, sys
spinner = itertools.cycle(['-', '/', '|', '\\'])
while True:
sys.stdout.write(next(spinner)) # write the next character
sys.stdout.flush() # flush stdout buffer (actual character display)
sys.stdout.write('\b') # erase the last written char
Also, you might want to use threading to display the spinner during a long function call, as in http://www.interclasse.com/scripts/spin.php
For completeness I want to add the great package halo. It offers a lot of preset spinners and higher level customization options.
Extract from their readme
from halo import Halo
spinner = Halo(text='Loading', spinner='dots')
spinner.start()
# Run time consuming work here
# You can also change properties for spinner as and when you want
spinner.stop()
Alternatively, you can use halo with Python's with statement:
from halo import Halo
with Halo(text='Loading', spinner='dots'):
# Run time consuming work here
Finally, you can use halo as a decorator:
from halo import Halo
#Halo(text='Loading', spinner='dots')
def long_running_function():
# Run time consuming work here
pass
long_running_function()
A solution:
import sys
import time
print "processing...\\",
syms = ['\\', '|', '/', '-']
bs = '\b'
for _ in range(10):
for sym in syms:
sys.stdout.write("\b%s" % sym)
sys.stdout.flush()
time.sleep(.5)
The key is to use the backspace character '\b' and flush stdout.
Improved version from #Victor Moyseenko
as the original version had few issues
was leaving spinner's characters after spinning is complete
and sometimes lead to removing following output's first character too
avoids a rare race condition by putting threading.Lock() on output
falls back to simpler output when no tty is available (no spinning)
import sys
import threading
import itertools
import time
class Spinner:
def __init__(self, message, delay=0.1):
self.spinner = itertools.cycle(['-', '/', '|', '\\'])
self.delay = delay
self.busy = False
self.spinner_visible = False
sys.stdout.write(message)
def write_next(self):
with self._screen_lock:
if not self.spinner_visible:
sys.stdout.write(next(self.spinner))
self.spinner_visible = True
sys.stdout.flush()
def remove_spinner(self, cleanup=False):
with self._screen_lock:
if self.spinner_visible:
sys.stdout.write('\b')
self.spinner_visible = False
if cleanup:
sys.stdout.write(' ') # overwrite spinner with blank
sys.stdout.write('\r') # move to next line
sys.stdout.flush()
def spinner_task(self):
while self.busy:
self.write_next()
time.sleep(self.delay)
self.remove_spinner()
def __enter__(self):
if sys.stdout.isatty():
self._screen_lock = threading.Lock()
self.busy = True
self.thread = threading.Thread(target=self.spinner_task)
self.thread.start()
def __exit__(self, exception, value, tb):
if sys.stdout.isatty():
self.busy = False
self.remove_spinner(cleanup=True)
else:
sys.stdout.write('\r')
example of usage of the Spinner class above:
with Spinner("just waiting a bit.. "):
time.sleep(3)
uploaded code to https://github.com/Tagar/stuff/blob/master/spinner.py
Nice, simple, and clean...
while True:
for i in '|\\-/':
print('\b' + i, end='')
Sure, it's possible. It's just a question of printing the backspace character (\b) in between the four characters that would make the "cursor" look like it's spinning ( -, \, |, /).
I have found py-spin package on GitHub. It has many nice spinning Styles. Here are some sample about how to use, Spin1 is the \-/ style:
from __future__ import print_function
import time
from pyspin.spin import make_spin, Spin1
# Choose a spin style and the words when showing the spin.
#make_spin(Spin1, "Downloading...")
def download_video():
time.sleep(10)
if __name__ == '__main__':
print("I'm going to download a video, and it'll cost much time.")
download_video()
print("Done!")
time.sleep(0.1)
It is also possible to control the spin manualy:
from __future__ import print_function
import sys
import time
from pyspin.spin import Spin1, Spinner
# Choose a spin style.
spin = Spinner(Spin1)
# Spin it now.
for i in range(50):
print(u"\r{0}".format(spin.next()), end="")
sys.stdout.flush()
time.sleep(0.1)
Other styles in the below gif.
Grab the awesome progressbar module - http://code.google.com/p/python-progressbar/
use RotatingMarker.
I built a generic Singleton, shared by the entire application
from itertools import cycle
import threading
import time
class Spinner:
__default_spinner_symbols_list = ['|-----|', '|#----|', '|-#---|', '|--#--|', '|---#-|', '|----#|']
def __init__(self, spinner_symbols_list: [str] = None):
spinner_symbols_list = spinner_symbols_list if spinner_symbols_list else Spinner.__default_spinner_symbols_list
self.__screen_lock = threading.Event()
self.__spinner = cycle(spinner_symbols_list)
self.__stop_event = False
self.__thread = None
def get_spin(self):
return self.__spinner
def start(self, spinner_message: str):
self.__stop_event = False
time.sleep(0.3)
def run_spinner(message):
while not self.__stop_event:
print("\r{message} {spinner}".format(message=message, spinner=next(self.__spinner)), end="")
time.sleep(0.3)
self.__screen_lock.set()
self.__thread = threading.Thread(target=run_spinner, args=(spinner_message,), daemon=True)
self.__thread.start()
def stop(self):
self.__stop_event = True
if self.__screen_lock.is_set():
self.__screen_lock.wait()
self.__screen_lock.clear()
print("\r", end="")
print("\r", end="")
if __name__ == '__main__':
import time
# Testing
spinner = Spinner()
spinner.start("Downloading")
# Make actions
time.sleep(5) # Simulate a process
#
spinner.stop()
You can write '\r\033[K' to clear the current line. And the following is a example modified from #nos.
import sys
import time
def spinning_cursor():
while True:
for cursor in '|/-\\':
yield cursor
spinner = spinning_cursor()
for _ in range(1, 10):
content = f'\r{next(spinner)} Downloading...'
print(content, end="")
time.sleep(0.1)
print('\r\033[K', end="")
For anyone who interested in nodejs, I also write a nodejs example.
function* makeSpinner(start = 0, end = 100, step = 1) {
let iterationCount = 0;
while (true) {
for (const char of '|/-\\') {
yield char;
}
}
return iterationCount;
}
async function sleep(seconds) {
return new Promise((resolve) => {
setTimeout(resolve, seconds * 1000);
});
}
(async () => {
const spinner = makeSpinner();
for (let i = 0; i < 10; i++) {
content = `\r${spinner.next().value} Downloading...`;
process.stdout.write(content);
await sleep(0.1);
process.stdout.write('\r\033[K');
}
})();
curses module. i'd have a look at the addstr() and addch() functions. Never used it though.
For more advanced console manipulations, on unix you can use the curses python module, and on windows, you can use WConio which provides equivalent functionality of the curses library.
#!/usr/bin/env python
import sys
chars = '|/-\\'
for i in xrange(1,1000):
for c in chars:
sys.stdout.write(c)
sys.stdout.write('\b')
sys.stdout.flush()
CAVEATS:
In my experience this doesn't work in all terminals. A more robust way to do this under Unix/Linux, be it more complicated is to use the curses module, which doesn't work under Windows.
You probably want to slow it down some how with actual processing that is going on in the background.
Here ya go - simple and clear.
import sys
import time
idx = 0
cursor = ['|','/','-','\\'] #frames of an animated cursor
while True:
sys.stdout.write(cursor[idx])
sys.stdout.write('\b')
idx = idx + 1
if idx > 3:
idx = 0
time.sleep(.1)
Crude but simple solution:
import sys
import time
cursor = ['|','/','-','\\']
for count in range(0,1000):
sys.stdout.write('\b{}'.format(cursor[count%4]))
sys.stdout.flush()
# replace time.sleep() with some logic
time.sleep(.1)
There are obvious limitations, but again, crude.
I propose a solution using decorators
from itertools import cycle
import functools
import threading
import time
def spinner(message, spinner_symbols: list = None):
spinner_symbols = spinner_symbols or list("|/-\\")
spinner_symbols = cycle(spinner_symbols)
global spinner_event
spinner_event = True
def start():
global spinner_event
while spinner_event:
symbol = next(spinner_symbols)
print("\r{message} {symbol}".format(message=message, symbol=symbol), end="")
time.sleep(0.3)
def stop():
global spinner_event
spinner_event = False
print("\r", end="")
def external(fct):
#functools.wraps(fct)
def wrapper(*args):
spinner_thread = threading.Thread(target=start, daemon=True)
spinner_thread.start()
result = fct(*args)
stop()
spinner_thread.join()
return result
return wrapper
return external
Simple usage
#spinner("Downloading")
def f():
time.sleep(10)
import sys
def DrowWaitCursor(counter):
if counter % 4 == 0:
print("/",end = "")
elif counter % 4 == 1:
print("-",end = "")
elif counter % 4 == 2:
print("\\",end = "")
elif counter % 4 == 3:
print("|",end = "")
sys.stdout.flush()
sys.stdout.write('\b')
This can be also another solution using a function with a parameter.
I just started with python about a week ago and found this posting. I've combined a bit of what I found here with stuff I learned about threads and queues elsewhere to provide a much better implementation in my opinion. In my solution, writing to the screen is handled by a thread that checks a queue for content. If that queue has content, the cursor spinning thread knows to stop. On the flipside, the cursor spinning thread uses a queue as a lock so the printing thread knows not to print until a full pass of the spinner code is complete. This prevents race conditions and a lot of excess code people are using to keep the console clean.
See below:
import threading, queue, itertools, sys, time # all required for my version of spinner
import datetime #not required for spinning cursor solution, only my example
console_queue = queue.Queue() # this queue should be initialized before functions
screenlock = queue.Queue() # this queue too...
def main():
threading.Thread(target=spinner).start()
threading.Thread(target=consoleprint).start()
while True:
# instead of invoking print or stdout.write, we just add items to the console_queue
# The next three lines are an example of my code in practice.
time.sleep(.5) # wait half a second
currenttime = "[" + datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S") + "] "
console_queue.put(currenttime) # The most important part. Substitute your print and stdout functions with this.
def spinner(console_queue = console_queue, screenlock = screenlock):
spinnerlist = itertools.cycle(['|', '/', '-', '\\'])
while True:
if console_queue.empty():
screenlock.put("locked")
sys.stdout.write(next(spinnerlist))
sys.stdout.flush()
sys.stdout.write('\b')
sys.stdout.flush()
screenlock.get()
time.sleep(.1)
def consoleprint(console_queue = console_queue, screenlock = screenlock):
while True:
if not console_queue.empty():
while screenlock.empty() == False:
time.sleep(.1)
sys.stdout.flush()
print(console_queue.get())
sys.stdout.flush()
if __name__ == "__main__":
main()
Having said all I said and written all I've written, I've only been doing python stuff for a week. If there are cleaner ways of doing this or I missed some best practices I'd love to learn. Thanks.
import requests
import time
import sys
weathercity = input("What city are you in? ")
weather = requests.get('http://api.openweathermap.org/data/2.5/weather?q='+weathercity+'&appid=886705b4c1182eb1c69f28eb8c520e20')
url = ('http://api.openweathermap.org/data/2.5/weather?q='+weathercity+'&appid=886705b4c1182eb1c69f28eb8c520e20')
def spinning_cursor():
while True:
for cursor in '|/-\\':
yield cursor
data = weather.json()
temp = data['main']['temp']
description = data['weather'][0]['description']
weatherprint ="In {}, it is currently {}°C with {}."
spinner = spinning_cursor()
for _ in range(25):
sys.stdout.write(next(spinner))
sys.stdout.flush()
time.sleep(0.1)
sys.stdout.write('\b')
convert = int(temp - 273.15)
print(weatherprint.format(weathercity, convert, description))
if you wanna python text spinner you can look picture
Simple:print_spinner("Hayatta en hakiki mürşit ilimdir.")
Here is the simplest loading spinner for python:
import time
spin=["loading...... ", "|", "/","-", "\"]
for i in spin:
print("\b"+i, end=" ")
time.sleep (0.2)
Output:
loading...... |

Categories