More efficient text-based loading bar - python

I am making a program that has a "loading bar" but I can't figure out how to make the code shorter. This might be a simple fix for all I know, but for the life of me, I just can't figure it out. Here is what I have tried to do so far:
def ldbar():
print "Load: 1%"
time.sleep(0.5)
os.system('clear')
print "Load: 2%"
time.sleep(0.5)
os.system('clear')
print "Load: 3%"
time.sleep(0.5)
os.system('clear')
print "Load: 4%"
time.sleep(0.5)
os.system('clear')
print "Load: 5%"
#So on and so forth up to 100%
ldbar()
So, like I said, is there anyway I can make this shorter?

This should work:
def ldbar():
for i in range(1, 100):
print "Load: {}%\r".format(i),
sys.stdout.flush()
time.sleep(0.5)
ldbar()
It uses a for loop to avoid having the same code over and over again. In the print statement I use \r which moves the cursor to the front of the line, allowing it to be overwriten which is why sys.stdout.flush is used to make sure the output is printed without a newline (notice the comma at the end of the print statement which says that a newline should not be printed).
For Python 3 you would use this (but I think you're using python 2):
def ldbar():
for i in range(1, 100):
print("Load: {}%\r".format(i), end="")
sys.stdout.flush()
time.sleep(0.5)
ldbar()

Here's a nice version using a context manager:
from contextlib import contextmanager
import sys
#contextmanager
def scoped_progress_bar(num_steps, message):
class Stepper(object):
'''
Helper class that does the work of actually advancing the progress bar message
'''
def __init__(self, num_steps, message):
self.current_step = 0.0
self.num_steps = num_steps
self.message = message
def step(self, steps = 1.0):
self.current_step += steps
sys.stdout.write('\r{}:{}%'.format(message, (self.current_step/self.num_steps)*100))
sys.stdout.flush()
stepper = Stepper(num_steps, message) # This is where we actually create the progress bar
yield stepper.step # This is where we do the yield secret sauce to let the user step the bar.
# Finally when the context block exits we wind up back here, and advance the bar to 100% if we need to
if stepper.current_step < stepper.num_steps:
stepper.step(stepper.num_steps - stepper.current_step)
The advantage of this method is that
You can specify an arbitrary number of steps
You can step an arbitrary number of steps
Even if you don't hit the end of the number of steps, the context manager will always print 100% at the end
You can specify an arbitrary message
Usage:
with scoped_progress_bar(10, 'loading') as step:
for i in xrange(7):
step()
time.sleep(0.5)
Which prints:
loading: 10%
loading: 20%
...
loading: 70%
loading: 100%
It's likely a bit overkill for your situation, but thought I'd provide it just in case.
An important thing to note with all of these answers is that they assume you won't be printing out stuff during the process in which you're advancing the progress bar. Doing so will still work just fine, it just might not look like what you expect.

First off, use the Progressbar module (https://pypi.python.org/pypi/progressbar), it already does everything you'll ever want from a text-mode progress bar, and then some.
Now for a fix for your specific implementation, what you want to do is write a bar to stdout (or stderr) with no line return, then erase it, then draw it again. You do it like so:
import sys
import time
sys.stdout.write("0%")
# stdout is line-buffered and you didn't print a newline,
# so nothing will show up unless you explicitly call flush.
sys.stdout.flush()
time.sleep(2)
# Move the cursor back to the beginning of the line
# and overwrite the characters.
sys.stdout.write("\r1%")
sys.stdout.flush()
time.sleep(2)
sys.stdout.write("\r2%")
sys.stdout.flush()
# etc.
But really, use progressbar.

Related

print information in a corner when using python/ipython REPL -- and quitting it while thread is running

I am using a python library I wrote to interact with a custom USB device. The library needs to send and receive data. I also need to interactively call methods. At the moment I utilize two shells, one receiving only and the other sending only. The latter is in the (i)python REPL. It works but it is clumsy, so I want to consolidate the two things in a single shell, which will have the advantage to have access to data structures from both sides in one context. That works fine. The problem is in the UI.
In fact, the receiving part needs to asynchronously print some information. So I wrote something like the following:
#!/usr/bin/python
import threading
import time
import blessings
TOTAL=5
def print_time( thread_id, delay):
count = 0
t=blessings.Terminal()
while count < TOTAL:
time.sleep(delay)
count += 1
stuff = "Thread " + str(thread_id) + " " + str(time.ctime(time.time())) + " -- " + str(TOTAL - count) + " to go"
with t.location(t.width - len(stuff) - 1, thread_id):
print (stuff, end=None )
print("", end="") # just return the cursor
try:
t1 = threading.Thread( target = print_time, args = (1, 2, ) )
t1.start()
print ("Thread started")
except:
print ("Error: unable to start thread")
That is my __init__.py file for the module. It somewhat works, but it has two problems:
While the thread is running, you cannot exit the REPL neither with CTRL-D nor with sys.exit() (that is the reason I am using TOTAL=5 above, so your life is easier if you try this code). This is a problem since my actual thread needs to be an infinite loop. I guess one solution could be to exit via a custom call which will cause a break into that infinite loop, but is there anything better?
The cursor does not return correctly to its earlier position
if I remove the end="" in the line with the comment # just return the cursor, it sort of works, but obviously print an unwanted newline in the place the cursor was (which messes us other input and/or output which might be happening there, in addition to add that unwanted newline)
if I leave the end="" it does not return the cursor, not even if I add something to print, e.g. print(".", end="") -- the dots . are printed at the right place, but the blinking cursor and the input is printed at the top
I know these are two unrelated problem and I could have asked two separate questions, but I need an answer to both, or otherwise it's a moot point. Or alternatively, I am open to other solutions. I thought of a separate GTK window, and that might work, but it's a subpar solution, since I really would like this to work in CLI only (to keep it possible in a ssh-without-X-tunneling setup).
Using blessed instead of blessing does not have the problem with the cursor not returning to the previous position, even without anything outside of the with context.
Making the thread a daemon solves the other problem.

Whole program typing effect

I'm making a text adventure game in Python 3. Is there any way to add a typing effect to any text that gets printed without repeating a command after every line?
Assuming that with "typing effect" you mean that the messages should slowly appear one character at a time, you can define a function that iterates the given message and prints it one character at a time, using time.sleep in between to wait a bit. Make sure to flush the buffer after each character.
import time
def slow_print(msg):
for c in msg:
print(c, end="", flush=True)
time.sleep(0.1)
print()
slow_print("Hello World!")
If you really want to apply this for each and every output in your game (which I'd really not recommend) you can overwrite the print function, keeping a reference to the original print function to use within your new slow print function.
original_print = print
def slow_print(msg):
# same as above, but using original_print instead of print
print = slow_print
print("Hello World!")
You could also just def print(...) directly, but I'd recommend defining it as a separate function and then assigning it to print. This way, you can still make this optional, as this will most likely annoy the player after the first few minutes.
I assume you want the characters to appear as if someone were typing them so I'll just assume that
Import modules
import os
import sys
import time
from colr import color
Define your function
def function_name(phrase,speed,r_value,g_value,b_value):
for char in phrase:
sys.stdout.write(color(char, fore=(r_value,g_value,b_value)))
sys.stdout.flush()
time.sleep(speed)
Test the function
function_name("Hello",0.05,0,255,0)
#prints the phrase "Hello" in green text
Alternatively you could write the function using the threading library, which would allow users to skip the typing effect if they so wish.
import time, threading, os, sys, tty, termios
from colr import color
def slow_type_interrupt(phrase,speed,r_value,g_value,b_value):
done = False # this acts as the kill switch, using if statements, you can make certain button presses stop the message printing and outright display it
def type_out():
for char in phrase:
if done:
break
sys.stdout.write(color(char,fore=(r_value,g_value,b_value)))
sys.stdout.flush()
time.sleep(speed)
os.system('clear')
print(color(phrase,fore=(r_value,g_value,b_value)))
t = threading.Thread(target=type_out)
t.start()
def getkey():
ky = sys.stdin.fileno()
Ab = termios.tcgetattr(ky)
try:
tty.setraw(sys.stdin.fileno())
key = sys.stdin.read(1)
finally:
termios.tcsetattr(ky, termios.TCSADRAIN, Ab)
return key
while not done:
key_press = getkey()
if key_press == 'a': #You can replace a with whatever key you want to act as the "kill key"
done = True
os.system('clear')
print(color(phrase,fore=(r_value,g_value,b_value)))
slow_type_interrupt("Hello this is a test. Pressing 'a' will end this and immediatley display the message",.05,0,255,0)
As I mentioned in the comments of the code, a can be replaced by whatever you want. The reason I use this particular method for retrieving keypresses is because it works on almost anything that runs Python. I would suggest reading up on some other ways to retrieve keyboard inputs.
Hope I could help :)

How to clear only last one line in python output console?

I am trying to clear only last few line from output console window. To achieve this I have decided to use create stopwatch and I have achieved to interrupt on keyboard interrupt and on enter key press it creates lap but my code only create lap once and my current code is clearing whole output screen.
clear.py
import os
import msvcrt, time
from datetime import datetime
from threading import Thread
def threaded_function(arg):
while True:
input()
lap_count = 0
if __name__ == "__main__":
# thread = Thread(target = threaded_function)
# thread.start()
try:
while True:
t = "{}:{}:{}:{}".format(datetime.now().hour, datetime.now().minute, datetime.now().second, datetime.now().microsecond)
print(t)
time.sleep(0.2)
os.system('cls||clear') # I want some way to clear only previous line instead of clearing whole console
if lap_count == 0:
if msvcrt.kbhit():
if msvcrt.getwche() == '\r': # this creates lap only once when I press "Enter" key
lap_count += 1
print("lap : {}".format(t))
time.sleep(1)
continue
except KeyboardInterrupt:
print("lap stop at : {}".format(t))
print(lap_count)
when I run
%run <path-to-script>/clear.py
in my ipython shell I am able to create only one lap but it is not staying for permanent.
To clear only a single line from the output :
print ("\033[A \033[A")
This will clear the preceding line and will place the cursor onto the beginning of the line.
If you strip the trailing newline then it will shift to the previous line as \033[A means put the cursor one line up
I think the simplest way is to use two print() to achieve clean the last line.
print("something will be updated/erased during next loop", end="")
print("\r", end="")
print("the info")
The 1st print() simply make sure the cursor ends at the end of the line and not start a new line
The 2nd print() would move the cursor to the beginning of the same line and not start a new line
Then it comes naturally for the 3rd print() which simply start print something where the cursor is currently at.
I also made a toy function to print progress bar using a loop and time.sleep(), go and check it out
def progression_bar(total_time=10):
num_bar = 50
sleep_intvl = total_time/num_bar
print("start: ")
for i in range(1,num_bar):
print("\r", end="")
print("{:.1%} ".format(i/num_bar),"-"*i, end="")
time.sleep(sleep_intvl)
The codes shared by Ankush Rathi above this comment are probably correct, except for the use of parenthesis in the print command. I personally recommend doing it like this.
print("This message will remain in the console.")
print("This is the message that will be deleted.", end="\r")
One thing to keep in mind though is that if you run it in IDLE by pressing F5, the shell will still display both messages. However, if you run the program by double clicking, the output console will delete it. This might be the misunderstanding that happened with Ankush Rathi's answer (in a previous post).
I know this is a really old question but i couldn't find any good answer at it. You have to use escape characters. Ashish Ghodake suggested to use this
print ("\033[A \033[A")
But what if the line you want to remove has more characters than the spaces in the string?
I think the better thing is to find out how many characters can fit in one of your terminal's lines and then to add the correspondent number of " " in the escape string like this.
import subprocess, time
tput = subprocess.Popen(['tput','cols'], stdout=subprocess.PIPE)
cols = int(tput.communicate()[0].strip()) # the number of columns in a line
i = 0
while True:
print(i)
time.sleep(0.1)
print("\033[A{}\033[A".format(' '*cols))
i += 1
finally I would say that the "function" to remove last line is
import subprocess
def remove():
tput = subprocess.Popen(['tput','cols'], stdout=subprocess.PIPE)
cols = int(tput.communicate()[0].strip())
print("\033[A{}\033[A".format(' '*cols))
For Python 3's, using f-String.
from time import sleep
for i in range(61):
print(f"\r{i}", end="")
sleep(0.1)
Found a solution on this page that works. Here is the helper function:
import sys
def delete_last_line():
"Deletes the last line in the STDOUT"
# cursor up one line
sys.stdout.write('\x1b[1A')
# delete last line
sys.stdout.write('\x1b[2K')
I hope it helps someone.
None of the other answers worked for me. Putting print("Sentence to be overwritten", end='\r') would instantly clear my sentence and it would never be visible to begin with. I'm using PyCharm on a Mac if that could be making the difference. What I had to do is the following:
from time import sleep
print("Sentence to be overwritten", end='')
sleep(1)
print("\r", end='')
print("Sentence to stay")
end='' makes it so the print doesn't automatically put a '\n' character at the end. Then print("\r", end='') will put the cursor at the beginning of the line. Then the 2nd print statement will be printed in the same spot as the first, overwriting it.
If you intend to delete certain line from the console output,
print "I want to keep this line"
print "I want to delete this line",
print "\r " # this is going to delete previous line
or
print "I want to keep this line"
print "I want to delete this line\r "

Loading animation in python

Im new to python and was wondering how to make a loading animation while my program runs. I need this because I don't want users thinking that the program is caught in a dead loop. I prefer a something like...
Loading...(with the dots disappearing and reappearing one by one)
Thanks!
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="")
The most proper way I can think of to do it would be using threading.
You would initiate a thread that starts displaying some indication that the program is doing something and then open a new thread that actually does the work.
When the thread doing the work finished then you can move on with whatever else the program does.
This looks ok when ran in windows command prompt, not sure how linux will like it:
import threading
import time
import os
import queue
q = queue.Queue()
q.put(False)
class counter(object):
def __init__(self):
wait_label = "Loading"
self.stop_flag = q.get()
while not self.stop_flag:
try:
self.stop_flag = q.get_nowait()
except:
pass
os.system('cls') # might need to change this command for linux
wait_label += "."
print(wait_label)
time.sleep(1)
class other(counter):
def __init__(self):
time.sleep(15)
q.put(True)
counter_thread = threading.Thread(None, counter)
counter_thread.start()
other_thread = threading.Thread(None, other)
other_thread.start()
To reduce the code length, we can loop it.
import time
# flag variable to print the dots and it's value increases inside the while loop.
flag = 1
# To print the dots we use while loop. In total, 4 dots will be printed.
while flag < 5:
print("\rLoading, Please Wait " + ("." * flag), end=" ")
time.sleep(1)
flag = flag + 1

Printing at timed intervals

I know how to utilize time.sleep(), but I was curious how to print something like so:
"hey...(pause)...you...(pause)....there"
where the 'pause' is some time.sleep() interval. I can only print these intervals on separate lines. Is there a way to keep it all on one line?
In python 2:
print "hey...",
time.sleep(0.5)
In python 3:
print("hey...", end=' ')
time.sleep(0.5)
In a print statement in python2.x, you can suppress the newline with a trailing comma:
print "hey...",
time.sleep(1)
print "...you...",
time.sleep(1)
print "....there"
On python3.x (or with from __future__ import print_function enabled), you use the end keyword to the print function:
print("hey...", end="")
etc.
Or, finally, you can always1 write to the sys.stdout stream:
import sys
sys.stdout.write("hey...")
time.sleep(1)
...
The advantage here is that you can flush it explicitly:
sys.stdout.flush()
1technically not always. sys.stdout could be replaced by something else: sys.stdout = 3 -- But making a mess without cleaning up after yourself is terribly rude. However, if you ever find yourself in that situation, sys.__stdout__ is a backup ;-).

Categories

Resources