mirror of https://github.com/Textualize/rich.git
table shrinking
This commit is contained in:
parent
05db129a6f
commit
181973311b
14
CHANGELOG.md
14
CHANGELOG.md
|
@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [3.0.3] - 2020-07-03
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed edge case with wrapped and overflowed text
|
||||
|
||||
### Changed
|
||||
|
||||
- New algorithm for compressing table that priorities smaller columns
|
||||
|
||||
### Added
|
||||
|
||||
- Added safe_box parameter to Console constructor
|
||||
|
||||
## [3.0.2] - 2020-07-02
|
||||
|
||||
### Added
|
||||
|
|
|
@ -54,7 +54,7 @@ TABLE_DATA = [
|
|||
|
||||
console = Console()
|
||||
|
||||
BEAT_TIME = 0.05
|
||||
BEAT_TIME = 0.04
|
||||
|
||||
|
||||
@contextmanager
|
||||
|
@ -110,7 +110,7 @@ try:
|
|||
with beat(10):
|
||||
console.print(table, justify="center")
|
||||
|
||||
table.caption = "Made with [b magenta]Rich[/]"
|
||||
table.caption = "Made with [b magenta not dim]Rich[/]"
|
||||
with beat(10):
|
||||
console.print(table, justify="center")
|
||||
|
||||
|
@ -187,25 +187,13 @@ try:
|
|||
with beat(10):
|
||||
console.print(table, justify="center")
|
||||
|
||||
for color in [
|
||||
"deep_pink4",
|
||||
"dark_khaki",
|
||||
"medium_purple2",
|
||||
"thistle3",
|
||||
"orange1",
|
||||
]:
|
||||
table.border_style = color
|
||||
with beat(10):
|
||||
console.print(table, justify="center")
|
||||
table.border_style = "bright_yellow"
|
||||
with beat(10):
|
||||
console.print(table, justify="center")
|
||||
|
||||
for box in [
|
||||
box.ASCII,
|
||||
box.ASCII2,
|
||||
box.DOUBLE,
|
||||
box.DOUBLE_EDGE,
|
||||
box.HEAVY_EDGE,
|
||||
box.MINIMAL_HEAVY_HEAD,
|
||||
box.MINIMAL_DOUBLE_HEAD,
|
||||
box.SQUARE,
|
||||
box.MINIMAL,
|
||||
box.SIMPLE,
|
||||
box.SIMPLE_HEAD,
|
||||
]:
|
||||
|
@ -213,6 +201,10 @@ try:
|
|||
with beat(20):
|
||||
console.print(table, justify="center")
|
||||
|
||||
table.pad_edge = False
|
||||
with beat(20):
|
||||
console.print(table, justify="center")
|
||||
|
||||
original_width = Measurement.get(console, table).maximum
|
||||
|
||||
for width in range(original_width, console.width, 2):
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
name = "rich"
|
||||
homepage = "https://github.com/willmcgugan/rich"
|
||||
documentation = "https://rich.readthedocs.io/en/latest/"
|
||||
version = "3.0.2"
|
||||
version = "3.0.3"
|
||||
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
|
||||
authors = ["Will McGugan <willmcgugan@gmail.com>"]
|
||||
license = "MIT"
|
||||
|
|
23
rich/box.py
23
rich/box.py
|
@ -228,10 +228,10 @@ SIMPLE: Box = Box(
|
|||
"""\
|
||||
|
||||
|
||||
╶──╴
|
||||
──
|
||||
|
||||
|
||||
╶──╴
|
||||
──
|
||||
|
||||
|
||||
"""
|
||||
|
@ -241,7 +241,7 @@ SIMPLE_HEAD: Box = Box(
|
|||
"""\
|
||||
|
||||
|
||||
╶──╴
|
||||
──
|
||||
|
||||
|
||||
|
||||
|
@ -255,10 +255,10 @@ SIMPLE_HEAVY: Box = Box(
|
|||
"""\
|
||||
|
||||
|
||||
╺━━╸
|
||||
━━
|
||||
|
||||
|
||||
╺━━╸
|
||||
━━
|
||||
|
||||
|
||||
"""
|
||||
|
@ -267,14 +267,14 @@ SIMPLE_HEAVY: Box = Box(
|
|||
|
||||
HORIZONTALS: Box = Box(
|
||||
"""\
|
||||
╶──╴
|
||||
──
|
||||
|
||||
╶──╴
|
||||
──
|
||||
|
||||
╶──╴
|
||||
╶──╴
|
||||
──
|
||||
──
|
||||
|
||||
╶──╴
|
||||
──
|
||||
"""
|
||||
)
|
||||
|
||||
|
@ -412,7 +412,8 @@ if __name__ == "__main__": # pragma: no cover
|
|||
"MINIMAL_HEAVY_HEAD",
|
||||
"MINIMAL_DOUBLE_HEAD",
|
||||
"SIMPLE",
|
||||
"SIMPLE_HEAD" "SIMPLE_HEAVY",
|
||||
"SIMPLE_HEAD",
|
||||
"SIMPLE_HEAVY",
|
||||
"HORIZONTALS",
|
||||
"ROUNDED",
|
||||
"HEAVY",
|
||||
|
|
|
@ -277,6 +277,7 @@ class Console:
|
|||
log_time_format (str, optional): Log time format if ``log_time`` is enabled. Defaults to "[%X] ".
|
||||
highlighter (HighlighterType, optional): Default highlighter.
|
||||
legacy_windows (bool, optional): Enable legacy Windows mode, or ``None`` to auto detect. Defaults to ``None``.
|
||||
safe_box (bool, optional): Restrict box options that don't render on legacy Windows.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
|
@ -301,6 +302,7 @@ class Console:
|
|||
log_time_format: str = "[%X]",
|
||||
highlighter: Optional["HighlighterType"] = ReprHighlighter(),
|
||||
legacy_windows: bool = None,
|
||||
safe_box: bool = True,
|
||||
):
|
||||
self.is_jupyter = force_jupyter or _is_jupyter()
|
||||
if self.is_jupyter:
|
||||
|
@ -336,6 +338,7 @@ class Console:
|
|||
show_time=log_time, show_path=log_path, time_format=log_time_format
|
||||
)
|
||||
self.highlighter: HighlighterType = highlighter or _null_highlighter
|
||||
self.safe_box = safe_box
|
||||
|
||||
self._record_buffer_lock = threading.RLock()
|
||||
self._thread_locals = ConsoleThreadLocals()
|
||||
|
|
|
@ -22,7 +22,7 @@ class Measurement(NamedTuple):
|
|||
def normalize(self) -> "Measurement":
|
||||
minimum, maximum = self
|
||||
minimum = min(max(0, minimum), maximum)
|
||||
return Measurement(minimum, max(minimum, maximum))
|
||||
return Measurement(max(0, minimum), max(0, max(minimum, maximum)))
|
||||
|
||||
def with_maximum(self, width: int) -> "Measurement":
|
||||
"""Get a RenderableWith where the widths are <= width.
|
||||
|
@ -63,8 +63,10 @@ class Measurement(NamedTuple):
|
|||
if is_renderable(renderable):
|
||||
get_console_width = getattr(renderable, "__rich_measure__", None)
|
||||
if get_console_width is not None:
|
||||
render_width = get_console_width(console, _max_width).with_maximum(
|
||||
_max_width
|
||||
render_width = (
|
||||
get_console_width(console, _max_width)
|
||||
.normalize()
|
||||
.with_maximum(_max_width)
|
||||
)
|
||||
return render_width.normalize()
|
||||
else:
|
||||
|
|
|
@ -39,7 +39,7 @@ class Panel(JupyterMixin):
|
|||
renderable: RenderableType,
|
||||
box: Box = ROUNDED,
|
||||
*,
|
||||
safe_box: bool = True,
|
||||
safe_box: Optional[bool] = None,
|
||||
expand: bool = True,
|
||||
style: Union[str, Style] = "none",
|
||||
width: Optional[int] = None,
|
||||
|
@ -74,11 +74,8 @@ class Panel(JupyterMixin):
|
|||
width = child_width + 2
|
||||
child_options = options.update(width=child_width)
|
||||
lines = console.render_lines(renderable, child_options)
|
||||
box = (
|
||||
get_safe_box(self.box, console.legacy_windows)
|
||||
if self.safe_box
|
||||
else self.box
|
||||
)
|
||||
safe_box = self.safe_box if self.safe_box is not None else console.safe_box
|
||||
box = get_safe_box(self.box, console.legacy_windows) if safe_box else self.box
|
||||
line_start = Segment(box.mid_left, style)
|
||||
line_end = Segment(f"{box.mid_right}\n", style)
|
||||
yield Segment(box.get_top([width - 2]), style)
|
||||
|
|
|
@ -832,7 +832,7 @@ if __name__ == "__main__": # pragma: no coverage
|
|||
|
||||
task1 = progress.add_task(" [red]Downloading", total=1000)
|
||||
task2 = progress.add_task(" [green]Processing", total=1000)
|
||||
# task3 = progress.add_task(" [yellow]Thinking", total=1000, start=False)
|
||||
task3 = progress.add_task(" [yellow]Thinking", total=1000, start=False)
|
||||
|
||||
while not progress.finished:
|
||||
progress.update(task1, advance=0.5)
|
||||
|
|
119
rich/table.py
119
rich/table.py
|
@ -98,7 +98,7 @@ class Table(JupyterMixin):
|
|||
caption (Union[str, Text], optional): The table caption rendered below. Defaults to None.
|
||||
width (int, optional): The width in characters of the table, or ``None`` to automatically fit. Defaults to None.
|
||||
box (box.Box, optional): One of the constants in box.py used to draw the edges (see :ref:`appendix_box`). Defaults to box.HEAVY_HEAD.
|
||||
safe_box (bool, optional): Disable box characters that don't display on windows legacy terminal with *raster* fonts. Defaults to True.
|
||||
safe_box (Optional[bool], optional): Disable box characters that don't display on windows legacy terminal with *raster* fonts. Defaults to True.
|
||||
padding (PaddingDimensions, optional): Padding for cells (top, right, bottom, left). Defaults to (0, 1).
|
||||
collapse_padding (bool, optional): Enable collapsing of padding around cells. Defaults to False.
|
||||
pad_edge (bool, optional): Enable padding of edge cells. Defaults to True.
|
||||
|
@ -125,7 +125,7 @@ class Table(JupyterMixin):
|
|||
caption: TextType = None,
|
||||
width: int = None,
|
||||
box: Optional[box.Box] = box.HEAVY_HEAD,
|
||||
safe_box: bool = True,
|
||||
safe_box: Optional[bool] = None,
|
||||
padding: PaddingDimensions = (0, 1),
|
||||
collapse_padding: bool = False,
|
||||
pad_edge: bool = True,
|
||||
|
@ -396,8 +396,7 @@ class Table(JupyterMixin):
|
|||
"""Calculate the widths of each column, including padding, not including borders."""
|
||||
columns = self.columns
|
||||
width_ranges = [
|
||||
self._measure_column(console, column, max_width)
|
||||
for column_index, column in enumerate(columns)
|
||||
self._measure_column(console, column, max_width) for column in columns
|
||||
]
|
||||
widths = [_range.maximum or 1 for _range in width_ranges]
|
||||
get_padding_width = self._get_padding_width
|
||||
|
@ -422,25 +421,19 @@ class Table(JupyterMixin):
|
|||
widths[index] = fixed_widths[index] + next(iter_flex_widths)
|
||||
table_width = sum(widths)
|
||||
|
||||
# Reduce rows that not no_wrap
|
||||
if table_width > max_width:
|
||||
widths = self._collapse_widths(
|
||||
widths, [not column.no_wrap for column in columns], max_width
|
||||
)
|
||||
|
||||
table_width = sum(widths)
|
||||
# last resort, reduce columns evenly
|
||||
if table_width > max_width:
|
||||
excess_width = table_width - max_width
|
||||
widths = ratio_reduce(
|
||||
excess_width,
|
||||
[width_range.minimum for width_range in width_ranges],
|
||||
widths,
|
||||
widths,
|
||||
)
|
||||
table_width = sum(widths)
|
||||
|
||||
elif table_width < max_width and self.expand:
|
||||
# last resort, reduce columns evenly
|
||||
if table_width > max_width:
|
||||
excess_width = table_width - max_width
|
||||
widths = ratio_reduce(excess_width, [1] * len(widths), widths, widths)
|
||||
table_width = sum(widths)
|
||||
|
||||
if table_width < max_width and self.expand:
|
||||
pad_widths = ratio_distribute(max_width - table_width, widths)
|
||||
widths = [_width + pad for _width, pad in zip(widths, pad_widths)]
|
||||
|
||||
|
@ -450,37 +443,42 @@ class Table(JupyterMixin):
|
|||
def _collapse_widths(
|
||||
cls, widths: List[int], wrapable: List[bool], max_width: int
|
||||
) -> List[int]:
|
||||
widths = widths[:]
|
||||
"""Reduce widths so that the total is under max_width.
|
||||
|
||||
Args:
|
||||
widths (List[int]): List of widths.
|
||||
wrapable (List[bool]): List of booleans that indicate if a column may shrink.
|
||||
max_width (int): Maximum width to reduce to.
|
||||
|
||||
Returns:
|
||||
List[int]: A new list of widths.
|
||||
"""
|
||||
total_width = sum(widths)
|
||||
excess_width = total_width - max_width
|
||||
while excess_width > 0:
|
||||
max_column = max(
|
||||
width if allow_wrap else 0
|
||||
for width, allow_wrap in zip(widths, wrapable)
|
||||
)
|
||||
if max_column == 0:
|
||||
break
|
||||
try:
|
||||
second_max_column = max(
|
||||
width if allow_wrap and width != max_column else 0
|
||||
for width, allow_wrap in zip(widths, wrapable)
|
||||
if any(wrapable):
|
||||
while total_width and excess_width > 0:
|
||||
max_column = max(
|
||||
width for width, allow_wrap in zip(widths, wrapable) if allow_wrap
|
||||
)
|
||||
except ValueError:
|
||||
second_max_column = 0
|
||||
column_difference = max_column - second_max_column
|
||||
ratios = [
|
||||
(1 if (width == max_column and allow_wrap) else 0)
|
||||
for width, allow_wrap in zip(widths, wrapable)
|
||||
]
|
||||
if not any(ratios):
|
||||
break
|
||||
|
||||
max_reduce = [min(excess_width, column_difference)] * len(widths)
|
||||
widths = ratio_reduce(excess_width, ratios, max_reduce, widths)
|
||||
|
||||
total_width = sum(widths)
|
||||
excess_width = total_width - max_width
|
||||
try:
|
||||
second_max_column = max(
|
||||
width if allow_wrap and width != max_column else 0
|
||||
for width, allow_wrap in zip(widths, wrapable)
|
||||
)
|
||||
except ValueError:
|
||||
second_max_column = 0
|
||||
column_difference = max_column - second_max_column
|
||||
ratios = [
|
||||
(1 if (width == max_column and allow_wrap) else 0)
|
||||
for width, allow_wrap in zip(widths, wrapable)
|
||||
]
|
||||
if not any(ratios) or not column_difference:
|
||||
break
|
||||
max_reduce = [min(excess_width, column_difference)] * len(widths)
|
||||
widths = ratio_reduce(excess_width, ratios, max_reduce, widths)
|
||||
|
||||
total_width = sum(widths)
|
||||
excess_width = total_width - max_width
|
||||
return widths
|
||||
|
||||
def _get_cells(self, column_index: int, column: Column) -> Iterable[_Cell]:
|
||||
|
@ -580,10 +578,9 @@ class Table(JupyterMixin):
|
|||
)
|
||||
)
|
||||
)
|
||||
safe_box = self.safe_box if self.safe_box is not None else console.safe_box
|
||||
_box = (
|
||||
box.get_safe_box(self.box, console.legacy_windows)
|
||||
if self.safe_box
|
||||
else self.box
|
||||
box.get_safe_box(self.box, console.legacy_windows) if safe_box else self.box
|
||||
)
|
||||
|
||||
# _box = self.box
|
||||
|
@ -709,7 +706,7 @@ if __name__ == "__main__": # pragma: no cover
|
|||
table = Table(
|
||||
show_lines=False,
|
||||
row_styles=["red", "green"],
|
||||
expand=True,
|
||||
expand=False,
|
||||
show_header=True,
|
||||
show_footer=False,
|
||||
show_edge=True,
|
||||
|
@ -718,26 +715,10 @@ if __name__ == "__main__": # pragma: no cover
|
|||
table.add_column("bar")
|
||||
table.add_column("baz")
|
||||
table.add_row("Magnet", "foo" * 20, "bar" * 10, "egg" * 15)
|
||||
# table.add_row(
|
||||
# "Magnet",
|
||||
# "pneumonoultramicroscopicsilicovolcanoconiosispneumonoultramicroscopicsilicovolcanoconiosispneumonoultramicroscopicsilicovolcanoconiosis",
|
||||
# "some words",
|
||||
# )
|
||||
# table.add_row(
|
||||
# "Magnet",
|
||||
# "pneumonoultramicroscopicsilicovolcanoconiosispneumonoultramicroscopicsilicovolcanoconiosispneumonoultramicroscopicsilicovolcanoconiosis",
|
||||
# "some more words",
|
||||
# )m
|
||||
# table.add_row(
|
||||
# "Magnet",
|
||||
# "pneumonoultramicroscopicsilicovolcanoconiosispneumonoultramicroscopicsilicovolcanoconiosispneumonoultramicroscopicsilicovolcanoconiosis",
|
||||
# "small words",
|
||||
# )
|
||||
# table.add_row(
|
||||
# "Magnet",
|
||||
# "pneumonoultramicroscopicsilicovolcanoconiosispneumonoultramicroscopicsilicovolcanoconiosispneumonoultramicroscopicsilicovolcanoconiosis",
|
||||
# )
|
||||
for width in range(180, 20, -5):
|
||||
c.print(table, width=width)
|
||||
|
||||
for width in range(170, 1, -1):
|
||||
print(" " * width + "<|")
|
||||
c = Console(width=width)
|
||||
c.print(table)
|
||||
|
||||
c.print("Some more words", width=4, overflow="ellipsis")
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue