1from typing import Union 2 3from .color import Color 4from .console import Console, ConsoleOptions, RenderResult 5from .jupyter import JupyterMixin 6from .measure import Measurement 7from .segment import Segment 8from .style import Style 9 10# There are left-aligned characters for 1/8 to 7/8, but 11# the right-aligned characters exist only for 1/8 and 4/8. 12BEGIN_BLOCK_ELEMENTS = ["█", "█", "█", "▐", "▐", "▐", "▕", "▕"] 13END_BLOCK_ELEMENTS = [" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉"] 14FULL_BLOCK = "█" 15 16 17class Bar(JupyterMixin): 18 """Renders a solid block bar. 19 20 Args: 21 size (float): Value for the end of the bar. 22 begin (float): Begin point (between 0 and size, inclusive). 23 end (float): End point (between 0 and size, inclusive). 24 width (int, optional): Width of the bar, or ``None`` for maximum width. Defaults to None. 25 color (Union[Color, str], optional): Color of the bar. Defaults to "default". 26 bgcolor (Union[Color, str], optional): Color of bar background. Defaults to "default". 27 """ 28 29 def __init__( 30 self, 31 size: float, 32 begin: float, 33 end: float, 34 *, 35 width: int = None, 36 color: Union[Color, str] = "default", 37 bgcolor: Union[Color, str] = "default", 38 ): 39 self.size = size 40 self.begin = max(begin, 0) 41 self.end = min(end, size) 42 self.width = width 43 self.style = Style(color=color, bgcolor=bgcolor) 44 45 def __repr__(self) -> str: 46 return f"Bar({self.size}, {self.begin}, {self.end})" 47 48 def __rich_console__( 49 self, console: Console, options: ConsoleOptions 50 ) -> RenderResult: 51 52 width = min( 53 self.width if self.width is not None else options.max_width, 54 options.max_width, 55 ) 56 57 if self.begin >= self.end: 58 yield Segment(" " * width, self.style) 59 yield Segment.line() 60 return 61 62 prefix_complete_eights = int(width * 8 * self.begin / self.size) 63 prefix_bar_count = prefix_complete_eights // 8 64 prefix_eights_count = prefix_complete_eights % 8 65 66 body_complete_eights = int(width * 8 * self.end / self.size) 67 body_bar_count = body_complete_eights // 8 68 body_eights_count = body_complete_eights % 8 69 70 # When start and end fall into the same cell, we ideally should render 71 # a symbol that's "center-aligned", but there is no good symbol in Unicode. 72 # In this case, we fall back to right-aligned block symbol for simplicity. 73 74 prefix = " " * prefix_bar_count 75 if prefix_eights_count: 76 prefix += BEGIN_BLOCK_ELEMENTS[prefix_eights_count] 77 78 body = FULL_BLOCK * body_bar_count 79 if body_eights_count: 80 body += END_BLOCK_ELEMENTS[body_eights_count] 81 82 suffix = " " * (width - len(body)) 83 84 yield Segment(prefix + body[len(prefix) :] + suffix, self.style) 85 yield Segment.line() 86 87 def __rich_measure__( 88 self, console: Console, options: ConsoleOptions 89 ) -> Measurement: 90 return ( 91 Measurement(self.width, self.width) 92 if self.width is not None 93 else Measurement(4, options.max_width) 94 ) 95