mirror of
https://github.com/Textualize/rich.git
synced 2025-08-03 09:42:47 +00:00
table styles, readme
This commit is contained in:
parent
2ef55219fa
commit
9168dbdbec
9 changed files with 126 additions and 26 deletions
79
README.md
79
README.md
|
@ -1,3 +1,5 @@
|
|||
**Note:** This library is currently work in progress. Documentation and tests are in progress...
|
||||
|
||||
# Rich
|
||||
|
||||
Rich is a Python library for _rich_ text and high level formatting in the terminal.
|
||||
|
@ -81,7 +83,38 @@ Style attributes and colors may appear in any order, i.e. `"bold magenta on yell
|
|||
|
||||
## Console Logging
|
||||
|
||||
TODO
|
||||
The Console object has a `log()` method which has a similar interface to `print()`, but also renders a column for the current time and the file and line which made the call. By default, Rich will do syntax highlighting for Python structures and for repr strings. If you log a collection (i.e. a dict or a list) Rich will pretty print it so that it fits in the available space. Here's an example of some of these features.
|
||||
|
||||
```python
|
||||
from rich.console import Console
|
||||
console = Console()
|
||||
|
||||
test_data = [
|
||||
{"jsonrpc": "2.0", "method": "sum", "params": [None, 1, 2, 4, False, True], "id": "1",},
|
||||
{"jsonrpc": "2.0", "method": "notify_hello", "params": [7]},
|
||||
{"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": "2"},
|
||||
]
|
||||
|
||||
def test_log():
|
||||
enabled = False
|
||||
context = {
|
||||
"foo": "bar",
|
||||
}
|
||||
movies = ["Deadpool", "Rise of the Skywalker"]
|
||||
console.log("Hello from", console, "!")
|
||||
console.log(test_data, log_locals=True)
|
||||
|
||||
|
||||
test_log()
|
||||
```
|
||||
|
||||
The above produces the following output:
|
||||
|
||||

|
||||
|
||||
Note the `log_locals` argument, which outputs a table containing the local variables where the log method was called.
|
||||
|
||||
The log method could be used for logging to the terminal for long running applications such as servers, but is also a very nice debugging aid.
|
||||
|
||||
## Emoji
|
||||
|
||||
|
@ -94,6 +127,50 @@ To insert an emoji in to console output place the name between two colons. Here'
|
|||
|
||||
Please use this feature wisely.
|
||||
|
||||
## Tables
|
||||
|
||||
Rich can render flexible tables with unicode box characters. There is a large variety of formatting options for borders, styles, cell alignment etc. Here's a simple example:
|
||||
|
||||
```python
|
||||
from rich.console import Console
|
||||
from rich.table import Column, Table
|
||||
|
||||
console = Console()
|
||||
|
||||
table = Table(show_header=True, header_style="bold magenta")
|
||||
table.add_column("Date", style="dim", width=12)
|
||||
table.add_column("Title")
|
||||
table.add_column("Production Budget", justify="right")
|
||||
table.add_column("Box Office", justify="right")
|
||||
table.add_row(
|
||||
"Dev 20, 2019", "Star Wars: The Rise of Skywalker", "$275,000,0000", "$375,126,118"
|
||||
)
|
||||
table.add_row(
|
||||
"May 25, 2018",
|
||||
"[red]Solo[/red]: A Star Wars Story",
|
||||
"$275,000,0000",
|
||||
"$393,151,347",
|
||||
)
|
||||
table.add_row(
|
||||
"Dec 15, 2017",
|
||||
"Star Wars Ep. VIII: The Last Jedi",
|
||||
"$262,000,000",
|
||||
"[bold]$1,332,539,889[/bold]",
|
||||
)
|
||||
|
||||
console.print(table)
|
||||
```
|
||||
|
||||
This produces the following output:
|
||||
|
||||

|
||||
|
||||
Note that console markup is rendered in the same was as `print()` and `log()`. In fact, anything that is renderable by Rich may be included in the headers / rows (even other tables).
|
||||
|
||||
The `Table` class is smart enough to resize columns to fit the available width of the terminal, wrapping text as required. Here's the same example, with the terminal made smaller than the table above:
|
||||
|
||||

|
||||
|
||||
## Markdown
|
||||
|
||||
Rich can render markdown and does a reasonable job of translating the formatting to the terminal.
|
||||
|
|
BIN
imgs/log.png
Normal file
BIN
imgs/log.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 449 KiB |
BIN
imgs/table.png
Normal file
BIN
imgs/table.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 363 KiB |
BIN
imgs/table2.png
Normal file
BIN
imgs/table2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 365 KiB |
|
@ -2,6 +2,7 @@ from __future__ import annotations
|
|||
|
||||
|
||||
from collections import ChainMap
|
||||
from collections.abc import Mapping, Sequence
|
||||
from contextlib import contextmanager
|
||||
from dataclasses import dataclass, replace
|
||||
from enum import Enum
|
||||
|
@ -588,9 +589,13 @@ class Console:
|
|||
append_text(
|
||||
highlight(repr(renderable)) if highlight else repr(renderable)
|
||||
)
|
||||
else:
|
||||
elif isinstance(renderable, (Mapping, Sequence)):
|
||||
check_text()
|
||||
append(Pretty(renderable))
|
||||
else:
|
||||
append_text(
|
||||
highlight(repr(renderable)) if highlight else repr(renderable)
|
||||
)
|
||||
|
||||
check_text()
|
||||
return renderables
|
||||
|
|
|
@ -56,6 +56,9 @@ DEFAULT_STYLES: Dict[str, Style] = {
|
|||
"repr.url": Style(underline=True, color="default"),
|
||||
"inspect.key": Style(italic=True),
|
||||
"inspect.value": Style(),
|
||||
"table.header": Style(bold=True),
|
||||
"table.footer": Style(bold=True),
|
||||
"table.cell": Style(),
|
||||
}
|
||||
|
||||
MARKDOWN_STYLES = {
|
||||
|
|
|
@ -3,9 +3,10 @@ from __future__ import annotations
|
|||
from collections import defaultdict
|
||||
from operator import itemgetter
|
||||
import re
|
||||
from typing import Dict, Iterable, List, Optional, Tuple
|
||||
from typing import Dict, Iterable, List, Optional, Tuple, Union
|
||||
|
||||
from .errors import MarkupError
|
||||
from .style import Style
|
||||
from .text import Span, Text
|
||||
|
||||
|
||||
|
@ -36,7 +37,7 @@ def _parse(markup: str) -> Iterable[Tuple[Optional[str], Optional[str]]]:
|
|||
yield markup[position:], None
|
||||
|
||||
|
||||
def render(markup: str) -> Text:
|
||||
def render(markup: str, style: Union[str, Style] = "") -> Text:
|
||||
"""Render console markup in to a Text instance.
|
||||
|
||||
Args:
|
||||
|
@ -48,7 +49,7 @@ def render(markup: str) -> Text:
|
|||
Returns:
|
||||
Text: A test instance.
|
||||
"""
|
||||
text = Text()
|
||||
text = Text(style=style)
|
||||
stylize = text.stylize
|
||||
|
||||
styles: Dict[str, List[int]] = defaultdict(list)
|
||||
|
|
|
@ -9,6 +9,7 @@ from typing import (
|
|||
Optional,
|
||||
Sequence,
|
||||
Tuple,
|
||||
TypeVar,
|
||||
TYPE_CHECKING,
|
||||
Union,
|
||||
)
|
||||
|
@ -34,14 +35,25 @@ from ._render_width import RenderWidth
|
|||
from ._tools import iter_first_last, iter_first, iter_last, ratio_divide
|
||||
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
def _pick_first(values: Iterable[Optional[T]], final: T) -> T:
|
||||
"""Pick first non-None value."""
|
||||
for value in values:
|
||||
if value is not None:
|
||||
return value
|
||||
return final
|
||||
|
||||
|
||||
@dataclass
|
||||
class Column:
|
||||
"""Defines a column in a table."""
|
||||
|
||||
header: Union[str, ConsoleRenderable] = ""
|
||||
footer: Union[str, ConsoleRenderable] = ""
|
||||
header_style: Union[str, Style] = "none"
|
||||
footer_style: Union[str, Style] = "none"
|
||||
header_style: Union[str, Style] = "table.header"
|
||||
footer_style: Union[str, Style] = "table.footer"
|
||||
style: Union[str, Style] = "none"
|
||||
justify: JustifyValues = "left"
|
||||
width: Optional[int] = None
|
||||
|
@ -61,7 +73,7 @@ class Column:
|
|||
@property
|
||||
def header_renderable(self) -> ConsoleRenderable:
|
||||
return (
|
||||
Text(self.header, style=self.header_style)
|
||||
Text.from_markup(self.header, style=self.header_style or "")
|
||||
if isinstance(self.header, str)
|
||||
else self.header
|
||||
)
|
||||
|
@ -69,7 +81,7 @@ class Column:
|
|||
@property
|
||||
def footer_renderable(self) -> ConsoleRenderable:
|
||||
return (
|
||||
Text(self.footer, style=self.footer_style)
|
||||
Text.from_markup(self.footer, style=self.footer_style or "")
|
||||
if isinstance(self.footer, str)
|
||||
else self.footer
|
||||
)
|
||||
|
@ -90,7 +102,7 @@ class Table:
|
|||
title: str = None,
|
||||
footer: str = None,
|
||||
width: int = None,
|
||||
box: Optional[box.Box] = box.MINIMAL_DOUBLE_HEAD,
|
||||
box: Optional[box.Box] = box.DOUBLE_EDGE,
|
||||
padding: PaddingDimensions = (0, 1),
|
||||
pad_edge: bool = True,
|
||||
expand: bool = False,
|
||||
|
@ -98,9 +110,10 @@ class Table:
|
|||
show_footer: bool = False,
|
||||
show_edge: bool = True,
|
||||
style: Union[str, Style] = "none",
|
||||
header_style: Union[str, Style] = "bold",
|
||||
border_style: Union[str, Style] = "",
|
||||
title_style: Union[str, Style] = "italic blue",
|
||||
header_style: Union[str, Style] = None,
|
||||
footer_style: Union[str, Style] = None,
|
||||
border_style: Union[str, Style] = None,
|
||||
title_style: Union[str, Style] = None,
|
||||
) -> None:
|
||||
self.columns = [
|
||||
(Column(header) if isinstance(header, str) else header)
|
||||
|
@ -112,11 +125,12 @@ class Table:
|
|||
self._padding = Padding.unpack(padding)
|
||||
self.pad_edge = pad_edge
|
||||
self.expand = expand
|
||||
self.show_header = show_header and headers
|
||||
self.show_header = show_header
|
||||
self.show_footer = show_footer
|
||||
self.show_edge = show_edge
|
||||
self.style = style
|
||||
self.header_style = header_style
|
||||
self.footer_style = footer_style
|
||||
self.border_style = border_style
|
||||
self.title_style = title_style
|
||||
self._row_count = 0
|
||||
|
@ -134,9 +148,9 @@ class Table:
|
|||
self,
|
||||
header: Union[str, ConsoleRenderable] = "",
|
||||
footer: Union[str, ConsoleRenderable] = "",
|
||||
header_style: Union[str, Style] = "none",
|
||||
footer_style: Union[str, Style] = "none",
|
||||
style: Union[str, Style] = "none",
|
||||
header_style: Union[str, Style] = None,
|
||||
footer_style: Union[str, Style] = None,
|
||||
style: Union[str, Style] = None,
|
||||
justify: JustifyValues = "left",
|
||||
width: int = None,
|
||||
ratio: int = None,
|
||||
|
@ -158,9 +172,9 @@ class Table:
|
|||
column = Column(
|
||||
header=header,
|
||||
footer=footer,
|
||||
header_style=header_style,
|
||||
footer_style=footer_style,
|
||||
style=style,
|
||||
header_style=_pick_first((header_style, self.header_style), "table.header"),
|
||||
footer_style=_pick_first((footer_style, self.footer_style), "table.footer"),
|
||||
style=_pick_first((style, self.style), "table.cell"),
|
||||
justify=justify,
|
||||
width=width,
|
||||
ratio=ratio,
|
||||
|
@ -196,7 +210,7 @@ class Table:
|
|||
elif renderable is None:
|
||||
add_cell(column, Text(""))
|
||||
elif isinstance(renderable, str):
|
||||
add_cell(column, Text(renderable))
|
||||
add_cell(column, Text.from_markup(renderable))
|
||||
else:
|
||||
raise errors.NotRenderableError(
|
||||
f"unable to render {renderable!r}; str or object with a __console__ method is required"
|
||||
|
@ -216,7 +230,7 @@ class Table:
|
|||
widths = self._calculate_column_widths(max_width)
|
||||
table_width = sum(widths) + len(self.columns) + (2 if self.box else 0)
|
||||
if self.title:
|
||||
title_text = Text(self.title, self.title_style)
|
||||
title_text = Text.from_markup(self.title, style=self.title_style or "")
|
||||
wrapped_title = title_text.wrap(table_width, "center")
|
||||
yield wrapped_title
|
||||
yield from self._render(console, options, widths)
|
||||
|
@ -325,9 +339,9 @@ class Table:
|
|||
def _render(
|
||||
self, console: Console, options: ConsoleOptions, widths: List[int]
|
||||
) -> RenderResult:
|
||||
table_style = console.get_style(self.style)
|
||||
table_style = console.get_style(self.style or "")
|
||||
|
||||
border_style = table_style + console.get_style(self.border_style)
|
||||
border_style = table_style + console.get_style(self.border_style or "")
|
||||
rows: Iterable[Tuple[_Cell, ...]] = zip(
|
||||
*(
|
||||
self._get_cells(column_index, column)
|
||||
|
|
|
@ -150,7 +150,7 @@ class Text:
|
|||
return NotImplemented
|
||||
|
||||
@classmethod
|
||||
def from_markup(cls, text: str) -> Text:
|
||||
def from_markup(cls, text: str, style: Union[str, Style] = "") -> Text:
|
||||
"""Create Text instance from markup.
|
||||
|
||||
Args:
|
||||
|
@ -161,7 +161,7 @@ class Text:
|
|||
"""
|
||||
from .markup import render
|
||||
|
||||
return render(text)
|
||||
return render(text, style)
|
||||
|
||||
@property
|
||||
def text(self) -> str:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue