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
0 commit comments