diff --git a/README.rst b/README.rst index 18c2bec..b485fb9 100644 --- a/README.rst +++ b/README.rst @@ -60,6 +60,7 @@ of widgets: - `FormatCustomText `_ - `FormatLabel `_ - `FormatLabelBar `_ + - `GranularBar `_ - `Percentage `_ - `PercentageLabelBar `_ - `ReverseBar `_ diff --git a/examples.py b/examples.py index a330044..0c6948d 100644 --- a/examples.py +++ b/examples.py @@ -177,6 +177,20 @@ def multi_progress_bar_example(left=True): time.sleep(0.02) +@example +def granular_progress_example(): + widgets = [ + progressbar.GranularBar(markers=" ▏▎▍▌▋▊▉█", left='', right='|'), + progressbar.GranularBar(markers=" ▁▂▃▄▅▆▇█", left='', right='|'), + progressbar.GranularBar(markers=" ▖▌▛█", left='', right='|'), + progressbar.GranularBar(markers=" ░▒▓█", left='', right='|'), + progressbar.GranularBar(markers=" ⡀⡄⡆⡇⣇⣧⣷⣿", left='', right='|'), + progressbar.GranularBar(markers=" .oO", left='', right=''), + ] + for i in progressbar.progressbar(range(100), widgets=widgets): + time.sleep(0.03) + + @example def percentage_label_bar_example(): widgets = [progressbar.PercentageLabelBar()] diff --git a/progressbar/__about__.py b/progressbar/__about__.py index 880be3e..35b1c9d 100644 --- a/progressbar/__about__.py +++ b/progressbar/__about__.py @@ -19,7 +19,7 @@ long running operations. '''.strip().split()) __email__ = 'wolph@wol.ph' -__version__ = '3.54.0' +__version__ = '3.55.0' __license__ = 'BSD' __copyright__ = 'Copyright 2015 Rick van Hattem (Wolph)' __url__ = 'https://github.com/WoLpH/python-progressbar' diff --git a/progressbar/__init__.py b/progressbar/__init__.py index ef50451..f93ab86 100644 --- a/progressbar/__init__.py +++ b/progressbar/__init__.py @@ -26,6 +26,7 @@ VariableMixin, MultiRangeBar, MultiProgressBar, + GranularBar, FormatLabelBar, PercentageLabelBar, Variable, @@ -74,6 +75,7 @@ 'VariableMixin', 'MultiRangeBar', 'MultiProgressBar', + 'GranularBar', 'FormatLabelBar', 'PercentageLabelBar', 'Variable', diff --git a/progressbar/utils.py b/progressbar/utils.py index a9dc6f5..258249f 100644 --- a/progressbar/utils.py +++ b/progressbar/utils.py @@ -192,7 +192,7 @@ def __init__(self, target, capturing=False, listeners=set()): self.listeners = listeners self.needs_clear = False - def isatty(self): + def isatty(self): # pragma: no cover return self.target.isatty() def write(self, value): diff --git a/progressbar/widgets.py b/progressbar/widgets.py index 5e24c3d..e9c03ca 100644 --- a/progressbar/widgets.py +++ b/progressbar/widgets.py @@ -909,6 +909,75 @@ def get_values(self, progress, data): return ranges +class GranularMarkers: + smooth = ' ▏▎▍▌▋▊▉█' + bar = ' ▁▂▃▄▅▆▇█' + snake = ' ▖▌▛█' + fade_in = ' ░▒▓█' + dots = ' ⡀⡄⡆⡇⣇⣧⣷⣿' + growing_circles = ' .oO' + + +class GranularBar(AutoWidthWidgetBase): + '''A progressbar that can display progress at a sub-character granularity + by using multiple marker characters. + + Examples of markers: + - Smooth: ` ▏▎▍▌▋▊▉█` (default) + - Bar: ` ▁▂▃▄▅▆▇█` + - Snake: ` ▖▌▛█` + - Fade in: ` ░▒▓█` + - Dots: ` ⡀⡄⡆⡇⣇⣧⣷⣿` + - Growing circles: ` .oO` + + The markers can be accessed through GranularMarkers. GranularMarkers.dots + for example + ''' + + def __init__(self, markers=GranularMarkers.smooth, left='|', right='|', + **kwargs): + '''Creates a customizable progress bar. + + markers - string of characters to use as granular progress markers. The + first character should represent 0% and the last 100%. + Ex: ` .oO`. + left - string or callable object to use as a left border + right - string or callable object to use as a right border + ''' + self.markers = markers + self.left = string_or_lambda(left) + self.right = string_or_lambda(right) + + AutoWidthWidgetBase.__init__(self, **kwargs) + + def __call__(self, progress, data, width): + left = converters.to_unicode(self.left(progress, data, width)) + right = converters.to_unicode(self.right(progress, data, width)) + width -= progress.custom_len(left) + progress.custom_len(right) + + if progress.max_value is not base.UnknownLength \ + and progress.max_value > 0: + percent = progress.value / progress.max_value + else: + percent = 0 + + num_chars = percent * width + + marker = self.markers[-1] * int(num_chars) + + marker_idx = int((num_chars % 1) * (len(self.markers) - 1)) + if marker_idx: + marker += self.markers[marker_idx] + + marker = converters.to_unicode(marker) + + # Make sure we ignore invisible characters when filling + width += len(marker) - progress.custom_len(marker) + marker = marker.ljust(width, self.markers[0]) + + return left + marker + right + + class FormatLabelBar(FormatLabel, Bar): '''A bar which has a formatted label in the center.''' def __init__(self, format, **kwargs): diff --git a/tests/test_monitor_progress.py b/tests/test_monitor_progress.py index 36ce89c..5dd6f5e 100644 --- a/tests/test_monitor_progress.py +++ b/tests/test_monitor_progress.py @@ -206,6 +206,26 @@ def test_percentage_label_bar(testdir): ] +def test_granular_bar(testdir): + result = testdir.runpython(testdir.makepyfile(_create_script( + widgets='[progressbar.GranularBar(markers=" .oO")]', + line_breaks=False, + items=list(range(5)), + ))) + pprint.pprint(result.stderr.lines, width=70) + assert result.stderr.lines == [ + u'', + u'| |', + u'|OOOOOOOOOOO. |', + u'|OOOOOOOOOOOOOOOOOOOOOOO |', + u'|OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOo |', + u'|OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO. |', + u'|OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO|', + u'', + u'|OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO|' + ] + + def test_colors(testdir): kwargs = dict( items=range(1),