Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Table vertical align #1816

Merged
merged 8 commits into from Jan 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 5 additions & 2 deletions CHANGELOG.md
Expand Up @@ -5,13 +5,16 @@ 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).


## [11.0.0] - Unreleased

### Added

- Fixed issue with pretty repr in jupyter notebook https://github.com/Textualize/rich/issues/1717
- Added max_depth arg to pretty printing
- Added `vertical_align` to Table.add_row

### Fixed

- Fixed issue with pretty repr in jupyter notebook https://github.com/Textualize/rich/issues/1717
- Fix Traceback theme defaults override user supplied styles https://github.com/Textualize/rich/issues/1786

## [10.16.2] - 2021-01-02
Expand Down
72 changes: 68 additions & 4 deletions docs/source/tables.rst
Expand Up @@ -40,7 +40,44 @@ This produces the following output:
</pre>


Rich is quite smart about rendering the table. It will adjust the column widths to fit the contents and will wrap text if it doesn't fit. You can also add anything that Rich knows how to render as a title or row cell (even another table)!
Rich will calculate the optimal column sizes to fit your content, and will wrap text to fit if the terminal is not wide enough to fit the contents.

.. note::
You are not limited to adding text in the ``add_row`` method. You can add anything that Rich knows how to render (including another table).

Table Options
~~~~~~~~~~~~~

There are a number of keyword arguments on the Table constructor you can use to define how a table should look.

- ``title`` Sets the title of the table (text show above the table).
- ``caption`` Sets the table caption (text show below the table).
- ``width`` Sets the desired width of the table (disables automatic width calculation).
- ``min_width`` Sets a minimum width for the table.
- ``box`` Sets one of the :ref:`appendix_box` styles for the table grid, or ``None`` for no grid.
- ``safe_box`` Set to ``True`` to force the table to generate ASCII characters rather than unicode.
- ``padding`` A integer, or tuple of 1, 2, or 4 values to set the padding on cells.
- ``collapse_padding`` If True the padding of neighboring cells will be merged.
- ``pad_edge`` Set to False to remove padding around the edge of the table.
- ``expand`` Set to True to expand the table to the full available size.
- ``show_header`` Set to True to show a header, False to disable it.
- ``show_footer`` Set to True to show a footer, False to disable it.
- ``show edge`` Set to False to disable the edge line around the table.
- ``show_lines`` Set to True to show lines between rows as well as header / footer.
- ``leading`` Additional space between rows.
- ``style`` A Style to apply to the entire table, e.g. "on blue"
- ``row_styles`` Set to a list of styles to style alternating rows. e.g. ``["dim", ""]`` to create *zebra stripes*
- ``header_style`` Set the default style for the header.
- ``footer_style`` Set the default style for the footer.
- ``border style`` Set a style for border characters.
- ``title_style`` Set a style for the title.
- ``caption_style`` Set a style for the caption.
- ``title_justify`` Set the title justify method ("left", "right", "center", or "full")
- ``caption_justify`` Set the caption justify method ("left", "right", "center", or "full")
- ``highlight`` Set to True to enable automatic highlighting of cell contents.

Border Styles
~~~~~~~~~~~~~

You can set the border style by importing one of the preset :class:`~rich.box.Box` objects and setting the ``box`` argument in the table constructor. Here's an example that modifies the look of the Star Wars table::

Expand All @@ -49,9 +86,17 @@ You can set the border style by importing one of the preset :class:`~rich.box.Bo

See :ref:`appendix_box` for other box styles.

You can also set ``box=None`` to remove borders entirely.

The :class:`~rich.table.Table` class offers a number of configuration options to set the look and feel of the table, including how borders are rendered and the style and alignment of the columns.


Lines
~~~~~

By default, Tables will show a line under the header only. If you want to show lines between all rows add ``show_lines=True`` to the constructor.


Empty Tables
~~~~~~~~~~~~

Expand Down Expand Up @@ -80,10 +125,29 @@ This allows you to specify the text of the column only. If you want to set other
title="Star Wars Movies"
)

Lines
~~~~~
Column Options
~~~~~~~~~~~~~~

By default, Tables will show a line under the header only. If you want to show lines between all rows add ``show_lines=True`` to the constructor.
There are a number of options you can set on a column to modify how it will look.

- ``header_style`` Sets the style of the header, e.g. "bold magenta".
- ``footer_style`` Sets the style of the footer.
- ``style`` Sets a style that applies to the column. You could use this to highlight a column by setting the background with "on green" for example.
- ``justify`` Sets the text justify to one of "left", "center", "right", or "full".
- ``vertical`` Sets the vertical alignment of the cells in a column, to one of "top", "middle", or "bottom".
- ``width`` Explicitly set the width of a row to a given number of characters (disables automatic calculation).
- ``min_width`` When set to an integer will prevent the column from shrinking below this amount.
- ``max_width`` When set to an integer will prevent the column from growing beyond this amount.
- ``ratio`` Defines a ratio to set the column width. For instance, if there are 3 columns with a total of 6 ratio, and ``ratio=2`` then the column will be a third of the available size.
- ``no_wrap`` Set to False to prevent this column from wrapping.

Vertical Alignment
~~~~~~~~~~~~~~~~~~

You can define the vertical alignment of a column by setting the ``vertical`` parameter of the column. You can also do this per-cell by wrapping your text or renderable with a :class:`~rich.align.Align` class::


table.add_row(Align("Title", vertical="middle"))

Grids
~~~~~
Expand Down
21 changes: 6 additions & 15 deletions rich/__main__.py
Expand Up @@ -4,13 +4,7 @@

from rich import box
from rich.color import Color
from rich.console import (
Console,
ConsoleOptions,
Group,
RenderResult,
RenderableType,
)
from rich.console import Console, ConsoleOptions, Group, RenderableType, RenderResult
from rich.markdown import Markdown
from rich.measure import Measurement
from rich.pretty import Pretty
Expand Down Expand Up @@ -247,13 +241,10 @@ def iter_last(values: Iterable[T]) -> Iterable[Tuple[bool, T]]:
sponsor_message = Table.grid(padding=1)
sponsor_message.add_column(style="green", justify="right")
sponsor_message.add_column(no_wrap=True)

sponsor_message.add_row(
"Sponsor me",
"[u blue link=https://github.com/sponsors/willmcgugan]https://github.com/sponsors/willmcgugan",
)
sponsor_message.add_row(
"Buy me a :coffee:",
"[u blue link=https://ko-fi.com/willmcgugan]https://ko-fi.com/willmcgugan",
"Buy devs a :coffee:",
"[u blue link=https://ko-fi.com/textualize]https://ko-fi.com/textualize",
)
sponsor_message.add_row(
"Twitter",
Expand All @@ -265,9 +256,9 @@ def iter_last(values: Iterable[T]) -> Iterable[Tuple[bool, T]]:

intro_message = Text.from_markup(
"""\
It takes a lot of time to develop Rich and to provide support.
We hope you enjoy using Rich!

Consider supporting my work via Github Sponsors (ask your company / organization), or buy me a coffee to say thanks.
Rich is maintained with :heart: by [link=https://www.textualize.io]Textualize.io[/]

- Will McGugan"""
)
Expand Down
120 changes: 102 additions & 18 deletions rich/segment.py
Expand Up @@ -12,6 +12,7 @@
Optional,
Sequence,
Tuple,
Type,
Union,
)

Expand Down Expand Up @@ -384,33 +385,116 @@ def set_shape(
lines (List[List[Segment]]): A list of lines.
width (int): Desired width.
height (int, optional): Desired height or None for no change.
style (Style, optional): Style of any padding added. Defaults to None.
style (Style, optional): Style of any padding added.
new_lines (bool, optional): Padded lines should include "\n". Defaults to False.

Returns:
List[List[Segment]]: New list of lines that fits width x height.
List[List[Segment]]: New list of lines.
"""
if height is None:
height = len(lines)
shaped_lines: List[List[Segment]] = []
pad_line = (
[Segment(" " * width, style), Segment("\n")]
if new_lines
else [Segment(" " * width, style)]
_height = height or len(lines)

blank = (
[cls(" " * width + "\n", style)] if new_lines else [cls(" " * width, style)]
)

append = shaped_lines.append
adjust_line_length = cls.adjust_line_length
line: Optional[List[Segment]]
iter_lines = iter(lines)
for _ in range(height):
line = next(iter_lines, None)
if line is None:
append(pad_line)
else:
append(adjust_line_length(line, width, style=style))
shaped_lines = lines[:_height]
shaped_lines[:] = [
adjust_line_length(line, width, style=style) for line in lines
]
if len(shaped_lines) < _height:
shaped_lines.extend([blank] * (_height - len(shaped_lines)))
return shaped_lines

@classmethod
def align_top(
cls: Type["Segment"],
lines: List[List["Segment"]],
width: int,
height: int,
style: Style,
new_lines: bool = False,
) -> List[List["Segment"]]:
"""Aligns lines to top (adds extra lines to bottom as required).

Args:
lines (List[List[Segment]]): A list of lines.
width (int): Desired width.
height (int, optional): Desired height or None for no change.
style (Style): Style of any padding added.
new_lines (bool, optional): Padded lines should include "\n". Defaults to False.

Returns:
List[List[Segment]]: New list of lines.
"""
extra_lines = height - len(lines)
if not extra_lines:
return lines[:]
lines = lines[:height]
blank = cls(" " * width + "\n", style) if new_lines else cls(" " * width, style)
lines = lines + [[blank]] * extra_lines
return lines

@classmethod
def align_bottom(
cls: Type["Segment"],
lines: List[List["Segment"]],
width: int,
height: int,
style: Style,
new_lines: bool = False,
) -> List[List["Segment"]]:
"""Aligns render to bottom (adds extra lines above as required).

Args:
lines (List[List[Segment]]): A list of lines.
width (int): Desired width.
height (int, optional): Desired height or None for no change.
style (Style): Style of any padding added. Defaults to None.
new_lines (bool, optional): Padded lines should include "\n". Defaults to False.

Returns:
List[List[Segment]]: New list of lines.
"""
extra_lines = height - len(lines)
if not extra_lines:
return lines[:]
lines = lines[:height]
blank = cls(" " * width + "\n", style) if new_lines else cls(" " * width, style)
lines = [[blank]] * extra_lines + lines
return lines

@classmethod
def align_middle(
cls: Type["Segment"],
lines: List[List["Segment"]],
width: int,
height: int,
style: Style,
new_lines: bool = False,
) -> List[List["Segment"]]:
"""Aligns lines to middle (adds extra lines to above and below as required).

Args:
lines (List[List[Segment]]): A list of lines.
width (int): Desired width.
height (int, optional): Desired height or None for no change.
style (Style): Style of any padding added.
new_lines (bool, optional): Padded lines should include "\n". Defaults to False.

Returns:
List[List[Segment]]: New list of lines.
"""
extra_lines = height - len(lines)
if not extra_lines:
return lines[:]
lines = lines[:height]
blank = cls(" " * width + "\n", style) if new_lines else cls(" " * width, style)
top_lines = extra_lines // 2
bottom_lines = extra_lines - top_lines
lines = [[blank]] * top_lines + lines + [[blank]] * bottom_lines
return lines

@classmethod
def simplify(cls, segments: Iterable["Segment"]) -> Iterable["Segment"]:
"""Simplify an iterable of segments by combining contiguous segments with the same style.
Expand Down