diff --git a/docs/source/appendix/box.rst b/docs/source/appendix/box.rst index e19f0afa..24415fc8 100644 --- a/docs/source/appendix/box.rst +++ b/docs/source/appendix/box.rst @@ -8,120 +8,62 @@ Rich defines a number of ways of drawing boxes and lines such as those used in t from rich import box table = Table(box=box.SQUARE) -The constants are as follows: + + +.. note:: + Some of the box drawing characters will not display correctly on Windows legacy terminal (cmd.exe) with *raster* fonts, and are disabled by default. If you want the full range of box options on Windows legacy terminal, use a *truetype* font and set the ``safe_box`` parameter on the Table class to ``False``. + + +The following table is generated with the following command:: + + python -m rich.box .. raw:: html -
box.ASCII - +-------------------------+ - | | | - |------------+------------| - | | | - |------------+------------| - | | | - +-------------------------+ - box.SQUARE - ┌────────────┬────────────┐ - │ │ │ - ├────────────┼────────────┤ - │ │ │ - ├────────────┼────────────┤ - │ │ │ - └────────────┴────────────┘ - box.MINIMAL - - │ - ────────────┼──────────── - │ - ────────────┼──────────── - │ - - box.MINIMAL_HEAVY_HEAD - - │ - ━━━━━━━━━━━━┿━━━━━━━━━━━━ - │ - ────────────┼──────────── - │ - - box.MINIMAL_DOUBLE_HEAD - - │ - ════════════╪════════════ - │ - ────────────┼──────────── - │ - - box.SIMPLE - - - ─────────────────────────── - - ─────────────────────────── - - - box.SIMPLE_HEAVY - - - ╺━━━━━━━━━━━━━━━━━━━━━━━━━╸ - - ╺━━━━━━━━━━━━━━━━━━━━━━━━━╸ - - - box.HORIZONTALS - ─────────────────────────── - - ─────────────────────────── - - ─────────────────────────── - - ─────────────────────────── - box.ROUNDED - ╭────────────┬────────────╮ - │ │ │ - ├────────────┼────────────┤ - │ │ │ - ├────────────┼────────────┤ - │ │ │ - ╰────────────┴────────────╯ - box.HEAVY - ┏━━━━━━━━━━━━┳━━━━━━━━━━━━┓ - ┃ ┃ ┃ - ┣━━━━━━━━━━━━╋━━━━━━━━━━━━┫ - ┃ ┃ ┃ - ┣━━━━━━━━━━━━╋━━━━━━━━━━━━┫ - ┃ ┃ ┃ - ┗━━━━━━━━━━━━┻━━━━━━━━━━━━┛ - box.HEAVY_EDGE - ┏━━━━━━━━━━━━┯━━━━━━━━━━━━┓ - ┃ │ ┃ - ┠────────────┼────────────┨ - ┃ │ ┃ - ┠────────────┼────────────┨ - ┃ │ ┃ - ┗━━━━━━━━━━━━┷━━━━━━━━━━━━┛ - box.HEAVY_HEAD - ┏━━━━━━━━━━━━┳━━━━━━━━━━━━┓ - ┃ ┃ ┃ - ┡━━━━━━━━━━━━╇━━━━━━━━━━━━┩ - │ │ │ - ├────────────┼────────────┤ - │ │ │ - └────────────┴────────────┘ - box.DOUBLE - ╔════════════╦════════════╗ - ║ ║ ║ - ╠════════════╬════════════╣ - ║ ║ ║ - ╠════════════╬════════════╣ - ║ ║ ║ - ╚════════════╩════════════╝ - box.DOUBLE_EDGE - ╔════════════╤════════════╗ - ║ │ ║ - ╟────────────┼────────────╢ - ║ │ ║ - ╟────────────┼────────────╢ - ║ │ ║ - ╚════════════╧════════════╝ +╭────────────────────────────────────────────────────────────────────────────────────────────────╮ + │ Box Constants │ + ╰────────────────────────────────────────────────────────────────────────────────────────────────╯ + + box.ASCII box.SQUARE box.MINIMAL box.MINIMAL_HEAVY_HEAD + +---------------------+ ┌──────────┬──────────┐ + | Header 1 | Header 2 | │ Header 1 │ Header 2 │ Header 1 │ Header 2 Header 1 │ Header 2 + |----------+----------| ├──────────┼──────────┤ ──────────┼────────── ━━━━━━━━━━┿━━━━━━━━━━ + | Cell | Cell | │ Cell │ Cell │ Cell │ Cell Cell │ Cell + | Cell | Cell | │ Cell │ Cell │ Cell │ Cell Cell │ Cell + |----------+----------| ├──────────┼──────────┤ ──────────┼────────── ──────────┼────────── + | Footer 1 | Footer 2 | │ Footer 1 │ Footer 2 │ Footer 1 │ Footer 2 Footer 1 │ Footer 2 + +---------------------+ └──────────┴──────────┘ + + + box.MINIMAL_DOUBLE_HEAD box.SIMPLE box.SIMPLE_HEAVY box.HORIZONTALS + ─────────────────────── + Header 1 │ Header 2 Header 1 Header 2 Header 1 Header 2 Header 1 Header 2 + ══════════╪══════════ ─────────────────────── ╺━━━━━━━━━━━━━━━━━━━━━╸ ─────────────────────── + Cell │ Cell Cell Cell Cell Cell Cell Cell + Cell │ Cell Cell Cell Cell Cell Cell Cell + ──────────┼────────── ─────────────────────── ╺━━━━━━━━━━━━━━━━━━━━━╸ ─────────────────────── + Footer 1 │ Footer 2 Footer 1 Footer 2 Footer 1 Footer 2 Footer 1 Footer 2 + ─────────────────────── + + + box.ROUNDED box.HEAVY box.HEAVY_EDGE box.HEAVY_HEAD + ╭──────────┬──────────╮ ┏━━━━━━━━━━┳━━━━━━━━━━┓ ┏━━━━━━━━━━┯━━━━━━━━━━┓ ┏━━━━━━━━━━┳━━━━━━━━━━┓ + │ Header 1 │ Header 2 │ ┃ Header 1 ┃ Header 2 ┃ ┃ Header 1 │ Header 2 ┃ ┃ Header 1 ┃ Header 2 ┃ + ├──────────┼──────────┤ ┣━━━━━━━━━━╋━━━━━━━━━━┫ ┠──────────┼──────────┨ ┡━━━━━━━━━━╇━━━━━━━━━━┩ + │ Cell │ Cell │ ┃ Cell ┃ Cell ┃ ┃ Cell │ Cell ┃ │ Cell │ Cell │ + │ Cell │ Cell │ ┃ Cell ┃ Cell ┃ ┃ Cell │ Cell ┃ │ Cell │ Cell │ + ├──────────┼──────────┤ ┣━━━━━━━━━━╋━━━━━━━━━━┫ ┠──────────┼──────────┨ ├──────────┼──────────┤ + │ Footer 1 │ Footer 2 │ ┃ Footer 1 ┃ Footer 2 ┃ ┃ Footer 1 │ Footer 2 ┃ │ Footer 1 │ Footer 2 │ + ╰──────────┴──────────╯ ┗━━━━━━━━━━┻━━━━━━━━━━┛ ┗━━━━━━━━━━┷━━━━━━━━━━┛ └──────────┴──────────┘ + + + box.DOUBLE box.DOUBLE_EDGE + ╔══════════╦══════════╗ ╔══════════╤══════════╗ + ║ Header 1 ║ Header 2 ║ ║ Header 1 │ Header 2 ║ + ╠══════════╬══════════╣ ╟──────────┼──────────╢ + ║ Cell ║ Cell ║ ║ Cell │ Cell ║ + ║ Cell ║ Cell ║ ║ Cell │ Cell ║ + ╠══════════╬══════════╣ ╟──────────┼──────────╢ + ║ Footer 1 ║ Footer 2 ║ ║ Footer 1 │ Footer 2 ║ + ╚══════════╩══════════╝ ╚══════════╧══════════╝\ No newline at end of file diff --git a/rich/box.py b/rich/box.py index f100d8b1..a7b14b54 100644 --- a/rich/box.py +++ b/rich/box.py @@ -348,9 +348,20 @@ DOUBLE_EDGE: Box = Box( """ ) +LEGACY_WINDOWS_SUBSTITUTIONS = { + ROUNDED: SQUARE, + MINIMAL_HEAVY_HEAD: MINIMAL, + SIMPLE_HEAVY: SIMPLE, + HEAVY: SQUARE, + HEAVY_EDGE: SQUARE, + HEAVY_HEAD: SQUARE, +} + if __name__ == "__main__": # pragma: no cover + from rich.columns import Columns + from rich.panel import Panel from .console import Console from .table import Table from .text import Text @@ -358,12 +369,6 @@ if __name__ == "__main__": # pragma: no cover console = Console(record=True) - table = Table(width=80, show_footer=True, style="dim", border_style="not dim") - spaces = " " * 10 - table.add_column(spaces, spaces) - table.add_column(spaces, spaces) - table.add_row(spaces, spaces) - BOXES = [ "ASCII", "SQUARE", @@ -381,9 +386,21 @@ if __name__ == "__main__": # pragma: no cover "DOUBLE_EDGE", ] + console.print(Panel("[bold green]Box Constants", style="green"), justify="center") + console.print() + + columns = Columns(expand=False, padding=2) for box_name in BOXES: + table = Table(width=80, show_footer=True, style="dim", border_style="not dim") + spaces = " " * 10 + 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") - console.print(table) + columns.add_renderable(table) + console.print(columns) + + # console.save_html("box.html", inline_styles=True) - console.save_html("box.html", inline_styles=True) diff --git a/rich/columns.py b/rich/columns.py index 6d530c08..be05c090 100644 --- a/rich/columns.py +++ b/rich/columns.py @@ -15,7 +15,7 @@ class Columns(JupyterMixin): """Display renderables in neat columns. Args: - renderables (Iterable[RenderableType]): Any number of Rich renderables (including str), + renderables (Iterable[RenderableType]): Any number of Rich renderables (including str). width (int, optional): The desired width of the columns, or None to auto detect. Defaults to None. padding (PaddingDimensions, optional): Optional padding around cells. Defaults to (0, 1). expand (bool, optional): Expand columns to full width. Defaults to False. @@ -26,7 +26,7 @@ class Columns(JupyterMixin): def __init__( self, - renderables: Iterable[RenderableType], + renderables: Iterable[RenderableType] = None, padding: PaddingDimensions = (0, 1), width: int = None, expand: bool = False, @@ -34,7 +34,7 @@ class Columns(JupyterMixin): column_first: bool = False, right_to_left: bool = False, ) -> None: - self.renderables = list(renderables) + self.renderables = list(renderables or []) self.width = width self.padding = padding self.expand = expand @@ -42,6 +42,14 @@ class Columns(JupyterMixin): self.column_first = column_first self.right_to_left = right_to_left + def add_renderable(self, renderable: RenderableType) -> None: + """Add a renderable to the columns. + + Args: + renderable (RenderableType): Any renderable object. + """ + self.renderables.append(renderable) + def __rich_console__( self, console: Console, options: ConsoleOptions ) -> RenderResult: diff --git a/rich/table.py b/rich/table.py index 6204e0a7..6d28f828 100644 --- a/rich/table.py +++ b/rich/table.py @@ -112,6 +112,7 @@ class Table(JupyterMixin): border_style (Union[str, Style], optional): Style of the border. Defaults to None. title_style (Union[str, Style], optional): Style of the title. Defaults to None. caption_style (Union[str, Style], optional): Style of the caption. Defaults to None. + safe_box (bool, optional): Disable box characters that don't display on windows legacy terminal with *raster* fonts. Defaults to True. """ columns: List[Column] @@ -138,6 +139,7 @@ class Table(JupyterMixin): border_style: StyleType = None, title_style: StyleType = None, caption_style: StyleType = None, + safe_box: bool = True, ) -> None: self.columns = [ @@ -162,6 +164,7 @@ class Table(JupyterMixin): self.border_style = border_style self.title_style = title_style self.caption_style = title_style + self.safe_box = safe_box self._row_count = 0 self.row_styles = list(row_styles or []) @@ -481,11 +484,11 @@ class Table(JupyterMixin): for first, last, (style, renderable) in loop_first_last(raw_cells): yield _Cell(style, add_padding(renderable, first, last)) - def _get_padding_width(self, column_index) -> int: + def _get_padding_width(self, column_index: int) -> int: """Get extra width from padding.""" _, pad_right, _, pad_left = self.padding if self.collapse_padding: - if column_index != 0: + if column_index > 0: pad_left = max(0, pad_right - pad_left) return pad_left + pad_right @@ -537,6 +540,9 @@ class Table(JupyterMixin): if isinstance(self.box, box.PlatformDefaultBox) else self.box ) + if self.safe_box and _box is not None and console.legacy_windows: + _box = box.LEGACY_WINDOWS_SUBSTITUTIONS.get(_box, _box) + # _box = self.box new_line = Segment.line() diff --git a/tests/test_columns.py b/tests/test_columns.py index b418aa4d..b07af7cb 100644 --- a/tests/test_columns.py +++ b/tests/test_columns.py @@ -25,7 +25,6 @@ COLUMN_DATA = [ "Anteater, australian spiny", "Tachyglossus aculeatus", "Anteater, giant", - "Myrmecophaga tridactyla", ] @@ -36,6 +35,7 @@ def render(): empty_columns = Columns([]) console.print(empty_columns) columns = Columns(COLUMN_DATA) + columns.add_renderable("Myrmecophaga tridactyla") console.rule("optimal") console.print(columns) console.rule("optimal, expand")