ProgressBar initial replacement outline

This commit is contained in:
Martinez 2016-02-18 08:01:25 +01:00
parent 271db52da3
commit b691bb9b25

View file

@ -41,6 +41,144 @@ def lexical_join(words, oxford=False):
# -------------------------------------------------- # --------------------------------------------------
class _ProgressBarChannel:
def __init__(self, unit, top, prefix_base2, precision):
self.unit = unit
self.top = top
self.prefix_base2 = prefix_base2
self.precision = precision
self.value = 0
self.set()
if prefix_base2:
_print("Unit does not yet support base2 prefixes (e.g. Gi, Mi), using decadic (G, M) instead", prefix.warn)
def set(self, value=None):
if value is not None:
self.value = value
self.ratio = self.value / self.top
s = Unit(self.value, self.unit, format="%.{:d}f pU".format(self.precision))
self.text = str(s).split(" ", 1)
class ProgressBar2:
"""
A configurable text progressbar.
Each atom consists of three characters: a literal §, an operator and a channel.
An operator is one of:
% - a percentage (value from 0 to 100, padded to three characters)
r - displays the raw value of the channel
z - displays the maximum (total) value
s - displays the rate of change in units/s
m - displays the rate of change in units/min
h - displays the rate of change in units/h
t - displays the elapsed time in s (use with uppercase channel to get an ETA)
T - displays the elapsed time in hh:mm:ss (use with uppercase channel to get an ETA)
any other character - a bar consisting of these characters filling the line so that
at 100% it fills the entirety of the remaining space
A channel ID as a lowercase letter is the channel's actual value. An uppercase letter
is that channel's complement.
Use §§ to display a literal §.
Examples
========
Format: §%a [§=a>§ A] §tA
Output: 42 % [============> ] 27 s
Format: §%a (§ra/§ma) [§-a>§ A] §sa [§rb/§rB] (ETA §tA)
Output: 53 % (21.0/39.6 kf) [---------------------> ] 27.1 f/s [233/439 Mio] (ETA 00:11:26)
"""
def __init__(self, format, channels, width=None, space_before_unit=True):
"""
Initialize the ProgressBar.
width is the desired size of the progressbar drawing area in characters (columns)
and defaults to terminal width.
space_before_unit enables a space character between a value and its unit.
channels is a dict that for each channel lists its properties:
{
"a": {
"unit": "f",
"prefix_base2": False,
"top": 39600,
"precision": 1
},
"b": {
"unit": "o",
"prefix_base2": True,
"top": measure_file_size_closure("/path/to/file"),
"precision": 0
}
}
Channel IDs (the "a", "b") are arbitrary lowercase letters ([a-z]).
Properties "unit" and "prefix_base2" are passed to over.core.text.Unit.
"top" is the value of the channel that corresponds to 100%. Channels are
allowed to exceed this value. It can either be a number or a callable. If
it's a callable, it will be called without arguments and shall return a number.
"precision" is the displayed floating point precision. Use 0 to force an integer.
"""
self.format = format
self.channels = {id: _ProgressBarChannel(**conf) for id, conf in channels.items()}
self.space_before_unit = space_before_unit
self.time_start = time.time()
self.width = width
def set(self, channel_id, value):
"""
Sets the channel's value.
"""
c = self.channels[channel_id]
c.set(value)
def render(self, clear=True, stream=sys.stderr):
"""
Renders the progressbar into a stream. If clear is True, clears the previous content first.
"""
width = self.width or get_terminal_size()[1]
raw = self.format
for tag in re.findall("§.[a-zA-Z]", raw):
op = tag[1]
ch = tag[2]
inverse = ch.isupper()
# display literal §s
raw = raw.replace("§§", "§")
# pave over previous render and draw the new one
if clear:
stream.write("\r" + "_" * width)
stream.write("\r" + raw)
def end(self, finish=False):
"""
Print a newline (heh :-)
If finish is True, set all channels to 100% and draw the progressbar first.
"""
if finish:
for c in self.channels.values():
c.set(c.top)
self.render()
print()
class ProgressBar: class ProgressBar:
''' '''
An animated progress bar. An animated progress bar.
@ -512,3 +650,34 @@ def get_terminal_size():
return struct.unpack('HHHH', fcntl.ioctl(terminal, termios.TIOCGWINSZ, struct.pack('HHHH', 0, 0, 0, 0))) return struct.unpack('HHHH', fcntl.ioctl(terminal, termios.TIOCGWINSZ, struct.pack('HHHH', 0, 0, 0, 0)))
except IOError: except IOError:
return (40, 80) return (40, 80)
# --------------------------------------------------
_print = Output('over.core.text', stream=sys.stderr)
# --------------------------------------------------
if __name__ == "__main__":
pb = ProgressBar2(
"§%a (§ra/§ma) [§-a>§ A] §sa [§rb/§rB] (ETA §tA)",
{
"a": {
"unit": "f",
"prefix_base2": False,
"top": 39600,
"precision": 1
},
"b": {
"unit": "o",
"prefix_base2": True,
"top": 3200,
"precision": 0
}
},
80
)
pb.set("a", 12000)
pb.set("b", 1500)
pb.render()
pb.end(not True)