| from typing import TYPE_CHECKING, Iterable, List, Literal |
|
|
|
|
| from ._loop import loop_last |
|
|
| if TYPE_CHECKING: |
| from rich.console import ConsoleOptions |
|
|
|
|
| class Box: |
| """Defines characters to render boxes. |
| |
| โโโฌโ top |
| โ โโ head |
| โโโผโค head_row |
| โ โโ mid |
| โโโผโค row |
| โโโผโค foot_row |
| โ โโ foot |
| โโโดโ bottom |
| |
| Args: |
| box (str): Characters making up box. |
| ascii (bool, optional): True if this box uses ascii characters only. Default is False. |
| """ |
|
|
| def __init__(self, box: str, *, ascii: bool = False) -> None: |
| self._box = box |
| self.ascii = ascii |
| line1, line2, line3, line4, line5, line6, line7, line8 = box.splitlines() |
| |
| self.top_left, self.top, self.top_divider, self.top_right = iter(line1) |
| |
| self.head_left, _, self.head_vertical, self.head_right = iter(line2) |
| |
| ( |
| self.head_row_left, |
| self.head_row_horizontal, |
| self.head_row_cross, |
| self.head_row_right, |
| ) = iter(line3) |
|
|
| |
| self.mid_left, _, self.mid_vertical, self.mid_right = iter(line4) |
| |
| self.row_left, self.row_horizontal, self.row_cross, self.row_right = iter(line5) |
| |
| ( |
| self.foot_row_left, |
| self.foot_row_horizontal, |
| self.foot_row_cross, |
| self.foot_row_right, |
| ) = iter(line6) |
| |
| self.foot_left, _, self.foot_vertical, self.foot_right = iter(line7) |
| |
| self.bottom_left, self.bottom, self.bottom_divider, self.bottom_right = iter( |
| line8 |
| ) |
|
|
| def __repr__(self) -> str: |
| return "Box(...)" |
|
|
| def __str__(self) -> str: |
| return self._box |
|
|
| def substitute(self, options: "ConsoleOptions", safe: bool = True) -> "Box": |
| """Substitute this box for another if it won't render due to platform issues. |
| |
| Args: |
| options (ConsoleOptions): Console options used in rendering. |
| safe (bool, optional): Substitute this for another Box if there are known problems |
| displaying on the platform (currently only relevant on Windows). Default is True. |
| |
| Returns: |
| Box: A different Box or the same Box. |
| """ |
| box = self |
| if options.legacy_windows and safe: |
| box = LEGACY_WINDOWS_SUBSTITUTIONS.get(box, box) |
| if options.ascii_only and not box.ascii: |
| box = ASCII |
| return box |
|
|
| def get_plain_headed_box(self) -> "Box": |
| """If this box uses special characters for the borders of the header, then |
| return the equivalent box that does not. |
| |
| Returns: |
| Box: The most similar Box that doesn't use header-specific box characters. |
| If the current Box already satisfies this criterion, then it's returned. |
| """ |
| return PLAIN_HEADED_SUBSTITUTIONS.get(self, self) |
|
|
| def get_top(self, widths: Iterable[int]) -> str: |
| """Get the top of a simple box. |
| |
| Args: |
| widths (List[int]): Widths of columns. |
| |
| Returns: |
| str: A string of box characters. |
| """ |
|
|
| parts: List[str] = [] |
| append = parts.append |
| append(self.top_left) |
| for last, width in loop_last(widths): |
| append(self.top * width) |
| if not last: |
| append(self.top_divider) |
| append(self.top_right) |
| return "".join(parts) |
|
|
| def get_row( |
| self, |
| widths: Iterable[int], |
| level: Literal["head", "row", "foot", "mid"] = "row", |
| edge: bool = True, |
| ) -> str: |
| """Get the top of a simple box. |
| |
| Args: |
| width (List[int]): Widths of columns. |
| |
| Returns: |
| str: A string of box characters. |
| """ |
| if level == "head": |
| left = self.head_row_left |
| horizontal = self.head_row_horizontal |
| cross = self.head_row_cross |
| right = self.head_row_right |
| elif level == "row": |
| left = self.row_left |
| horizontal = self.row_horizontal |
| cross = self.row_cross |
| right = self.row_right |
| elif level == "mid": |
| left = self.mid_left |
| horizontal = " " |
| cross = self.mid_vertical |
| right = self.mid_right |
| elif level == "foot": |
| left = self.foot_row_left |
| horizontal = self.foot_row_horizontal |
| cross = self.foot_row_cross |
| right = self.foot_row_right |
| else: |
| raise ValueError("level must be 'head', 'row' or 'foot'") |
|
|
| parts: List[str] = [] |
| append = parts.append |
| if edge: |
| append(left) |
| for last, width in loop_last(widths): |
| append(horizontal * width) |
| if not last: |
| append(cross) |
| if edge: |
| append(right) |
| return "".join(parts) |
|
|
| def get_bottom(self, widths: Iterable[int]) -> str: |
| """Get the bottom of a simple box. |
| |
| Args: |
| widths (List[int]): Widths of columns. |
| |
| Returns: |
| str: A string of box characters. |
| """ |
|
|
| parts: List[str] = [] |
| append = parts.append |
| append(self.bottom_left) |
| for last, width in loop_last(widths): |
| append(self.bottom * width) |
| if not last: |
| append(self.bottom_divider) |
| append(self.bottom_right) |
| return "".join(parts) |
|
|
|
|
| |
| ASCII: Box = Box( |
| "+--+\n" |
| "| ||\n" |
| "|-+|\n" |
| "| ||\n" |
| "|-+|\n" |
| "|-+|\n" |
| "| ||\n" |
| "+--+\n", |
| ascii=True, |
| ) |
|
|
| ASCII2: Box = Box( |
| "+-++\n" |
| "| ||\n" |
| "+-++\n" |
| "| ||\n" |
| "+-++\n" |
| "+-++\n" |
| "| ||\n" |
| "+-++\n", |
| ascii=True, |
| ) |
|
|
| ASCII_DOUBLE_HEAD: Box = Box( |
| "+-++\n" |
| "| ||\n" |
| "+=++\n" |
| "| ||\n" |
| "+-++\n" |
| "+-++\n" |
| "| ||\n" |
| "+-++\n", |
| ascii=True, |
| ) |
|
|
| SQUARE: Box = Box( |
| "โโโฌโ\n" |
| "โ โโ\n" |
| "โโโผโค\n" |
| "โ โโ\n" |
| "โโโผโค\n" |
| "โโโผโค\n" |
| "โ โโ\n" |
| "โโโดโ\n" |
| ) |
|
|
| SQUARE_DOUBLE_HEAD: Box = Box( |
| "โโโฌโ\n" |
| "โ โโ\n" |
| "โโโชโก\n" |
| "โ โโ\n" |
| "โโโผโค\n" |
| "โโโผโค\n" |
| "โ โโ\n" |
| "โโโดโ\n" |
| ) |
|
|
| MINIMAL: Box = Box( |
| " โท \n" |
| " โ \n" |
| "โถโโผโด\n" |
| " โ \n" |
| "โถโโผโด\n" |
| "โถโโผโด\n" |
| " โ \n" |
| " โต \n" |
| ) |
|
|
|
|
| MINIMAL_HEAVY_HEAD: Box = Box( |
| " โท \n" |
| " โ \n" |
| "โบโโฟโธ\n" |
| " โ \n" |
| "โถโโผโด\n" |
| "โถโโผโด\n" |
| " โ \n" |
| " โต \n" |
| ) |
|
|
| MINIMAL_DOUBLE_HEAD: Box = Box( |
| " โท \n" |
| " โ \n" |
| " โโช \n" |
| " โ \n" |
| " โโผ \n" |
| " โโผ \n" |
| " โ \n" |
| " โต \n" |
| ) |
|
|
|
|
| SIMPLE: Box = Box( |
| " \n" |
| " \n" |
| " โโ \n" |
| " \n" |
| " \n" |
| " โโ \n" |
| " \n" |
| " \n" |
| ) |
|
|
| SIMPLE_HEAD: Box = Box( |
| " \n" |
| " \n" |
| " โโ \n" |
| " \n" |
| " \n" |
| " \n" |
| " \n" |
| " \n" |
| ) |
|
|
|
|
| SIMPLE_HEAVY: Box = Box( |
| " \n" |
| " \n" |
| " โโ \n" |
| " \n" |
| " \n" |
| " โโ \n" |
| " \n" |
| " \n" |
| ) |
|
|
|
|
| HORIZONTALS: Box = Box( |
| " โโ \n" |
| " \n" |
| " โโ \n" |
| " \n" |
| " โโ \n" |
| " โโ \n" |
| " \n" |
| " โโ \n" |
| ) |
|
|
| ROUNDED: Box = Box( |
| "โญโโฌโฎ\n" |
| "โ โโ\n" |
| "โโโผโค\n" |
| "โ โโ\n" |
| "โโโผโค\n" |
| "โโโผโค\n" |
| "โ โโ\n" |
| "โฐโโดโฏ\n" |
| ) |
|
|
| HEAVY: Box = Box( |
| "โโโณโ\n" |
| "โ โโ\n" |
| "โฃโโโซ\n" |
| "โ โโ\n" |
| "โฃโโโซ\n" |
| "โฃโโโซ\n" |
| "โ โโ\n" |
| "โโโปโ\n" |
| ) |
|
|
| HEAVY_EDGE: Box = Box( |
| "โโโฏโ\n" |
| "โ โโ\n" |
| "โ โโผโจ\n" |
| "โ โโ\n" |
| "โ โโผโจ\n" |
| "โ โโผโจ\n" |
| "โ โโ\n" |
| "โโโทโ\n" |
| ) |
|
|
| HEAVY_HEAD: Box = Box( |
| "โโโณโ\n" |
| "โ โโ\n" |
| "โกโโโฉ\n" |
| "โ โโ\n" |
| "โโโผโค\n" |
| "โโโผโค\n" |
| "โ โโ\n" |
| "โโโดโ\n" |
| ) |
|
|
| DOUBLE: Box = Box( |
| "โโโฆโ\n" |
| "โ โโ\n" |
| "โ โโฌโฃ\n" |
| "โ โโ\n" |
| "โ โโฌโฃ\n" |
| "โ โโฌโฃ\n" |
| "โ โโ\n" |
| "โโโฉโ\n" |
| ) |
|
|
| DOUBLE_EDGE: Box = Box( |
| "โโโคโ\n" |
| "โ โโ\n" |
| "โโโผโข\n" |
| "โ โโ\n" |
| "โโโผโข\n" |
| "โโโผโข\n" |
| "โ โโ\n" |
| "โโโงโ\n" |
| ) |
|
|
| MARKDOWN: Box = Box( |
| " \n" |
| "| ||\n" |
| "|-||\n" |
| "| ||\n" |
| "|-||\n" |
| "|-||\n" |
| "| ||\n" |
| " \n", |
| ascii=True, |
| ) |
| |
|
|
| |
| LEGACY_WINDOWS_SUBSTITUTIONS = { |
| ROUNDED: SQUARE, |
| MINIMAL_HEAVY_HEAD: MINIMAL, |
| SIMPLE_HEAVY: SIMPLE, |
| HEAVY: SQUARE, |
| HEAVY_EDGE: SQUARE, |
| HEAVY_HEAD: SQUARE, |
| } |
|
|
| |
| PLAIN_HEADED_SUBSTITUTIONS = { |
| HEAVY_HEAD: SQUARE, |
| SQUARE_DOUBLE_HEAD: SQUARE, |
| MINIMAL_DOUBLE_HEAD: MINIMAL, |
| MINIMAL_HEAVY_HEAD: MINIMAL, |
| ASCII_DOUBLE_HEAD: ASCII2, |
| } |
|
|
|
|
| if __name__ == "__main__": |
| from rich.columns import Columns |
| from rich.panel import Panel |
|
|
| from . import box as box |
| from .console import Console |
| from .table import Table |
| from .text import Text |
|
|
| console = Console(record=True) |
|
|
| BOXES = [ |
| "ASCII", |
| "ASCII2", |
| "ASCII_DOUBLE_HEAD", |
| "SQUARE", |
| "SQUARE_DOUBLE_HEAD", |
| "MINIMAL", |
| "MINIMAL_HEAVY_HEAD", |
| "MINIMAL_DOUBLE_HEAD", |
| "SIMPLE", |
| "SIMPLE_HEAD", |
| "SIMPLE_HEAVY", |
| "HORIZONTALS", |
| "ROUNDED", |
| "HEAVY", |
| "HEAVY_EDGE", |
| "HEAVY_HEAD", |
| "DOUBLE", |
| "DOUBLE_EDGE", |
| "MARKDOWN", |
| ] |
|
|
| console.print(Panel("[bold green]Box Constants", style="green"), justify="center") |
| console.print() |
|
|
| columns = Columns(expand=True, padding=2) |
| for box_name in sorted(BOXES): |
| table = Table( |
| show_footer=True, style="dim", border_style="not dim", expand=True |
| ) |
| table.add_column("Header 1", "Footer 1") |
| table.add_column("Header 2", "Footer 2") |
| table.add_row("Cell", "Cell") |
| table.add_row("Cell", "Cell") |
| table.box = getattr(box, box_name) |
| table.title = Text(f"box.{box_name}", style="magenta") |
| columns.add_renderable(table) |
| console.print(columns) |
|
|
| |
|
|