mirror of https://github.com/Textualize/rich.git
186 lines
5.8 KiB
Python
186 lines
5.8 KiB
Python
"""
|
|
Demonstrates a Rich "application" using the Layout and Live classes.
|
|
|
|
"""
|
|
|
|
from datetime import datetime
|
|
|
|
from rich import box
|
|
from rich.align import Align
|
|
from rich.console import Console, RenderGroup
|
|
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.text import Text
|
|
|
|
console = Console()
|
|
|
|
|
|
def make_layout() -> Layout:
|
|
"""Define the layout."""
|
|
layout = Layout(name="root")
|
|
|
|
layout.split(
|
|
Layout(name="header", size=3),
|
|
Layout(name="main", ratio=1),
|
|
Layout(name="footer", size=7),
|
|
)
|
|
layout["main"].split(
|
|
Layout(name="side"),
|
|
Layout(name="body", ratio=2, minimum_size=60),
|
|
direction="horizontal",
|
|
)
|
|
layout["side"].split(Layout(name="box1"), Layout(name="box2"))
|
|
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(
|
|
"""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=1)
|
|
message.add_column()
|
|
message.add_column(no_wrap=True)
|
|
message.add_row(intro_message, sponsor_message)
|
|
|
|
message_panel = Panel(
|
|
Align.center(
|
|
RenderGroup(intro_message, "\n", Align.center(sponsor_message)),
|
|
vertical="middle",
|
|
),
|
|
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)
|
|
grid.add_column(justify="right")
|
|
grid.add_row(
|
|
"[b]Rich[/b] Layout application",
|
|
datetime.now().ctime().replace(":", "[blink]:[/]"),
|
|
)
|
|
return Panel(grid, style="white on blue")
|
|
|
|
|
|
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
|
|
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(
|
|
"{task.description}",
|
|
SpinnerColumn(),
|
|
BarColumn(),
|
|
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
|
)
|
|
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()
|
|
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),
|
|
),
|
|
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)
|
|
|
|
|
|
from rich.live import Live
|
|
from time import sleep
|
|
|
|
with Live(layout, refresh_per_second=10, screen=True):
|
|
while not overall_progress.finished:
|
|
sleep(0.1)
|
|
for job in job_progress.tasks:
|
|
if not job.finished:
|
|
job_progress.advance(job.id)
|
|
|
|
completed = sum(task.completed for task in job_progress.tasks)
|
|
overall_progress.update(overall_task, completed=completed)
|