simpler use of progress bar

now written as class (avoids the managing of function attributes) which
is used as a small wrapper around a loop.
ETA is shown, based on datetime class which hast nice formatting
This commit is contained in:
Martin Diehl 2020-03-09 13:39:20 +01:00
parent 0b340a6d42
commit 32378fa609
2 changed files with 84 additions and 52 deletions

View File

@ -17,7 +17,7 @@ from . import Orientation
from . import Environment from . import Environment
from . import grid_filters from . import grid_filters
class Result(): class Result:
""" """
Read and write to DADF5 files. Read and write to DADF5 files.
@ -925,10 +925,9 @@ class Result():
groups = self.groups_with_datasets(datasets.values()) groups = self.groups_with_datasets(datasets.values())
default_arg = partial(self._job,func=func,datasets=datasets,args=args,lock=lock) default_arg = partial(self._job,func=func,datasets=datasets,args=args,lock=lock)
util.progressBar(iteration=0,total=len(groups)) for result in util.show_progress(pool.imap_unordered(default_arg,groups),len(groups)):
for i,result in enumerate(pool.imap_unordered(default_arg,groups)): if not result:
util.progressBar(iteration=i+1,total=len(groups)) continue
if not result: continue
lock.acquire() lock.acquire()
with h5py.File(self.fname, 'a') as f: with h5py.File(self.fname, 'a') as f:
try: try:

View File

@ -1,9 +1,9 @@
import sys import sys
import time import datetime
import os import os
import subprocess import subprocess
import shlex import shlex
from fractions import Fraction import fractions
from functools import reduce from functools import reduce
from optparse import Option from optparse import Option
@ -11,10 +11,10 @@ import numpy as np
class bcolors: class bcolors:
""" """
ASCII Colors (Blender code). ASCII Colors.
https://svn.blender.org/svnroot/bf-blender/trunk/blender/build_files/scons/tools/bcolors.py https://svn.blender.org/svnroot/bf-blender/trunk/blender/build_files/scons/tools/bcolors.py
http://stackoverflow.com/questions/287871/print-in-terminal-with-colors-using-python https://stackoverflow.com/questions/287871
""" """
HEADER = '\033[95m' HEADER = '\033[95m'
@ -38,18 +38,18 @@ class bcolors:
self.BOLD = '' self.BOLD = ''
self.UNDERLINE = '' self.UNDERLINE = ''
self.CROSSOUT = '' self.CROSSOUT = ''
def srepr(arg,glue = '\n'): def srepr(arg,glue = '\n'):
r""" r"""
Join arguments as individual lines. Join arguments as individual lines.
Parameters Parameters
---------- ----------
arg : iterable arg : iterable
Items to join. Items to join.
glue : str, optional glue : str, optional
Defaults to \n. Defaults to \n.
""" """
if (not hasattr(arg, "strip") and if (not hasattr(arg, "strip") and
@ -62,13 +62,13 @@ def srepr(arg,glue = '\n'):
def croak(what, newline = True): def croak(what, newline = True):
""" """
Write formated to stderr. Write formated to stderr.
Parameters Parameters
---------- ----------
what : str or iterable what : str or iterable
Content to be displayed Content to be displayed
newline : bool, optional newline : bool, optional
Separate items of what by newline. Defaults to True. Separate items of what by newline. Defaults to True.
""" """
if not what: if not what:
@ -165,63 +165,96 @@ class extendableOption(Option):
Option.take_action(self, action, dest, opt, value, values, parser) Option.take_action(self, action, dest, opt, value, values, parser)
def progressBar(iteration, total, prefix='', bar_length=50): class _ProgressBar:
""" """
Call in a loop to create terminal progress bar. Report progress of an interation as a status bar.
From https://gist.github.com/aubricus/f91fb55dc6ba5557fbab06119420dd6a Works for 0-based loops, ETA is estimated by linear extrapolation.
"""
def __init__(self,total,prefix,bar_length):
"""
Inititalize a progress bar to current time as basis for ETA estimation.
Parameters
----------
total : int
Total # of iterations.
prefix : str
Prefix string.
bar_length : int
Character length of bar.
"""
self.total = total
self.prefix = prefix
self.bar_length = bar_length
self.start_time = datetime.datetime.now()
self.last_fraction = 0.0
sys.stderr.write('{} {} 0% ETA n/a'.format(self.prefix, ''*self.bar_length))
sys.stderr.flush()
def update(self,iteration):
fraction = (iteration+1) / self.total
if int(self.bar_length * fraction) > int(self.bar_length * self.last_fraction):
delta_time = datetime.datetime.now() - self.start_time
remaining_time = (self.total - (iteration+1)) * delta_time / (iteration+1)
remaining_time -= datetime.timedelta(microseconds=remaining_time.microseconds) # remove μs
filled_length = int(self.bar_length * fraction)
bar = '' * filled_length + '' * (self.bar_length - filled_length)
sys.stderr.write('\r{} {} {:>4.0%} ETA {}'.format(self.prefix, bar, fraction, remaining_time))
sys.stderr.flush()
self.last_fraction = fraction
if iteration == self.total - 1:
sys.stderr.write('\n')
sys.stderr.flush()
def show_progress(iterable,N_iter=None,prefix='',bar_length=50):
"""
Decorate a loop with a status bar.
Use similar like enumerate.
Parameters Parameters
---------- ----------
iteration : int iterable : iterable/function with yield statement
Current iteration. Iterable (or function with yield statement) to be decorated.
total : int N_iter : int
Total iterations. Total # of iterations. Needed if number of iterations can not be obtained as len(iterable).
prefix : str, optional prefix : str, optional.
Prefix string. Prefix string.
bar_length : int, optional bar_length : int, optional
Character length of bar. Defaults to 50. Character length of bar. Defaults to 50.
""" """
fraction = iteration / float(total) if N_iter:
if not hasattr(progressBar, "last_fraction"): # first call to function status = _ProgressBar(N_iter,prefix,bar_length)
progressBar.start_time = time.time()
progressBar.last_fraction = -1.0
remaining_time = ' n/a'
else: else:
if fraction <= progressBar.last_fraction or iteration == 0: # reset: called within a new loop status = _ProgressBar(len(iterable),prefix,bar_length)
progressBar.start_time = time.time()
progressBar.last_fraction = -1.0
remaining_time = ' n/a'
else:
progressBar.last_fraction = fraction
remainder = (total - iteration) * (time.time()-progressBar.start_time)/iteration
remaining_time = '{: 3d}:'.format(int( remainder//3600)) + \
'{:02d}:'.format(int((remainder//60)%60)) + \
'{:02d}' .format(int( remainder %60))
filled_length = int(round(bar_length * fraction)) for i,item in enumerate(iterable):
bar = '' * filled_length + '' * (bar_length - filled_length) yield item
status.update(i)
sys.stderr.write('\r{} {} {}'.format(prefix, bar, remaining_time)),
if iteration == total:
sys.stderr.write('\n')
sys.stderr.flush()
def scale_to_coprime(v): def scale_to_coprime(v):
"""Scale vector to co-prime (relatively prime) integers.""" """Scale vector to co-prime (relatively prime) integers."""
MAX_DENOMINATOR = 1000 MAX_DENOMINATOR = 1000
def get_square_denominator(x): def get_square_denominator(x):
"""Denominator of the square of a number.""" """Denominator of the square of a number."""
return Fraction(x ** 2).limit_denominator(MAX_DENOMINATOR).denominator return fractions.Fraction(x ** 2).limit_denominator(MAX_DENOMINATOR).denominator
def lcm(a, b): def lcm(a, b):
"""Least common multiple.""" """Least common multiple."""
return a * b // np.gcd(a, b) return a * b // np.gcd(a, b)
denominators = [int(get_square_denominator(i)) for i in v] denominators = [int(get_square_denominator(i)) for i in v]
s = reduce(lcm, denominators) ** 0.5 s = reduce(lcm, denominators) ** 0.5
m = (np.array(v)*s).astype(np.int) m = (np.array(v)*s).astype(np.int)
@ -230,7 +263,7 @@ def scale_to_coprime(v):
class return_message(): class return_message():
"""Object with formatted return message.""" """Object with formatted return message."""
def __init__(self,message): def __init__(self,message):
""" """
Sets return message. Sets return message.
@ -242,7 +275,7 @@ class return_message():
""" """
self.message = message self.message = message
def __repr__(self): def __repr__(self):
"""Return message suitable for interactive shells.""" """Return message suitable for interactive shells."""
return srepr(self.message) return srepr(self.message)