I am running a program that works in parallel, utilizing the Pool
object from the multiprocessing module.
What I am trying to do is run a function n number of times in parallel, each having a separate loading %, and I would like it to be updating the percentage for each function without replacing other percentages... example:
f(x):
while x < 0:
print 'For x = {0}, {1}% completed...\r'.format(x, percentage),
And I would run the function multiple times in parallel.
The effect I am trying to achieve is the following, for f(10000000), f(15000000), f(7000000):
For x = 10000000, 43% completed
For x = 15000000, 31% completed
For x = 7000000, 77% completed
And the percentages will be updating in their individual lines without replacing for other values of x, in this function f
which will be running three times at the same time.
I tried using the carriage return '\r' but that replaces every line and just creates a mess.
Thanks for taking the time to read this post! I hope you can tell me.
I am using Python 2.7 but if it can only be achieved with Python 3 I am open to suggestions.
As @Keozon mentioned in the comments, one way to achieve this would be to use the curses library.
There is a good guide for curses on the python website.
Alternatively, you might try using ANSI escape codes to move the cursor around.
This is in Python3, but it'll work just fine in any version, you'll just need to change the print statements around (or from __future__ import print_function
).
print('Hello')
print('World')
print('\033[F\033[F\033[K', end='') # Up, Up, Clear line
# Cursor is at the 'H' of 'Hello'
print('Hi') # Overwriting 'Hello'
# Cursor is at the 'W' of 'World'
print('\033[E', end='') # Down
# Cursor is on the blank line after 'World'
print('Back to the end')
Hi
World
Back to the end
I've done way too much work for you here, but hey, here's basically a full solution using the ANSI method I mentioned above:
import time
import random
class ProgressBar:
def __init__(self, name):
self._name = name
self._progress = 0
@property
def name(self):
return self._name
def get_progress(self):
"""
Randomly increment the progress bar and ensure it doesn't go
over 100
"""
self._progress += int(random.random()*5)
if self._progress > 100:
self._progress = 100
return self._progress
class MultipleProgressBars:
def __init__(self, progress_bars):
self._progress_bars = progress_bars
self._first_update = True
self._all_finished = False
@property
def all_finished(self):
"""
A boolean indicating if all progress bars are at 100
"""
return self._all_finished
def update(self):
"""
Update each progress bar
"""
# We don't want to move up and clear a line on the first run
# so we have a flag to make sure this only happens on later
# calls
if not self._first_update:
# Move up and clear the line the correct number of times
print('\033[F\033[K'*len(self._progress_bars),end='', sep='')
num_complete = 0 # Number of progress bars complete
for progress_bar in self._progress_bars:
name = progress_bar.name
progress = progress_bar.get_progress()
if progress == 100:
num_complete += 1
# Print out a progress bar (scaled to 40 chars wide)
print(
name.ljust(10),
'[' + ('='*int(progress*0.4)).ljust(40) + ']',
str(progress)+'%')
if num_complete == len(self._progress_bars):
self._all_finished = True
self._first_update = False # Mark the first update done
# Create a list of ProgressBars and give them relevant names
progress_bars = [
ProgressBar('James'),
ProgressBar('Bert'),
ProgressBar('Alfred'),
ProgressBar('Frank')
]
# Create a new instance of our MultipleProgressBars class
mpb = MultipleProgressBars(progress_bars)
# Keep updating them while at least one of them is still active
while not mpb.all_finished:
mpb.update()
time.sleep(0.2)