| from typing import Optional, Union |
| |
| from .color import Color |
| from .console import Console, ConsoleOptions, RenderResult |
| from .jupyter import JupyterMixin |
| from .measure import Measurement |
| from .segment import Segment |
| from .style import Style |
| |
| # There are left-aligned characters for 1/8 to 7/8, but |
| # the right-aligned characters exist only for 1/8 and 4/8. |
| BEGIN_BLOCK_ELEMENTS = ["█", "█", "█", "▐", "▐", "▐", "▕", "▕"] |
| END_BLOCK_ELEMENTS = [" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉"] |
| FULL_BLOCK = "█" |
| |
| |
| class Bar(JupyterMixin): |
| """Renders a solid block bar. |
| |
| Args: |
| size (float): Value for the end of the bar. |
| begin (float): Begin point (between 0 and size, inclusive). |
| end (float): End point (between 0 and size, inclusive). |
| width (int, optional): Width of the bar, or ``None`` for maximum width. Defaults to None. |
| color (Union[Color, str], optional): Color of the bar. Defaults to "default". |
| bgcolor (Union[Color, str], optional): Color of bar background. Defaults to "default". |
| """ |
| |
| def __init__( |
| self, |
| size: float, |
| begin: float, |
| end: float, |
| *, |
| width: Optional[int] = None, |
| color: Union[Color, str] = "default", |
| bgcolor: Union[Color, str] = "default", |
| ): |
| self.size = size |
| self.begin = max(begin, 0) |
| self.end = min(end, size) |
| self.width = width |
| self.style = Style(color=color, bgcolor=bgcolor) |
| |
| def __repr__(self) -> str: |
| return f"Bar({self.size}, {self.begin}, {self.end})" |
| |
| def __rich_console__( |
| self, console: Console, options: ConsoleOptions |
| ) -> RenderResult: |
| |
| width = min( |
| self.width if self.width is not None else options.max_width, |
| options.max_width, |
| ) |
| |
| if self.begin >= self.end: |
| yield Segment(" " * width, self.style) |
| yield Segment.line() |
| return |
| |
| prefix_complete_eights = int(width * 8 * self.begin / self.size) |
| prefix_bar_count = prefix_complete_eights // 8 |
| prefix_eights_count = prefix_complete_eights % 8 |
| |
| body_complete_eights = int(width * 8 * self.end / self.size) |
| body_bar_count = body_complete_eights // 8 |
| body_eights_count = body_complete_eights % 8 |
| |
| # When start and end fall into the same cell, we ideally should render |
| # a symbol that's "center-aligned", but there is no good symbol in Unicode. |
| # In this case, we fall back to right-aligned block symbol for simplicity. |
| |
| prefix = " " * prefix_bar_count |
| if prefix_eights_count: |
| prefix += BEGIN_BLOCK_ELEMENTS[prefix_eights_count] |
| |
| body = FULL_BLOCK * body_bar_count |
| if body_eights_count: |
| body += END_BLOCK_ELEMENTS[body_eights_count] |
| |
| suffix = " " * (width - len(body)) |
| |
| yield Segment(prefix + body[len(prefix) :] + suffix, self.style) |
| yield Segment.line() |
| |
| def __rich_measure__( |
| self, console: Console, options: ConsoleOptions |
| ) -> Measurement: |
| return ( |
| Measurement(self.width, self.width) |
| if self.width is not None |
| else Measurement(4, options.max_width) |
| ) |