Skip to content

Commit 9616cee

Browse files
committed
added color support for rotating markers
1 parent 04d0f65 commit 9616cee

2 files changed

Lines changed: 74 additions & 13 deletions

File tree

progressbar/widgets.py

Lines changed: 66 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import sys
1010
import pprint
1111
import datetime
12+
import functools
1213

1314
from python_utils import converters
1415

@@ -32,7 +33,49 @@ def render_input(progress, data, width):
3233
return input_
3334

3435

35-
def create_marker(marker):
36+
def create_wrapper(wrapper):
37+
'''Convert a wrapper tuple or format string to a format string
38+
39+
>>> create_wrapper('')
40+
41+
>>> create_wrapper('a{}b')
42+
'a{}b'
43+
44+
>>> create_wrapper(('a', 'b'))
45+
'a{}b'
46+
'''
47+
if isinstance(wrapper, tuple) and len(wrapper) == 2:
48+
a, b = wrapper
49+
wrapper = (a or '') + '{}' + (b or '')
50+
elif not wrapper:
51+
return
52+
53+
if isinstance(wrapper, six.string_types):
54+
assert '{}' in wrapper, 'Expected string with {} for formatting'
55+
else:
56+
raise RuntimeError('Pass either a begin/end string as a tuple or a'
57+
' template string with {}')
58+
59+
return wrapper
60+
61+
62+
def wrapper(function, wrapper):
63+
'''Wrap the output of a function in a template string or a tuple with
64+
begin/end strings
65+
66+
'''
67+
wrapper = create_wrapper(wrapper)
68+
if not wrapper:
69+
return function
70+
71+
@functools.wraps(function)
72+
def wrap(*args, **kwargs):
73+
return wrapper.format(function(*args, **kwargs))
74+
75+
return wrap
76+
77+
78+
def create_marker(marker, wrap=None):
3679
def _marker(progress, data, width):
3780
if progress.max_value is not base.UnknownLength \
3881
and progress.max_value > 0:
@@ -45,9 +88,9 @@ def _marker(progress, data, width):
4588
marker = converters.to_unicode(marker)
4689
assert utils.len_color(marker) == 1, \
4790
'Markers are required to be 1 char'
48-
return _marker
91+
return wrapper(_marker, wrap)
4992
else:
50-
return marker
93+
return wrapper(marker, wrap)
5194

5295

5396
class FormatWidgetMixin(object):
@@ -525,10 +568,13 @@ class AnimatedMarker(TimeSensitiveWidgetBase):
525568
it were rotating.
526569
'''
527570

528-
def __init__(self, markers='|/-\\', default=None, fill='', **kwargs):
571+
def __init__(self, markers='|/-\\', default=None, fill='',
572+
marker_wrap=None, fill_wrap=None, **kwargs):
529573
self.markers = markers
574+
self.marker_wrap = create_wrapper(marker_wrap)
530575
self.default = default or markers[0]
531-
self.fill = create_marker(fill) if fill else None
576+
self.fill_wrap = create_wrapper(fill_wrap)
577+
self.fill = create_marker(fill, self.fill_wrap) if fill else None
532578
WidgetBase.__init__(self, **kwargs)
533579

534580
def __call__(self, progress, data, width=None):
@@ -538,14 +584,17 @@ def __call__(self, progress, data, width=None):
538584
if progress.end_time:
539585
return self.default
540586

587+
marker = self.markers[data['updates'] % len(self.markers)]
588+
if self.marker_wrap:
589+
marker = self.marker_wrap.format(marker)
590+
541591
if self.fill:
542592
# Cut the last character so we can replace it with our marker
543-
fill = self.fill(progress, data, width)[:-1]
593+
fill = self.fill(progress, data, width - progress.custom_len(
594+
marker))
544595
else:
545596
fill = ''
546-
547-
marker = self.markers[data['updates'] % len(self.markers)]
548-
597+
549598
# Python 3 returns an int when indexing bytes
550599
if isinstance(marker, int): # pragma: no cover
551600
marker = bytes(marker)
@@ -642,7 +691,7 @@ class Bar(AutoWidthWidgetBase):
642691
'''A progress bar which stretches to fill the line.'''
643692

644693
def __init__(self, marker='#', left='|', right='|', fill=' ',
645-
fill_left=True, **kwargs):
694+
fill_left=True, marker_wrap=None, **kwargs):
646695
'''Creates a customizable progress bar.
647696
648697
The callable takes the same parameters as the `__call__` method
@@ -654,7 +703,7 @@ def __init__(self, marker='#', left='|', right='|', fill=' ',
654703
fill_left - whether to fill from the left or the right
655704
'''
656705

657-
self.marker = create_marker(marker)
706+
self.marker = create_marker(marker, marker_wrap)
658707
self.left = string_or_lambda(left)
659708
self.right = string_or_lambda(right)
660709
self.fill = string_or_lambda(fill)
@@ -670,6 +719,10 @@ def __call__(self, progress, data, width):
670719
width -= progress.custom_len(left) + progress.custom_len(right)
671720
marker = converters.to_unicode(self.marker(progress, data, width))
672721
fill = converters.to_unicode(self.fill(progress, data, width))
722+
723+
# Make sure we ignore invisible characters when filling
724+
width += len(marker) - progress.custom_len(marker)
725+
673726
if self.fill_left:
674727
marker = marker.ljust(width, fill)
675728
else:
@@ -796,7 +849,7 @@ def __call__(self, progress, data, width):
796849
width_accumulated = 0
797850
for marker, value in zip(self.markers, values):
798851
marker = converters.to_unicode(marker(progress, data, width))
799-
assert utils.len_color(marker) == 1
852+
assert progress.custom_len(marker) == 1
800853

801854
values_accumulated += value
802855
item_width = int(values_accumulated / values_sum * width)
@@ -805,7 +858,7 @@ def __call__(self, progress, data, width):
805858
middle += item_width * marker
806859
else:
807860
fill = converters.to_unicode(self.fill(progress, data, width))
808-
assert utils.len_color(fill) == 1
861+
assert progress.custom_len(fill) == 1
809862
middle = fill * width
810863

811864
return left + middle + right

tests/test_widgets.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@
66
max_values = [None, 10, progressbar.UnknownLength]
77

88

9+
def test_create_wrapper():
10+
with pytest.raises(AssertionError):
11+
progressbar.widgets.create_wrapper('ab')
12+
13+
with pytest.raises(RuntimeError):
14+
progressbar.widgets.create_wrapper(123)
15+
16+
917
def test_widgets_small_values():
1018
widgets = [
1119
'Test: ',

0 commit comments

Comments
 (0)