table shrinking

This commit is contained in:
Will McGugan 2020-07-03 17:17:37 +01:00
parent 05db129a6f
commit 181973311b
11 changed files with 102 additions and 112 deletions

View File

@ -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

View File

@ -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):

View File

@ -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"

View File

@ -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",

View File

@ -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()

View File

@ -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:

View File

@ -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)

View File

@ -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)

View File

@ -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