from dataclasses import dataclass, field, replace from typing import (
TYPE_CHECKING,
Dict,
Iterable,
List,
NamedTuple,
Optional,
Sequence,
Tuple,
Union,
)
from . import box, errors from ._loop import loop_first_last, loop_last from ._pick import pick_bool from ._ratio import ratio_distribute, ratio_reduce from .align import VerticalAlignMethod from .jupyter import JupyterMixin from .measure import Measurement from .padding import Padding, PaddingDimensions from .protocol import is_renderable from .segment import Segment from .style import Style, StyleType from .text import Text, TextType
if TYPE_CHECKING: from .console import (
Console,
ConsoleOptions,
JustifyMethod,
OverflowMethod,
RenderableType,
RenderResult,
)
@dataclass class Column: """Defines a column within a ~Table.
Args:
title (Union[str, Text], optional): The title of the table rendered at the top. Defaults to None.
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.
min_width (Optional[int], optional): The minimum width of the table, or ``None`` for no minimum. Defaults to None.
box (box.Box, optional): One of the constants in box.py used to draw the edges (see :ref:`appendix_box`), or ``None`` for no box lines. Defaults to box.HEAVY_HEAD.
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.
expand (bool, optional): Expand the table to fit the available space if ``True``, otherwise the table width will be auto-calculated. Defaults to False.
show_header (bool, optional): Show a header row. Defaults to True.
show_footer (bool, optional): Show a footer row. Defaults to False.
show_edge (bool, optional): Draw a box around the outside of the table. Defaults to True.
show_lines (bool, optional): Draw lines between every row. Defaults to False.
leading (int, optional): Number of blank lines between rows (precludes ``show_lines``). Defaults to 0.
style (Union[str, Style], optional): Default style for the table. Defaults to "none".
row_styles (List[Union, str], optional): Optional list of row styles, if more than one style is given then the styles will alternate. Defaults to None.
header_style (Union[str, Style], optional): Style of the header. Defaults to "table.header".
footer_style (Union[str, Style], optional): Style of the footer. Defaults to "table.footer".
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.
title_justify (str, optional): Justify method for title. Defaults to "center".
caption_justify (str, optional): Justify method for caption. Defaults to "center".
highlight (bool, optional): Highlight cell contents (if str). Defaults to False. """
header: "RenderableType" = "" """RenderableType: Renderable for the header (typically a string)"""
footer: "RenderableType" = "" """RenderableType: Renderable for the footer (typically a string)"""
header_style: StyleType = "" """StyleType: The style of the header."""
footer_style: StyleType = "" """StyleType: The style of the footer."""
style: StyleType = "" """StyleType: The style of the column."""
justify: "JustifyMethod" = "left" """str: How to justify text within the column ("left", "center", "right", or "full")"""
vertical: "VerticalAlignMethod" = "top" """str: How to vertically align content ("top", "middle", or "bottom")"""
def copy(self) -> "Column": """Return a copy of this Column.""" return replace(self, _cells=[])
@property def cells(self) -> Iterable["RenderableType"]: """Get all cells in the column, not including header.""" yieldfrom self._cells
@property def flexible(self) -> bool: """Check if this column is flexible.""" return self.ratio isnotNone
@dataclass class Row: """Information regarding a row."""
style: Optional[StyleType] = None """Style to apply to row."""
end_section: bool = False """Indicated end of section, which will force a line beneath the row."""
class _Cell(NamedTuple): """A single cell in a table."""
style: StyleType """Style to apply to cell."""
renderable: "RenderableType" """Cell renderable."""
vertical: VerticalAlignMethod """Cell vertical alignment."""
class Table(JupyterMixin): """A console renderable to draw a table.
Args:
*headers (Union[Column, str]): Column headers, either as a string, or :class:`~rich.table.Column` instance.
title (Union[str, Text], optional): The title of the table rendered at the top. Defaults to None.
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.
min_width (Optional[int], optional): The minimum width of the table, or ``None`` for no minimum. Defaults to None.
box (box.Box, optional): One of the constants in box.py used to draw the edges (see :ref:`appendix_box`), or ``None`` for no box lines. Defaults to box.HEAVY_HEAD.
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.
expand (bool, optional): Expand the table to fit the available space if ``True``, otherwise the table width will be auto-calculated. Defaults to False.
show_header (bool, optional): Show a header row. Defaults to True.
show_footer (bool, optional): Show a footer row. Defaults to False.
show_edge (bool, optional): Draw a box around the outside of the table. Defaults to True.
show_lines (bool, optional): Draw lines between every row. Defaults to False.
leading (int, optional): Number of blank lines between rows (precludes ``show_lines``). Defaults to 0.
style (Union[str, Style], optional): Default style for the table. Defaults to "none".
row_styles (List[Union, str], optional): Optional list of row styles, if more than one style is given then the styles will alternate. Defaults to None.
header_style (Union[str, Style], optional): Style of the header. Defaults to "table.header".
footer_style (Union[str, Style], optional): Style of the footer. Defaults to "table.footer".
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.
title_justify (str, optional): Justify method for title. Defaults to "center".
caption_justify (str, optional): Justify method for caption. Defaults to "center".
highlight (bool, optional): Highlight cell contents (if str). Defaults to False. """
@classmethod def grid(
cls,
*headers: Union[Column, str],
padding: PaddingDimensions = 0,
collapse_padding: bool = True,
pad_edge: bool = False,
expand: bool = False,
) -> "Table": """Get a table with no lines, headers, or footer.
Args:
*headers (Union[Column, str]): Column headers, either as a string, or :class:`~rich.table.Column` instance.
padding (PaddingDimensions, optional): Get padding around cells. Defaults to 0.
collapse_padding (bool, optional): Enable collapsing of padding around cells. Defaults to True.
pad_edge (bool, optional): Enable padding around edges of table. Defaults to False.
expand (bool, optional): Expand the table to fit the available space if ``True``, otherwise the table width will be auto-calculated. Defaults to False.
Args:
header (RenderableType, optional): Text or renderable for the header.
Defaults to "".
footer (RenderableType, optional): Text or renderable for the footer.
Defaults to "".
header_style (Union[str, Style], optional): Style for the header, orNonefor default. Defaults to None.
highlight (bool, optional): Whether to highlight the text. The default of None uses the value of the table (self) object.
footer_style (Union[str, Style], optional): Style for the footer, orNonefor default. Defaults to None.
style (Union[str, Style], optional): Style for the column cells, orNonefor default. Defaults to None.
justify (JustifyMethod, optional): Alignment for cells. Defaults to "left".
vertical (VerticalAlignMethod, optional): Vertical alignment, one of "top", "middle", or"bottom". Defaults to "top".
overflow (OverflowMethod): Overflow method: "crop", "fold", "ellipsis". Defaults to "ellipsis".
width (int, optional): Desired width of column in characters, orNone to fit to contents. Defaults to None.
min_width (Optional[int], optional): Minimum width of column, or ``None`` for no minimum. Defaults to None.
max_width (Optional[int], optional): Maximum width of column, or ``None`` for no maximum. Defaults to None.
ratio (int, optional): Flexible ratio for the column (requires ``Table.expand`` or ``Table.width``). Defaults to None.
no_wrap (bool, optional): Set to ``True`` to disable wrapping of this column. """
Args:
*renderables (Noneor renderable): Each cell in a row must be a renderable object (including str), or ``None`` for a blank cell.
style (StyleType, optional): An optional style to apply to the entire row. Defaults to None.
end_section (bool, optional): End a section and draw a line. Defaults to False.
Raises:
errors.NotRenderableError: If you add something that can't be rendered. """
def _calculate_column_widths(
self, console: "Console", options: "ConsoleOptions"
) -> List[int]: """Calculate the widths of each column, including padding, not including borders."""
max_width = options.max_width
columns = self.columns
width_ranges = [
self._measure_column(console, options, column) for column in columns
]
widths = [_range.maximum or 1 for _range in width_ranges]
get_padding_width = self._get_padding_width
extra_width = self._extra_width if self.expand:
ratios = [col.ratio or 0 for col in columns if col.flexible] if any(ratios):
fixed_widths = [
0 if column.flexible else _range.maximum for _range, column in zip(width_ranges, columns)
]
flex_minimum = [
(column.width or 1) + get_padding_width(column._index) for column in columns if column.flexible
]
flexible_width = max_width - sum(fixed_widths)
flex_widths = ratio_distribute(flexible_width, ratios, flex_minimum)
iter_flex_widths = iter(flex_widths) for index, column in enumerate(columns): if column.flexible:
widths[index] = fixed_widths[index] + next(iter_flex_widths)
table_width = sum(widths)
if table_width > max_width:
widths = self._collapse_widths(
widths,
[(column.width isNoneandnot 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, [1] * len(widths), widths, widths)
table_width = sum(widths)
width_ranges = [
self._measure_column(console, options.update_width(width), column) for width, column in zip(widths, columns)
]
widths = [_range.maximum or 0 for _range in width_ranges]
if (table_width < max_width and self.expand) or (
self.min_width isnotNoneand table_width < (self.min_width - extra_width)
):
_max_width = (
max_width if self.min_width isNone else min(self.min_width - extra_width, max_width)
)
pad_widths = ratio_distribute(_max_width - table_width, widths)
widths = [_width + pad for _width, pad in zip(widths, pad_widths)]
return widths
@classmethod def _collapse_widths(
cls, widths: List[int], wrapable: List[bool], max_width: int
) -> List[int]: """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 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
)
second_max_column = max(
width if allow_wrap and width != max_column else 0 for width, allow_wrap in zip(widths, wrapable)
)
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)
] ifnot any(ratios) ornot column_difference: break
max_reduce = [min(excess_width, column_difference)] * len(widths)
widths = ratio_reduce(excess_width, ratios, max_reduce, widths)
if collapse_padding: ifnot first_column:
left = max(0, left - right) ifnot last_row:
bottom = max(0, top - bottom)
ifnot pad_edge: if first_column:
left = 0 if last_column:
right = 0 if first_row:
top = 0 if last_row:
bottom = 0
_padding = (top, right, bottom, left)
_padding_cache[(first_row, last_row)] = _padding return _padding
if any_padding:
_Padding = Padding for first, last, (style, renderable) in loop_first_last(raw_cells): yield _Cell(
style,
_Padding(renderable, get_padding(first, last)),
getattr(renderable, "vertical", None) or column.vertical,
) else: for style, renderable in raw_cells: yield _Cell(
style,
renderable,
getattr(renderable, "vertical", None) or column.vertical,
)
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:
pad_left = max(0, pad_left - pad_right) return pad_left + pad_right
def _measure_column(
self,
console: "Console",
options: "ConsoleOptions",
column: Column,
) -> Measurement: """Get the minimum and maximum width of the column."""
max_width = options.max_width if max_width < 1: return Measurement(0, 0)
if _box: if last and show_footer: yield _Segment(
_box.get_row(widths, "foot", edge=show_edge), border_style
) yield new_line
left, right, _divider = box_segments[0 if first else (2 if last else 1)]
# If the column divider is whitespace also style it with the row background
divider = (
_divider if _divider.text.strip() else _Segment(
_divider.text, row_style.background_style + _divider.style
)
) for line_no in range(max_height): if show_edge: yield left for last_cell, rendered_cell in loop_last(cells): yieldfrom rendered_cell[line_no] ifnot last_cell: yield divider if show_edge: yield right yield new_line else: for line_no in range(max_height): for rendered_cell in cells: yieldfrom rendered_cell[line_no] yield new_line if _box and first and show_header: yield _Segment(
_box.get_row(widths, "head", edge=show_edge), border_style
) yield new_line
end_section = row and row.end_section if _box and (show_lines or leading or end_section): if ( not last andnot (show_footer and index >= len(row_cells) - 2) andnot (show_header and header_row)
): if leading: yield _Segment(
_box.get_row(widths, "mid", edge=show_edge) * leading,
border_style,
) else: yield _Segment(
_box.get_row(widths, "row", edge=show_edge), border_style
) yield new_line
if _box and show_edge: yield _Segment(_box.get_bottom(widths), border_style) yield new_line
if __name__ == "__main__": # pragma: no cover from rich.console import Console from rich.highlighter import ReprHighlighter from rich.table import Table as Table
from ._timer import timer
with timer("Table render"):
table = Table(
title="Star Wars Movies",
caption="Rich example table",
caption_justify="right",
)
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.