ProgressBar initial replacement outline
This commit is contained in:
parent
271db52da3
commit
b691bb9b25
1 changed files with 169 additions and 0 deletions
169
core/text.py
169
core/text.py
|
@ -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:
|
||||
'''
|
||||
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)))
|
||||
except IOError:
|
||||
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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue