Skip to content

Commit c77f5d8

Browse files
Ken KundertKen Kundert
authored andcommitted
Added bar, modified ProgressBar.
Added bar class, deprecated render_bar. The width of the prefix is now included in the width of the ProgressBar.
1 parent ddd5236 commit c77f5d8

File tree

7 files changed

+137
-30
lines changed

7 files changed

+137
-30
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2014-2024 Kenneth S. Kundert
3+
Copyright (c) 2014-2025 Kenneth S. Kundert
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

doc/api.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ User Utilities
8080
.. autoclass:: inform.ProgressBar
8181
:members:
8282

83+
.. autoclass:: inform.bar
84+
:members:
85+
8386
.. autofunction:: inform.columns
8487

8588
.. autofunction:: inform.conjoin

doc/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848

4949
# General information about the project.
5050
project = u'inform'
51-
copyright = u'2017-2024, Ken Kundert'
51+
copyright = u'2017-2025, Ken Kundert'
5252

5353
# The version info for the project you're documenting, acts as replacement for
5454
# |version| and |release|, also used in various other places throughout the

doc/releases.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ Latest development release
99
| Version: 1.34
1010
| Released: 2025-05-10
1111
12+
- Added :class:`bar`.
13+
- The width of the *prefix* is now included in the width of the :class:`ProgressBar`.
14+
15+
1216
1.34 (2025-05-10)
1317
-----------------
1418

inform/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
Color, Info, LoggingCache,
55

66
# user utility functions and classes
7-
columns, conjoin, dedent, did_you_mean, fmt, format_range, full_stop,
7+
bar, columns, conjoin, dedent, did_you_mean, fmt, format_range, full_stop,
88
os_error, parse_range, plural, ProgressBar, render, render_bar, title_case,
99
tree, truth,
1010

inform/inform.py

Lines changed: 123 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# Utilities for communicating directly with the user.
55
# Documentation can be found at inform.readthedocs.io.
66
#
7-
# Copyright (c) 2014-2024 Kenneth S. Kundert
7+
# Copyright (c) 2014-2025 Kenneth S. Kundert
88
# This software is licensed under the `MIT Licents <https://mit-license.org>`_.
99

1010
# Imports {{{1
@@ -1712,7 +1712,7 @@ def columns(
17121712

17131713

17141714
# render bar {{{2
1715-
def render_bar(value, width=72, full_width=False):
1715+
class bar:
17161716
"""Render graphic representation of a value in the form of a bar
17171717
17181718
Args:
@@ -1722,35 +1722,128 @@ def render_bar(value, width=72, full_width=False):
17221722
17231723
full_width (bool):
17241724
Whether bar should be rendered to fill the whole width using
1725-
trailing spaces,. This is useful if you plan to mark the end of the
1726-
bar.
1725+
trailing spaces. This is useful if anything follows the bar on its
1726+
line, such as if you wish to mark the end of the bar.
1727+
1728+
clip (bool):
1729+
Should the length of the bar be limited to *width* if a value
1730+
greater than 1 is specified.
1731+
1732+
When rendered within a string you can specify a format that overrides the
1733+
above arguments. The format strings take the form *WFC* where:
1734+
1735+
- *W* is an integer that overrides *width*.
1736+
- *F* is either 'f' or 'F' overrides *full_width*; is true if capitalized.
1737+
- *C* is either 'c' or 'C' overrides *clip*; is true if capitalized.
1738+
17271739
**Examples**::
17281740
1729-
>>> from inform import render_bar
1741+
>>> from inform import bar
17301742
17311743
>>> assets = {'property': 13_194, 'cash': 2846, 'equities': 19_301}
17321744
>>> total = sum(assets.values())
17331745
>>> for key, value in assets.items():
1734-
... display(f"{key:>8}: ❭{render_bar(value/total, full_width=True)}❬")
1735-
property: ❭██████████████████████████▉ ❬
1736-
cash: ❭█████▊ ❬
1737-
equities: ❭███████████████████████████████████████▎ ❬
1738-
1746+
... display(f"{key:>8}: ❭{bar(value/total):60F}❬")
1747+
property: ❭██████████████████████▍ ❬
1748+
cash: ❭████▊ ❬
1749+
equities: ❭████████████████████████████████▊ ❬
17391750
17401751
"""
1741-
scaled = value*width
1742-
if scaled > width:
1743-
scaled = width
1744-
if scaled < 0:
1745-
scaled = 0
1746-
buckets = int(scaled)
1747-
frac = int((NUM_BAR_CHARS*scaled) % NUM_BAR_CHARS)
1748-
extra = BAR_CHARS[frac-1:frac]
1749-
bar = buckets*BAR_CHARS[-1] + extra
1750-
if full_width:
1751-
bar += (width - len(bar))*' '
1752-
return bar
1752+
def __init__(self, value, width=72, full_width=False, clip=True):
1753+
self.value = value
1754+
self.width = width
1755+
self.full_width = full_width
1756+
self.clip = clip
1757+
1758+
def render(self, value=None, width=None, full_width=None, clip=None):
1759+
"""Render bar to string
1760+
1761+
Any arguments given will override those specified when class was
1762+
instantiated.
1763+
1764+
Args:
1765+
value (real): Should be normalized (fall between 0 and 1)
1766+
1767+
width (int): The width of the bar in characters when value is 1.
1768+
1769+
full_width (bool):
1770+
Whether bar should be rendered to fill the whole width using
1771+
trailing spaces. This is useful if you plan to mark the end of
1772+
the bar.
1773+
1774+
clip (bool):
1775+
Should the length of the bar be limited to *width* if a value
1776+
greater than 1 is specified.
1777+
"""
1778+
1779+
value = self.value if value is None else value
1780+
width = self.width if width is None else width
1781+
full_width = self.full_width if full_width is None else full_width
1782+
clip = self.clip if clip is None else clip
1783+
scaled = value*width
1784+
if clip and scaled > width:
1785+
scaled = width
1786+
if scaled < 0:
1787+
scaled = 0
1788+
buckets = int(scaled)
1789+
frac = int((NUM_BAR_CHARS*scaled) % NUM_BAR_CHARS)
1790+
extra = BAR_CHARS[frac-1:frac]
1791+
bar = buckets*BAR_CHARS[-1] + extra
1792+
if full_width:
1793+
bar += (width - len(bar))*' '
1794+
return bar
1795+
1796+
def __format__(self, formatter):
1797+
# format strings take the form WFC where:
1798+
# W is an integer indicating desired width
1799+
# F is either 'f' or 'F' for full_width, cap is true
1800+
# C is either 'c' or 'C' for clip, cap is true
1801+
value = self.value
1802+
width = self.width
1803+
full_width = self.full_width
1804+
clip = self.clip
1805+
if formatter:
1806+
match = re.match(r'(\d*)([fFcC]{0,2})', formatter)
1807+
try:
1808+
if match[1]:
1809+
width = int(match[1])
1810+
if 'f' in match[2]:
1811+
full_width = False
1812+
if 'F' in match[2]:
1813+
full_width = True
1814+
if 'c' in match[2]:
1815+
clip = False
1816+
if 'C' in match[2]:
1817+
clip = True
1818+
except (TypeError, ValueError):
1819+
warn('invalid format string')
1820+
return self.render(value, width, full_width, clip)
1821+
1822+
def __str__(self):
1823+
return self.render()
1824+
1825+
def render_bar(value, width=72, full_width=False, clip=True):
1826+
"""Render graphic representation of a value in the form of a bar
1827+
1828+
This function is deprecated. You should instead use::
1829+
1830+
bar(value, width, full_width, clip).render()
17531831
1832+
Args:
1833+
value (real): Should be normalized (fall between 0 and 1)
1834+
1835+
width (int): The width of the bar in characters when value is 1.
1836+
1837+
full_width (bool):
1838+
Whether bar should be rendered to fill the whole width using
1839+
trailing spaces. This is useful if you plan to mark the end of
1840+
the bar.
1841+
1842+
clip (bool):
1843+
Should the length of the bar be limited to *width* if a value
1844+
greater than 1 is specified.
1845+
"""
1846+
return bar(value, width, full_width, clip).render()
17541847

17551848
# tree {{{2
17561849
# _gen_connectors {{{3
@@ -1905,6 +1998,10 @@ class ProgressBar:
19051998
The maximum width of the bar, the largest factor of 10 that
19061999
is less than or equal to this value is used. If width is less than
19072000
or equal to zero, it is added to the current width of the terminal.
2001+
The width includes the width of the prefix. If you are displaying a
2002+
succession of progress bars with prefixes, you should make the all
2003+
the prefixes the same width if you wish to avoid ragged left and
2004+
right boundaries on the bars.
19082005
19092006
informant (informant):
19102007
Which informant to use when outputting the progress bar. By
@@ -1989,7 +2086,10 @@ def __init__(
19892086
try:
19902087
width = os.get_terminal_size().columns + width
19912088
except OSError:
1992-
width=79
2089+
width = 79
2090+
width -= len(prefix or '')
2091+
if width < 10:
2092+
width = 79
19932093

19942094
self.override_limits(stop, start, log)
19952095

tests/test_utilities.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1492,7 +1492,7 @@ def test_filling(capsys):
14921492
stop = 1e-6
14931493
step = 1e-9
14941494
display('before')
1495-
with ProgressBar(stop, prefix='Progress: ', width=60) as progress:
1495+
with ProgressBar(stop, prefix='Progress: ', width=70) as progress:
14961496
value = 0
14971497
while value <= stop/2:
14981498
progress.draw(value)
@@ -1696,13 +1696,13 @@ def test_aerosol(capsys):
16961696
# ProgressBar: empty context manager
16971697
with Inform(prog_name=False, narrate=False, verbose=False, quiet=False, mute=False):
16981698
display('before')
1699-
for vs in ProgressBar([], prefix='nil: |', width=60):
1699+
for vs in ProgressBar([], prefix='nil: |', width=66):
17001700
pass
17011701
display('mid 1')
1702-
for vs in ProgressBar([1], prefix='one: |', width=60):
1702+
for vs in ProgressBar([1], prefix='one: |', width=66):
17031703
pass
17041704
display('mid 2')
1705-
for vs in ProgressBar([1,2], prefix='two: |', width=60):
1705+
for vs in ProgressBar([1,2], prefix='two: |', width=66):
17061706
pass
17071707
display('after')
17081708
captured = capsys.readouterr()

0 commit comments

Comments
 (0)