mirror of
https://github.com/Textualize/rich.git
synced 2025-08-04 18:18:22 +00:00
docs
This commit is contained in:
parent
0a5315a211
commit
9a29b36514
5 changed files with 173 additions and 66 deletions
|
@ -1,7 +1,7 @@
|
|||
Layout
|
||||
======
|
||||
|
||||
Rich offers a :class:`~rich.layout.Layout` class which can be used to divide the screen area in to parts, where each part can contain its own content. It is typically used with :ref:`Live` to create full-screen "applications", but may be used standalone.
|
||||
Rich offers a :class:`~rich.layout.Layout` class which can be used to divide the screen area in to parts, where each part may contain independent content. It is often used with :ref:`Live` to create full-screen "applications" but may be used standalone.
|
||||
|
||||
To see an example of a Layout, run the following from the command line::
|
||||
|
||||
|
@ -18,7 +18,7 @@ To define a layout, construct a Layout object and print it::
|
|||
layout = Layout()
|
||||
print(layout)
|
||||
|
||||
This will draw a box the size of the terminal with some information regarding the layout. The box is a "placeholder" because we have yet to add any content to it. Before we do that, we can call the :meth:`~rich.layout.Layout.split` method to divide the layout in to two sub-layouts::
|
||||
This will draw a box the size of the terminal with some information regarding the layout. The box is a "placeholder" because we have yet to add any content to it. Before we do that, let's create a more interesting layout by calling the :meth:`~rich.layout.Layout.split` method to divide the layout in to two sub-layouts::
|
||||
|
||||
layout.split(
|
||||
Layout(name="upper"),
|
||||
|
@ -37,12 +37,52 @@ This will divide the terminal screen in to two equal sized portions, one on top
|
|||
|
||||
The addition of the ``direction="horizontal"`` tells the Layout class to split left-to-right, rather than the default of top-to-bottom.
|
||||
|
||||
You should now see the screen area divided in to 3 portions; an upper half and a lower half that is split in to two quarters. You can continue to call split() in this way to create as many parts to the screen as you wish.
|
||||
You should now see the screen area divided in to 3 portions; an upper half and a lower half that is split in to two quarters.
|
||||
|
||||
Updating renderables
|
||||
--------------------
|
||||
.. raw:: html
|
||||
|
||||
The Layout class would not be that useful if it only displayed placeholders. Fortunately we can tell the layout (or sub-layout) to display text or any other renderable in it's area of the screen by calling :meth:`~rich.layout.Layout.update`. Here is an example::
|
||||
<pre style="font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="color: #000080">╭─────────────────────────────── </span><span style="color: #008000">'upper'</span><span style="color: #000080"> </span><span style="color: #000080; font-weight: bold">(</span><span style="color: #000080; font-weight: bold">84</span><span style="color: #000080"> x </span><span style="color: #000080; font-weight: bold">13</span><span style="color: #000080; font-weight: bold">)</span><span style="color: #000080"> ────────────────────────────────╮</span>
|
||||
<span style="color: #000080">│</span> <span style="color: #000080">│</span>
|
||||
<span style="color: #000080">│</span> <span style="color: #000080">│</span>
|
||||
<span style="color: #000080">│</span> <span style="color: #000080">│</span>
|
||||
<span style="color: #000080">│</span> <span style="color: #000080">│</span>
|
||||
<span style="color: #000080">│</span> <span style="color: #000080">│</span>
|
||||
<span style="color: #000080">│</span> <span style="font-weight: bold">{</span><span style="color: #008000">'size'</span>: <span style="color: #800080; font-style: italic">None</span>, <span style="color: #008000">'minimum_size'</span>: <span style="color: #000080; font-weight: bold">1</span>, <span style="color: #008000">'ratio'</span>: <span style="color: #000080; font-weight: bold">1</span>, <span style="color: #008000">'name'</span>: <span style="color: #008000">'upper'</span><span style="font-weight: bold">}</span> <span style="color: #000080">│</span>
|
||||
<span style="color: #000080">│</span> <span style="color: #000080">│</span>
|
||||
<span style="color: #000080">│</span> <span style="color: #000080">│</span>
|
||||
<span style="color: #000080">│</span> <span style="color: #000080">│</span>
|
||||
<span style="color: #000080">│</span> <span style="color: #000080">│</span>
|
||||
<span style="color: #000080">│</span> <span style="color: #000080">│</span>
|
||||
<span style="color: #000080">╰──────────────────────────────────────────────────────────────────────────────────╯</span>
|
||||
<span style="color: #000080">╭─────────── </span><span style="color: #008000">'left'</span><span style="color: #000080"> </span><span style="color: #000080; font-weight: bold">(</span><span style="color: #000080; font-weight: bold">42</span><span style="color: #000080"> x </span><span style="color: #000080; font-weight: bold">14</span><span style="color: #000080; font-weight: bold">)</span><span style="color: #000080"> ───────────╮╭────────── </span><span style="color: #008000">'right'</span><span style="color: #000080"> </span><span style="color: #000080; font-weight: bold">(</span><span style="color: #000080; font-weight: bold">42</span><span style="color: #000080"> x </span><span style="color: #000080; font-weight: bold">14</span><span style="color: #000080; font-weight: bold">)</span><span style="color: #000080"> ───────────╮</span>
|
||||
<span style="color: #000080">│</span> <span style="color: #000080">││</span> <span style="color: #000080">│</span>
|
||||
<span style="color: #000080">│</span> <span style="color: #000080">││</span> <span style="color: #000080">│</span>
|
||||
<span style="color: #000080">│</span> <span style="color: #000080">││</span> <span style="color: #000080">│</span>
|
||||
<span style="color: #000080">│</span> <span style="font-weight: bold">{</span> <span style="color: #000080">││</span> <span style="font-weight: bold">{</span> <span style="color: #000080">│</span>
|
||||
<span style="color: #000080">│</span> <span style="color: #008000">'size'</span>: <span style="color: #800080; font-style: italic">None</span>, <span style="color: #000080">││</span> <span style="color: #008000">'size'</span>: <span style="color: #800080; font-style: italic">None</span>, <span style="color: #000080">│</span>
|
||||
<span style="color: #000080">│</span> <span style="color: #008000">'minimum_size'</span>: <span style="color: #000080; font-weight: bold">1</span>, <span style="color: #000080">││</span> <span style="color: #008000">'minimum_size'</span>: <span style="color: #000080; font-weight: bold">1</span>, <span style="color: #000080">│</span>
|
||||
<span style="color: #000080">│</span> <span style="color: #008000">'ratio'</span>: <span style="color: #000080; font-weight: bold">1</span>, <span style="color: #000080">││</span> <span style="color: #008000">'ratio'</span>: <span style="color: #000080; font-weight: bold">1</span>, <span style="color: #000080">│</span>
|
||||
<span style="color: #000080">│</span> <span style="color: #008000">'name'</span>: <span style="color: #008000">'left'</span> <span style="color: #000080">││</span> <span style="color: #008000">'name'</span>: <span style="color: #008000">'right'</span> <span style="color: #000080">│</span>
|
||||
<span style="color: #000080">│</span> <span style="font-weight: bold">}</span> <span style="color: #000080">││</span> <span style="font-weight: bold">}</span> <span style="color: #000080">│</span>
|
||||
<span style="color: #000080">│</span> <span style="color: #000080">││</span> <span style="color: #000080">│</span>
|
||||
<span style="color: #000080">│</span> <span style="color: #000080">││</span> <span style="color: #000080">│</span>
|
||||
<span style="color: #000080">│</span> <span style="color: #000080">││</span> <span style="color: #000080">│</span>
|
||||
<span style="color: #000080">╰────────────────────────────────────────╯╰────────────────────────────────────────╯</span>
|
||||
</pre>
|
||||
|
||||
You can continue to call split() in this way to create as many parts to the screen as you wish.
|
||||
|
||||
Setting renderables
|
||||
-------------------
|
||||
|
||||
The first position argument to ``Layout`` can be any Rich renderable, which will be sized to fit within the layout's area. Here's how we might divide the "right" layout in to two panels::
|
||||
|
||||
layout["right"].split(
|
||||
Layout(Panel("Hello")),
|
||||
Layout(Panel("World!))
|
||||
)
|
||||
|
||||
You can also call :meth:`~rich.layout.Layout.update` to set or replace the current renderable::
|
||||
|
||||
layout["left"].update("The mystery of life isn't a problem to solve, but a reality to experience.")
|
||||
print(layout)
|
||||
|
@ -50,7 +90,7 @@ The Layout class would not be that useful if it only displayed placeholders. For
|
|||
Fixed size
|
||||
----------
|
||||
|
||||
You can set a sub-layout to use a fixed size by setting the ``size`` argument on the Layout constructor or by setting the attribute. Here's an example::
|
||||
You can set a layout to use a fixed size by setting the ``size`` argument on the Layout constructor or by setting the attribute. Here's an example::
|
||||
|
||||
layout["upper"].size = 10
|
||||
print(layout)
|
||||
|
@ -60,7 +100,7 @@ This will set the upper portion to be exactly 10 rows, no matter the size of the
|
|||
Ratio
|
||||
-----
|
||||
|
||||
In addition to a fixed size, you can also assign a *ratio* to a Layout in the constructor or by assigning to the attribute. The ratio defines how much of the screen the layout should occupy in relation to other layouts. For example, lets reset the size and set the ratio of the upper layout to 2::
|
||||
In addition to a fixed size, you can also make a flexible layout setting the ``ratio`` argument on the constructor or by assigning to the attribute. The ratio defines how much of the screen the layout should occupy in relation to other layouts. For example, lets reset the size and set the ratio of the upper layout to 2::
|
||||
|
||||
layout["upper"].size = None
|
||||
layout["upper"].ratio = 2
|
||||
|
@ -75,7 +115,7 @@ A layout with a ratio set may also have a minimum size to prevent it from gettin
|
|||
Visibility
|
||||
----------
|
||||
|
||||
Sub-layouts are visible by default, but you can make a layout invisible by setting the ``visible`` attribute to False. Here's an example::
|
||||
You can make a layout invisible by setting the ``visible`` attribute to False. Here's an example::
|
||||
|
||||
layout["upper"].visible = False
|
||||
print(layout)
|
||||
|
@ -85,10 +125,17 @@ The top layout is now invisible, and the "lower" layout will expand to fill the
|
|||
layout["upper"].visible = True
|
||||
print(layout)
|
||||
|
||||
You could use this to toggle parts of your interface based on your applications configuration.
|
||||
|
||||
Tree
|
||||
----
|
||||
|
||||
To help visualize complex layouts you can print the ``tree`` attribute which will display a summary of the layout with a tree::
|
||||
To help visualize complex layouts you can print the ``tree`` attribute which will display a summary of the layout as a tree::
|
||||
|
||||
print(layout.tree)
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
See `fullscreen.py <https://github.com/willmcgugan/rich/blob/master/examples/fullscreen.py>`_ for an example that combines :class:`~rich.layout.Layout` and :class:`rich.live.Live` to create a fullscreen "application".
|
||||
|
|
|
@ -75,7 +75,7 @@ Alternate screen
|
|||
|
||||
You can opt to show a Live display in the "alternate screen" by setting ``screen=False`` on the constructor. This will allow your live display to go full screen and restore the command prompt on exit.
|
||||
|
||||
You can use this feature in combination with :ref:`Layoout` to display sophisticated terminal "applications".
|
||||
You can use this feature in combination with :ref:`Layout` to display sophisticated terminal "applications".
|
||||
|
||||
Transient display
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
|
|
@ -1,17 +1,24 @@
|
|||
"""
|
||||
Demonstrates a Rich "application" using the Layout and Live classes.
|
||||
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from rich import box
|
||||
from rich.console import Console
|
||||
from rich.layout import Layout
|
||||
from rich.panel import Panel
|
||||
from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn
|
||||
from rich.syntax import Syntax
|
||||
from rich.table import Table
|
||||
|
||||
from rich.__main__ import make_test_card
|
||||
from rich.text import Text
|
||||
|
||||
console = Console()
|
||||
|
||||
|
||||
def make_layout() -> Layout:
|
||||
"""Define the layout."""
|
||||
layout = Layout(name="root")
|
||||
|
||||
layout.split(
|
||||
|
@ -28,7 +35,54 @@ def make_layout() -> Layout:
|
|||
return layout
|
||||
|
||||
|
||||
def make_sponsor_message() -> Panel:
|
||||
"""Some example content."""
|
||||
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",
|
||||
)
|
||||
sponsor_message.add_row(
|
||||
"Twitter",
|
||||
"[u blue link=https://twitter.com/willmcgugan]https://twitter.com/willmcgugan",
|
||||
)
|
||||
sponsor_message.add_row(
|
||||
"Blog", "[u blue link=https://www.willmcgugan.com]https://www.willmcgugan.com"
|
||||
)
|
||||
|
||||
intro_message = Text.from_markup(
|
||||
"""\
|
||||
It takes a lot of time to develop Rich and to provide support.
|
||||
|
||||
Consider supporting my work via Github Sponsors (ask your company / organization), or buy me a coffee to say thanks.
|
||||
|
||||
- Will McGugan"""
|
||||
)
|
||||
|
||||
message = Table.grid(padding=2)
|
||||
message.add_column()
|
||||
message.add_column(no_wrap=True)
|
||||
message.add_row(intro_message, sponsor_message)
|
||||
|
||||
message_panel = Panel.fit(
|
||||
message,
|
||||
box=box.ROUNDED,
|
||||
padding=(1, 2),
|
||||
title="[b red]Thanks for trying out Rich!",
|
||||
border_style="bright_blue",
|
||||
)
|
||||
return message_panel
|
||||
|
||||
|
||||
class Header:
|
||||
"""Display header with clock."""
|
||||
|
||||
def __rich__(self) -> Panel:
|
||||
grid = Table.grid(expand=True)
|
||||
grid.add_column(justify="center", ratio=1)
|
||||
|
@ -40,56 +94,48 @@ class Header:
|
|||
return Panel(grid, style="white on blue")
|
||||
|
||||
|
||||
code = """\
|
||||
def ratio_resolve(total: int, edges: List[Edge]) -> List[int]:
|
||||
sizes = [(edge.size or None) for edge in edges]
|
||||
def make_syntax() -> Syntax:
|
||||
code = """\
|
||||
def ratio_resolve(total: int, edges: List[Edge]) -> List[int]:
|
||||
sizes = [(edge.size or None) for edge in edges]
|
||||
|
||||
# While any edges haven't been calculated
|
||||
while any(size is None for size in sizes):
|
||||
# Get flexible edges and index to map these back on to sizes list
|
||||
flexible_edges = [
|
||||
(index, edge)
|
||||
for index, (size, edge) in enumerate(zip(sizes, edges))
|
||||
if size is None
|
||||
]
|
||||
# Remaining space in total
|
||||
remaining = total - sum(size or 0 for size in sizes)
|
||||
if remaining <= 0:
|
||||
# No room for flexible edges
|
||||
sizes[:] = [(size or 0) for size in sizes]
|
||||
break
|
||||
# Calculate number of characters in a ratio portion
|
||||
portion = remaining / sum((edge.ratio or 1) for _, edge in flexible_edges)
|
||||
|
||||
# If any edges will be less than their minimum, replace size with the minimum
|
||||
for index, edge in flexible_edges:
|
||||
if portion * edge.ratio <= edge.minimum_size:
|
||||
sizes[index] = edge.minimum_size
|
||||
# While any edges haven't been calculated
|
||||
while any(size is None for size in sizes):
|
||||
# Get flexible edges and index to map these back on to sizes list
|
||||
flexible_edges = [
|
||||
(index, edge)
|
||||
for index, (size, edge) in enumerate(zip(sizes, edges))
|
||||
if size is None
|
||||
]
|
||||
# Remaining space in total
|
||||
remaining = total - sum(size or 0 for size in sizes)
|
||||
if remaining <= 0:
|
||||
# No room for flexible edges
|
||||
sizes[:] = [(size or 0) for size in sizes]
|
||||
break
|
||||
else:
|
||||
# Distribute flexible space and compensate for rounding error
|
||||
# Since edge sizes can only be integers we need to add the remainder
|
||||
# to the following line
|
||||
_modf = modf
|
||||
remainder = 0.0
|
||||
# Calculate number of characters in a ratio portion
|
||||
portion = remaining / sum((edge.ratio or 1) for _, edge in flexible_edges)
|
||||
|
||||
# If any edges will be less than their minimum, replace size with the minimum
|
||||
for index, edge in flexible_edges:
|
||||
remainder, size = _modf(portion * edge.ratio + remainder)
|
||||
sizes[index] = int(size)
|
||||
break
|
||||
# Sizes now contains integers only
|
||||
return cast(List[int], sizes)
|
||||
"""
|
||||
syntax = Syntax(code, "python", line_numbers=True)
|
||||
|
||||
|
||||
layout = make_layout()
|
||||
layout["header"].update(Header())
|
||||
layout["body"].update(make_test_card())
|
||||
|
||||
|
||||
layout["box2"].update(Panel(syntax, border_style="green"))
|
||||
|
||||
layout["box1"].update(Panel(layout.tree, border_style="red"))
|
||||
if portion * edge.ratio <= edge.minimum_size:
|
||||
sizes[index] = edge.minimum_size
|
||||
break
|
||||
else:
|
||||
# Distribute flexible space and compensate for rounding error
|
||||
# Since edge sizes can only be integers we need to add the remainder
|
||||
# to the following line
|
||||
_modf = modf
|
||||
remainder = 0.0
|
||||
for index, edge in flexible_edges:
|
||||
remainder, size = _modf(portion * edge.ratio + remainder)
|
||||
sizes[index] = int(size)
|
||||
break
|
||||
# Sizes now contains integers only
|
||||
return cast(List[int], sizes)
|
||||
"""
|
||||
syntax = Syntax(code, "python", line_numbers=True)
|
||||
return syntax
|
||||
|
||||
|
||||
job_progress = Progress(
|
||||
|
@ -98,9 +144,9 @@ job_progress = Progress(
|
|||
BarColumn(),
|
||||
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
||||
)
|
||||
job1 = job_progress.add_task("[green]Cooking")
|
||||
job2 = job_progress.add_task("[magenta]Baking", total=200)
|
||||
job3 = job_progress.add_task("[cyan]Mixing", total=400)
|
||||
job_progress.add_task("[green]Cooking")
|
||||
job_progress.add_task("[magenta]Baking", total=200)
|
||||
job_progress.add_task("[cyan]Mixing", total=400)
|
||||
|
||||
total = sum(task.total for task in job_progress.tasks)
|
||||
overall_progress = Progress()
|
||||
|
@ -109,11 +155,20 @@ overall_task = overall_progress.add_task("All Jobs", total=int(total))
|
|||
progress_table = Table.grid(expand=True)
|
||||
progress_table.add_row(
|
||||
Panel(
|
||||
overall_progress, title="Overall Progress", border_style="green", padding=(2, 2)
|
||||
overall_progress,
|
||||
title="Overall Progress",
|
||||
border_style="green",
|
||||
padding=(2, 2),
|
||||
),
|
||||
Panel(job_progress, title="[b]Jobs", border_style="red", padding=(1, 2)),
|
||||
)
|
||||
|
||||
|
||||
layout = make_layout()
|
||||
layout["header"].update(Header())
|
||||
layout["body"].update(make_sponsor_message())
|
||||
layout["box2"].update(Panel(make_syntax(), border_style="green"))
|
||||
layout["box1"].update(Panel(layout.tree, border_style="red"))
|
||||
layout["footer"].update(progress_table)
|
||||
|
||||
|
||||
|
|
|
@ -135,12 +135,16 @@ class Layout:
|
|||
|
||||
def summary(layout) -> "Text":
|
||||
name = repr(layout.name) + " " if layout.name else ""
|
||||
direction = "➡" if layout.direction == "horizontal" else "⬇"
|
||||
direction = (
|
||||
("➡" if layout.direction == "horizontal" else "⬇")
|
||||
if layout._children
|
||||
else "■"
|
||||
)
|
||||
if layout.size:
|
||||
_summary = highlighter(f"{direction} {name}(size={layout.size})")
|
||||
else:
|
||||
_summary = highlighter(f"{direction} {name}(ratio={layout.ratio})")
|
||||
_summary.stylize("dim" if layout.visible else "")
|
||||
_summary.stylize("" if layout.visible else "dim")
|
||||
return _summary
|
||||
|
||||
layout = self
|
||||
|
|
|
@ -892,6 +892,7 @@ class Progress(JupyterMixin):
|
|||
return table
|
||||
|
||||
def __rich__(self) -> RenderableType:
|
||||
"""Makes the Progress class itself renderable."""
|
||||
return self.get_renderable()
|
||||
|
||||
def add_task(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue