summaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/README.md5
-rw-r--r--examples/bars.py21
-rw-r--r--examples/columns.py28
-rw-r--r--examples/downloader.py64
-rw-r--r--examples/exception.py36
-rw-r--r--examples/fullscreen.py185
-rw-r--r--examples/group.py9
-rw-r--r--examples/group2.py12
-rw-r--r--examples/highlighter.py20
-rw-r--r--examples/jobs.py31
-rw-r--r--examples/justify.py13
-rw-r--r--examples/justify2.py15
-rw-r--r--examples/layout.py57
-rw-r--r--examples/link.py4
-rw-r--r--examples/listdir.py35
-rw-r--r--examples/live_progress.py45
-rw-r--r--examples/log.py77
-rw-r--r--examples/overflow.py11
-rw-r--r--examples/padding.py5
-rw-r--r--examples/rainbow.py20
-rw-r--r--examples/screen.py21
-rw-r--r--examples/spinners.py23
-rw-r--r--examples/status.py13
-rw-r--r--examples/table.py20
-rw-r--r--examples/table_movie.py197
-rw-r--r--examples/top_lite_simulator.py81
-rw-r--r--examples/tree.py55
27 files changed, 1103 insertions, 0 deletions
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 0000000..c7024be
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1,5 @@
+# Examples
+
+This directory contains various demonstrations various Rich features. To run them, make sure Rich is installed, then enter `python example.py` on the command line.
+
+Be sure to check the source!
diff --git a/examples/bars.py b/examples/bars.py
new file mode 100644
index 0000000..4ce7635
--- /dev/null
+++ b/examples/bars.py
@@ -0,0 +1,21 @@
+"""
+
+Use Bar to renderer a sort-of circle.
+
+"""
+import math
+
+from rich.align import Align
+from rich.bar import Bar
+from rich.color import Color
+from rich import print
+
+
+SIZE = 40
+
+for row in range(SIZE):
+ y = (row / (SIZE - 1)) * 2 - 1
+ x = math.sqrt(1 - y * y)
+ color = Color.from_rgb((1 + y) * 127.5, 0, 0)
+ bar = Bar(2, width=SIZE * 2, begin=1 - x, end=1 + x, color=color)
+ print(Align.center(bar))
diff --git a/examples/columns.py b/examples/columns.py
new file mode 100644
index 0000000..96962b0
--- /dev/null
+++ b/examples/columns.py
@@ -0,0 +1,28 @@
+"""
+This example shows how to display content in columns.
+
+The data is pulled from https://randomuser.me
+"""
+
+import json
+from urllib.request import urlopen
+
+from rich.console import Console
+from rich.columns import Columns
+from rich.panel import Panel
+
+
+def get_content(user):
+ """Extract text from user dict."""
+ country = user["location"]["country"]
+ name = f"{user['name']['first']} {user['name']['last']}"
+ return f"[b]{name}[/b]\n[yellow]{country}"
+
+
+console = Console()
+
+
+users = json.loads(urlopen("https://randomuser.me/api/?results=30").read())["results"]
+console.print(users, overflow="ignore", crop=False)
+user_renderables = [Panel(get_content(user), expand=True) for user in users]
+console.print(Columns(user_renderables))
diff --git a/examples/downloader.py b/examples/downloader.py
new file mode 100644
index 0000000..5120ed5
--- /dev/null
+++ b/examples/downloader.py
@@ -0,0 +1,64 @@
+"""
+A rudimentary URL downloader (like wget or curl) to demonstrate Rich progress bars.
+"""
+
+from concurrent.futures import ThreadPoolExecutor
+from functools import partial
+import os.path
+import sys
+from typing import Iterable
+from urllib.request import urlopen
+
+from rich.progress import (
+ BarColumn,
+ DownloadColumn,
+ TextColumn,
+ TransferSpeedColumn,
+ TimeRemainingColumn,
+ Progress,
+ TaskID,
+)
+
+
+progress = Progress(
+ TextColumn("[bold blue]{task.fields[filename]}", justify="right"),
+ BarColumn(bar_width=None),
+ "[progress.percentage]{task.percentage:>3.1f}%",
+ "•",
+ DownloadColumn(),
+ "•",
+ TransferSpeedColumn(),
+ "•",
+ TimeRemainingColumn(),
+)
+
+
+def copy_url(task_id: TaskID, url: str, path: str) -> None:
+ """Copy data from a url to a local file."""
+ response = urlopen(url)
+ # This will break if the response doesn't contain content length
+ progress.update(task_id, total=int(response.info()["Content-length"]))
+ with open(path, "wb") as dest_file:
+ progress.start_task(task_id)
+ for data in iter(partial(response.read, 32768), b""):
+ dest_file.write(data)
+ progress.update(task_id, advance=len(data))
+
+
+def download(urls: Iterable[str], dest_dir: str):
+ """Download multuple files to the given directory."""
+ with progress:
+ with ThreadPoolExecutor(max_workers=4) as pool:
+ for url in urls:
+ filename = url.split("/")[-1]
+ dest_path = os.path.join(dest_dir, filename)
+ task_id = progress.add_task("download", filename=filename, start=False)
+ pool.submit(copy_url, task_id, url, dest_path)
+
+
+if __name__ == "__main__":
+ # Try with https://releases.ubuntu.com/20.04/ubuntu-20.04.1-desktop-amd64.iso
+ if sys.argv[1:]:
+ download(sys.argv[1:], "./")
+ else:
+ print("Usage:\n\tpython downloader.py URL1 URL2 URL3 (etc)")
diff --git a/examples/exception.py b/examples/exception.py
new file mode 100644
index 0000000..6bf2a1e
--- /dev/null
+++ b/examples/exception.py
@@ -0,0 +1,36 @@
+"""
+Basic example to show how to print an traceback of an exception
+"""
+from typing import List, Tuple
+from rich.console import Console
+
+console = Console()
+
+
+def divide_by(number: float, divisor: float) -> float:
+ """Divide any number by zero."""
+ # Will throw a ZeroDivisionError if divisor is 0
+ result = number / divisor
+ return result
+
+
+def divide_all(divides: List[Tuple[float, float]]) -> None:
+ """Do something impossible every day."""
+ try:
+ for number, divisor in divides:
+ result = divide_by(number, divisor)
+ console.print(f"{number} divided by {divisor} is {result}")
+ except Exception:
+ console.print_exception(extra_lines=5, show_locals=True)
+
+
+DIVIDES = [
+ (1000, 200),
+ (10000, 500),
+ (0, 1000000),
+ (3.1427, 2),
+ (2 ** 32, 2 ** 16),
+ (1, 0),
+]
+
+divide_all(DIVIDES)
diff --git a/examples/fullscreen.py b/examples/fullscreen.py
new file mode 100644
index 0000000..507eed1
--- /dev/null
+++ b/examples/fullscreen.py
@@ -0,0 +1,185 @@
+"""
+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)
diff --git a/examples/group.py b/examples/group.py
new file mode 100644
index 0000000..a61f830
--- /dev/null
+++ b/examples/group.py
@@ -0,0 +1,9 @@
+from rich import print
+from rich.console import RenderGroup
+from rich.panel import Panel
+
+panel_group = RenderGroup(
+ Panel("Hello", style="on blue"),
+ Panel("World", style="on red"),
+)
+print(Panel(panel_group))
diff --git a/examples/group2.py b/examples/group2.py
new file mode 100644
index 0000000..13be070
--- /dev/null
+++ b/examples/group2.py
@@ -0,0 +1,12 @@
+from rich import print
+from rich.console import render_group
+from rich.panel import Panel
+
+
+@render_group()
+def get_panels():
+ yield Panel("Hello", style="on blue")
+ yield Panel("World", style="on red")
+
+
+print(Panel(get_panels()))
diff --git a/examples/highlighter.py b/examples/highlighter.py
new file mode 100644
index 0000000..a667d0d
--- /dev/null
+++ b/examples/highlighter.py
@@ -0,0 +1,20 @@
+"""
+This example demonstrates a simple text highlighter.
+"""
+
+from rich.console import Console
+from rich.highlighter import RegexHighlighter
+from rich.theme import Theme
+
+
+class EmailHighlighter(RegexHighlighter):
+ """Apply style to anything that looks like an email."""
+
+ base_style = "example."
+ highlights = [r"(?P<email>[\w-]+@([\w-]+\.)+[\w-]+)"]
+
+
+theme = Theme({"example.email": "bold magenta"})
+console = Console(highlighter=EmailHighlighter(), theme=theme)
+
+console.print("Send funds to money@example.org")
diff --git a/examples/jobs.py b/examples/jobs.py
new file mode 100644
index 0000000..6286b56
--- /dev/null
+++ b/examples/jobs.py
@@ -0,0 +1,31 @@
+from time import sleep
+from rich.panel import Panel
+from rich.progress import Progress
+
+
+JOBS = [100, 150, 25, 70, 110, 90]
+
+progress = Progress(auto_refresh=False)
+master_task = progress.add_task("overall", total=sum(JOBS))
+jobs_task = progress.add_task("jobs")
+
+progress.console.print(
+ Panel(
+ "[bold blue]A demonstration of progress with a current task and overall progress.",
+ padding=1,
+ )
+)
+
+with progress:
+ for job_no, job in enumerate(JOBS):
+ progress.log(f"Starting job #{job_no}")
+ sleep(0.2)
+ progress.reset(jobs_task, total=job, description=f"job [bold yellow]#{job_no}")
+ progress.start_task(jobs_task)
+ for wait in progress.track(range(job), task_id=jobs_task):
+ sleep(0.01)
+ progress.advance(master_task, job)
+ progress.log(f"Job #{job_no} is complete")
+ progress.log(
+ Panel(":sparkle: All done! :sparkle:", border_style="green", padding=1)
+ )
diff --git a/examples/justify.py b/examples/justify.py
new file mode 100644
index 0000000..6709882
--- /dev/null
+++ b/examples/justify.py
@@ -0,0 +1,13 @@
+"""
+This example demonstrates the justify argument to print.
+"""
+
+from rich.console import Console
+
+console = Console(width=20)
+
+style = "bold white on blue"
+console.print("Rich", style=style)
+console.print("Rich", style=style, justify="left")
+console.print("Rich", style=style, justify="center")
+console.print("Rich", style=style, justify="right")
diff --git a/examples/justify2.py b/examples/justify2.py
new file mode 100644
index 0000000..3b9882a
--- /dev/null
+++ b/examples/justify2.py
@@ -0,0 +1,15 @@
+"""
+This example demonstrates the justify argument to print.
+"""
+
+from rich.console import Console
+from rich.panel import Panel
+
+console = Console(width=20)
+
+style = "bold white on blue"
+panel = Panel("Rich", style="on red", expand=False)
+console.print(panel, style=style)
+console.print(panel, style=style, justify="left")
+console.print(panel, style=style, justify="center")
+console.print(panel, style=style, justify="right")
diff --git a/examples/layout.py b/examples/layout.py
new file mode 100644
index 0000000..046476b
--- /dev/null
+++ b/examples/layout.py
@@ -0,0 +1,57 @@
+"""
+
+Demonstrates a dynamic Layout
+
+"""
+
+from datetime import datetime
+
+from time import sleep
+
+from rich.align import Align
+from rich.console import Console
+from rich.layout import Layout
+from rich.live import Live
+from rich.text import Text
+
+console = Console()
+layout = Layout()
+
+layout.split(
+ Layout(name="header", size=1),
+ Layout(ratio=1, name="main"),
+ Layout(size=10, name="footer"),
+)
+
+layout["main"].split(
+ Layout(name="side"), Layout(name="body", ratio=2), direction="horizontal"
+)
+
+layout["side"].split(Layout(), Layout())
+
+layout["body"].update(
+ Align.center(
+ Text(
+ """This is a demonstration of rich.Layout\n\nHit Ctrl+C to exit""",
+ justify="center",
+ ),
+ vertical="middle",
+ )
+)
+
+
+class Clock:
+ """Renders the time in the center of the screen."""
+
+ def __rich__(self) -> Text:
+ return Text(datetime.now().ctime(), style="bold magenta", justify="center")
+
+
+layout["header"].update(Clock())
+
+with Live(layout, screen=True) as live:
+ try:
+ while True:
+ sleep(1)
+ except KeyboardInterrupt:
+ pass
diff --git a/examples/link.py b/examples/link.py
new file mode 100644
index 0000000..9773c33
--- /dev/null
+++ b/examples/link.py
@@ -0,0 +1,4 @@
+from rich import print
+
+print("If your terminal supports links, the following text should be clickable:")
+print("[link=https://www.willmcgugan.com][i]Visit [red]my[/red][/i] [yellow]Blog[/]")
diff --git a/examples/listdir.py b/examples/listdir.py
new file mode 100644
index 0000000..bffc50a
--- /dev/null
+++ b/examples/listdir.py
@@ -0,0 +1,35 @@
+"""
+A very simple `ls` clone.
+
+If your terminal supports hyperlinks you should be able to launch files by clicking the filename
+(usually with cmd / ctrl).
+
+"""
+
+import os
+import sys
+
+from rich import print
+from rich.columns import Columns
+from rich.text import Text
+
+try:
+ root_path = sys.argv[1]
+except IndexError:
+ print("Usage: python listdir.py DIRECTORY")
+else:
+
+ def make_filename_text(filename):
+ path = os.path.abspath(os.path.join(root_path, filename))
+ text = Text(filename, style="bold blue" if os.path.isdir(path) else "default")
+ text.stylize(f"link file://{path}")
+ text.highlight_regex(r"\..*?$", "bold")
+ return text
+
+ filenames = [
+ filename for filename in os.listdir(root_path) if not filename.startswith(".")
+ ]
+ filenames.sort(key=lambda filename: filename.lower())
+ filename_text = [make_filename_text(filename) for filename in filenames]
+ columns = Columns(filename_text, equal=True, column_first=True)
+ print(columns)
diff --git a/examples/live_progress.py b/examples/live_progress.py
new file mode 100644
index 0000000..7ae1056
--- /dev/null
+++ b/examples/live_progress.py
@@ -0,0 +1,45 @@
+"""
+
+Demonstrates the use of multiple Progress instances in a single Live display.
+
+"""
+
+from time import sleep
+
+from rich.live import Live
+from rich.panel import Panel
+from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn
+from rich.table import Table
+
+
+job_progress = Progress(
+ "{task.description}",
+ SpinnerColumn(),
+ 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)
+
+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()
+progress_table.add_row(
+ Panel.fit(
+ overall_progress, title="Overall Progress", border_style="green", padding=(2, 2)
+ ),
+ Panel.fit(job_progress, title="[b]Jobs", border_style="red", padding=(1, 2)),
+)
+
+with Live(progress_table, refresh_per_second=10):
+ 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)
diff --git a/examples/log.py b/examples/log.py
new file mode 100644
index 0000000..5fe54e8
--- /dev/null
+++ b/examples/log.py
@@ -0,0 +1,77 @@
+"""
+A simulation of Rich console logging.
+"""
+
+import time
+from rich.console import Console
+from rich.style import Style
+from rich.theme import Theme
+from rich.highlighter import RegexHighlighter
+
+
+class RequestHighlighter(RegexHighlighter):
+ base_style = "req."
+ highlights = [
+ r"^(?P<protocol>\w+) (?P<method>\w+) (?P<path>\S+) (?P<result>\w+) (?P<stats>\[.+\])$",
+ r"\/(?P<filename>\w+\..{3,4})",
+ ]
+
+
+theme = Theme(
+ {
+ "req.protocol": Style.parse("dim bold green"),
+ "req.method": Style.parse("bold cyan"),
+ "req.path": Style.parse("magenta"),
+ "req.filename": Style.parse("bright_magenta"),
+ "req.result": Style.parse("yellow"),
+ "req.stats": Style.parse("dim"),
+ }
+)
+console = Console(theme=theme)
+
+console.log("Server starting...")
+console.log("Serving on http://127.0.0.1:8000")
+
+time.sleep(1)
+
+request_highlighter = RequestHighlighter()
+
+console.log(
+ request_highlighter("HTTP GET /foo/bar/baz/egg.html 200 [0.57, 127.0.0.1:59076]"),
+)
+
+console.log(
+ request_highlighter(
+ "HTTP GET /foo/bar/baz/background.jpg 200 [0.57, 127.0.0.1:59076]"
+ ),
+)
+
+
+time.sleep(1)
+
+
+def test_locals():
+ foo = (1, 2, 3)
+ movies = ["Deadpool", "Rise of the Skywalker"]
+ console = Console()
+
+ console.log(
+ "[b]JSON[/b] RPC [i]batch[/i]",
+ [
+ {"jsonrpc": "2.0", "method": "sum", "params": [1, 2, 4], "id": "1"},
+ {"jsonrpc": "2.0", "method": "notify_hello", "params": [7]},
+ {"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": "2"},
+ {"foo": "boo"},
+ {
+ "jsonrpc": "2.0",
+ "method": "foo.get",
+ "params": {"name": "myself", "enable": False, "grommits": None},
+ "id": "5",
+ },
+ {"jsonrpc": "2.0", "method": "get_data", "id": "9"},
+ ],
+ log_locals=True,
+ )
+
+
+test_locals()
diff --git a/examples/overflow.py b/examples/overflow.py
new file mode 100644
index 0000000..5adaa3d
--- /dev/null
+++ b/examples/overflow.py
@@ -0,0 +1,11 @@
+from typing import List
+from rich.console import Console, OverflowMethod
+
+console = Console(width=14)
+supercali = "supercalifragilisticexpialidocious"
+
+overflow_methods: List[OverflowMethod] = ["fold", "crop", "ellipsis"]
+for overflow in overflow_methods:
+ console.rule(overflow)
+ console.print(supercali, overflow=overflow, style="bold blue")
+ console.print()
diff --git a/examples/padding.py b/examples/padding.py
new file mode 100644
index 0000000..b01d274
--- /dev/null
+++ b/examples/padding.py
@@ -0,0 +1,5 @@
+from rich import print
+from rich.padding import Padding
+
+test = Padding("Hello", (2, 4), style="on blue", expand=False)
+print(test)
diff --git a/examples/rainbow.py b/examples/rainbow.py
new file mode 100644
index 0000000..2121a78
--- /dev/null
+++ b/examples/rainbow.py
@@ -0,0 +1,20 @@
+"""
+
+This example demonstrates how to write a custom highlighter.
+
+"""
+
+from random import randint
+
+from rich import print
+from rich.highlighter import Highlighter
+
+
+class RainbowHighlighter(Highlighter):
+ def highlight(self, text):
+ for index in range(len(text)):
+ text.stylize(f"color({randint(16, 255)})", index, index + 1)
+
+
+rainbow = RainbowHighlighter()
+print(rainbow("I must not fear. Fear is the mind-killer."))
diff --git a/examples/screen.py b/examples/screen.py
new file mode 100644
index 0000000..90f1028
--- /dev/null
+++ b/examples/screen.py
@@ -0,0 +1,21 @@
+"""
+Demonstration of Console.screen()
+"""
+
+from time import sleep
+
+from rich.console import Console
+from rich.align import Align
+from rich.text import Text
+from rich.panel import Panel
+
+console = Console()
+
+with console.screen(style="bold white on red") as screen:
+ for count in range(5, 0, -1):
+ text = Align.center(
+ Text.from_markup(f"[blink]Don't Panic![/blink]\n{count}", justify="center"),
+ vertical="middle",
+ )
+ screen.update(Panel(text))
+ sleep(1)
diff --git a/examples/spinners.py b/examples/spinners.py
new file mode 100644
index 0000000..9daf847
--- /dev/null
+++ b/examples/spinners.py
@@ -0,0 +1,23 @@
+from time import sleep
+
+from rich.columns import Columns
+from rich.panel import Panel
+from rich.live import Live
+from rich.text import Text
+from rich.spinner import Spinner, SPINNERS
+
+all_spinners = Columns(
+ [
+ Spinner(spinner_name, text=Text(repr(spinner_name), style="green"))
+ for spinner_name in sorted(SPINNERS)
+ ],
+ column_first=True,
+ expand=True,
+)
+
+with Live(
+ Panel(all_spinners, title="Spinners", border_style="blue"),
+ refresh_per_second=20,
+) as live:
+ while True:
+ sleep(0.1)
diff --git a/examples/status.py b/examples/status.py
new file mode 100644
index 0000000..88d1679
--- /dev/null
+++ b/examples/status.py
@@ -0,0 +1,13 @@
+from time import sleep
+from rich.console import Console
+
+console = Console()
+console.print()
+
+tasks = [f"task {n}" for n in range(1, 11)]
+
+with console.status("[bold green]Working on tasks...") as status:
+ while tasks:
+ task = tasks.pop(0)
+ sleep(1)
+ console.log(f"{task} complete")
diff --git a/examples/table.py b/examples/table.py
new file mode 100644
index 0000000..6ffa7ee
--- /dev/null
+++ b/examples/table.py
@@ -0,0 +1,20 @@
+"""
+Demonstrates how to render a table.
+"""
+
+from rich.console import Console
+from rich.table import Table
+
+table = Table(title="Star Wars Movies")
+
+table.add_column("Released", style="cyan", no_wrap=True)
+table.add_column("Title", style="magenta")
+table.add_column("Box Office", justify="right", style="green")
+
+table.add_row("Dec 20, 2019", "Star Wars: The Rise of Skywalker", "$952,110,690")
+table.add_row("May 25, 2018", "Solo: A Star Wars Story", "$393,151,347")
+table.add_row("Dec 15, 2017", "Star Wars Ep. V111: The Last Jedi", "$1,332,539,889")
+table.add_row("Dec 16, 2016", "Rogue One: A Star Wars Story", "$1,332,439,889")
+
+console = Console()
+console.print(table, justify="center")
diff --git a/examples/table_movie.py b/examples/table_movie.py
new file mode 100644
index 0000000..eaa35cd
--- /dev/null
+++ b/examples/table_movie.py
@@ -0,0 +1,197 @@
+"""Same as the table_movie.py but uses Live to update"""
+import time
+from contextlib import contextmanager
+
+from rich import box
+from rich.align import Align
+from rich.console import Console
+from rich.live import Live
+from rich.measure import Measurement
+from rich.table import Table
+from rich.text import Text
+
+TABLE_DATA = [
+ [
+ "May 25, 1977",
+ "Star Wars Ep. [b]IV[/]: [i]A New Hope",
+ "$11,000,000",
+ "$1,554,475",
+ "$775,398,007",
+ ],
+ [
+ "May 21, 1980",
+ "Star Wars Ep. [b]V[/]: [i]The Empire Strikes Back",
+ "$23,000,000",
+ "$4,910,483",
+ "$547,969,004",
+ ],
+ [
+ "May 25, 1983",
+ "Star Wars Ep. [b]VI[/b]: [i]Return of the Jedi",
+ "$32,500,000",
+ "$23,019,618",
+ "$475,106,177",
+ ],
+ [
+ "May 19, 1999",
+ "Star Wars Ep. [b]I[/b]: [i]The phantom Menace",
+ "$115,000,000",
+ "$64,810,870",
+ "$1,027,044,677",
+ ],
+ [
+ "May 16, 2002",
+ "Star Wars Ep. [b]II[/b]: [i]Attack of the Clones",
+ "$115,000,000",
+ "$80,027,814",
+ "$656,695,615",
+ ],
+ [
+ "May 19, 2005",
+ "Star Wars Ep. [b]III[/b]: [i]Revenge of the Sith",
+ "$115,500,000",
+ "$380,270,577",
+ "$848,998,877",
+ ],
+]
+
+console = Console()
+
+BEAT_TIME = 0.04
+
+
+@contextmanager
+def beat(length: int = 1) -> None:
+ yield
+ time.sleep(length * BEAT_TIME)
+
+
+table = Table(show_footer=False)
+table_centered = Align.center(table)
+
+console.clear()
+
+with Live(table_centered, console=console, screen=False, refresh_per_second=20):
+ with beat(10):
+ table.add_column("Release Date", no_wrap=True)
+
+ with beat(10):
+ table.add_column("Title", Text.from_markup("[b]Total", justify="right"))
+
+ with beat(10):
+ table.add_column("Budget", "[u]$412,000,000", no_wrap=True)
+
+ with beat(10):
+ table.add_column("Opening Weekend", "[u]$577,703,455", no_wrap=True)
+
+ with beat(10):
+ table.add_column("Box Office", "[u]$4,331,212,357", no_wrap=True)
+
+ with beat(10):
+ table.title = "Star Wars Box Office"
+
+ with beat(10):
+ table.title = (
+ "[not italic]:popcorn:[/] Star Wars Box Office [not italic]:popcorn:[/]"
+ )
+
+ with beat(10):
+ table.caption = "Made with Rich"
+
+ with beat(10):
+ table.caption = "Made with [b]Rich[/b]"
+
+ with beat(10):
+ table.caption = "Made with [b magenta not dim]Rich[/]"
+
+ for row in TABLE_DATA:
+ with beat(10):
+ table.add_row(*row)
+
+ with beat(10):
+ table.show_footer = True
+
+ table_width = Measurement.get(console, table, console.width).maximum
+
+ with beat(10):
+ table.columns[2].justify = "right"
+
+ with beat(10):
+ table.columns[3].justify = "right"
+
+ with beat(10):
+ table.columns[4].justify = "right"
+
+ with beat(10):
+ table.columns[2].header_style = "bold red"
+
+ with beat(10):
+ table.columns[3].header_style = "bold green"
+
+ with beat(10):
+ table.columns[4].header_style = "bold blue"
+
+ with beat(10):
+ table.columns[2].style = "red"
+
+ with beat(10):
+ table.columns[3].style = "green"
+
+ with beat(10):
+ table.columns[4].style = "blue"
+
+ with beat(10):
+ table.columns[0].style = "cyan"
+ table.columns[0].header_style = "bold cyan"
+
+ with beat(10):
+ table.columns[1].style = "magenta"
+ table.columns[1].header_style = "bold magenta"
+
+ with beat(10):
+ table.columns[2].footer_style = "bright_red"
+
+ with beat(10):
+ table.columns[3].footer_style = "bright_green"
+
+ with beat(10):
+ table.columns[4].footer_style = "bright_blue"
+
+ with beat(10):
+ table.row_styles = ["none", "dim"]
+
+ with beat(10):
+ table.border_style = "bright_yellow"
+
+ for box in [
+ box.SQUARE,
+ box.MINIMAL,
+ box.SIMPLE,
+ box.SIMPLE_HEAD,
+ ]:
+ with beat(10):
+ table.box = box
+
+ with beat(10):
+ table.pad_edge = False
+
+ original_width = Measurement.get(console, table).maximum
+
+ for width in range(original_width, console.width, 2):
+ with beat(1):
+ table.width = width
+
+ for width in range(console.width, original_width, -2):
+ with beat(1):
+ table.width = width
+
+ for width in range(original_width, 90, -2):
+ with beat(1):
+ table.width = width
+
+ for width in range(90, original_width + 1, 2):
+ with beat(1):
+ table.width = width
+
+ with beat(2):
+ table.width = None
diff --git a/examples/top_lite_simulator.py b/examples/top_lite_simulator.py
new file mode 100644
index 0000000..e4a10f5
--- /dev/null
+++ b/examples/top_lite_simulator.py
@@ -0,0 +1,81 @@
+"""Lite simulation of the top linux command."""
+
+import datetime
+import random
+import time
+from dataclasses import dataclass
+
+from rich import box
+from rich.console import Console
+from rich.live import Live
+from rich.table import Table
+from typing_extensions import Literal
+
+
+@dataclass
+class Process:
+ pid: int
+ command: str
+ cpu_percent: float
+ memory: int
+ start_time: datetime.datetime
+ thread_count: int
+ state: Literal["running", "sleeping"]
+
+ @property
+ def memory_str(self) -> str:
+ if self.memory > 1e6:
+ return f"{int(self.memory/1e6)}M"
+ if self.memory > 1e3:
+ return f"{int(self.memory/1e3)}K"
+ return str(self.memory)
+
+ @property
+ def time_str(self) -> str:
+ return str(datetime.datetime.now() - self.start_time)
+
+
+def generate_process(pid: int) -> Process:
+ return Process(
+ pid=pid,
+ command=f"Process {pid}",
+ cpu_percent=random.random() * 20,
+ memory=random.randint(10, 200) ** 3,
+ start_time=datetime.datetime.now()
+ - datetime.timedelta(seconds=random.randint(0, 500) ** 2),
+ thread_count=random.randint(1, 32),
+ state="running" if random.randint(0, 10) < 8 else "sleeping",
+ )
+
+
+def create_process_table(height: int) -> Table:
+
+ processes = sorted(
+ [generate_process(pid) for pid in range(height)],
+ key=lambda p: p.cpu_percent,
+ reverse=True,
+ )
+ table = Table(
+ "PID", "Command", "CPU %", "Memory", "Time", "Thread #", "State", box=box.SIMPLE
+ )
+
+ for process in processes:
+ table.add_row(
+ str(process.pid),
+ process.command,
+ f"{process.cpu_percent:.1f}",
+ process.memory_str,
+ process.time_str,
+ str(process.thread_count),
+ process.state,
+ )
+
+ return table
+
+
+console = Console()
+
+with Live(console=console, screen=True, auto_refresh=False) as live:
+ while True:
+ live.update(create_process_table(console.size.height - 4), refresh=True)
+ time.sleep(1)
diff --git a/examples/tree.py b/examples/tree.py
new file mode 100644
index 0000000..9927719
--- /dev/null
+++ b/examples/tree.py
@@ -0,0 +1,55 @@
+"""
+Demonstrates how to display a tree of files / directories with the Tree renderable.
+"""
+
+import os
+import pathlib
+import sys
+
+from rich import print
+from rich.filesize import decimal
+from rich.markup import escape
+from rich.text import Text
+from rich.tree import Tree
+
+
+def walk_directory(directory: pathlib.Path, tree: Tree) -> None:
+ """Recursively build a Tree with directory contents."""
+ # Sort dirs first then by filename
+ paths = sorted(
+ pathlib.Path(directory).iterdir(),
+ key=lambda path: (path.is_file(), path.name.lower()),
+ )
+ for path in paths:
+ # Remove hidden files
+ if path.name.startswith("."):
+ continue
+ if path.is_dir():
+ style = "dim" if path.name.startswith("__") else ""
+ branch = tree.add(
+ f"[bold magenta]:open_file_folder: [link file://{path}]{escape(path.name)}",
+ style=style,
+ guide_style=style,
+ )
+ walk_directory(path, branch)
+ else:
+ text_filename = Text(path.name, "green")
+ text_filename.highlight_regex(r"\..*$", "bold red")
+ text_filename.stylize(f"link file://{path}")
+ file_size = path.stat().st_size
+ text_filename.append(f" ({decimal(file_size)})", "blue")
+ icon = "🐍 " if path.suffix == ".py" else "📄 "
+ tree.add(Text(icon) + text_filename)
+
+
+try:
+ directory = os.path.abspath(sys.argv[1])
+except IndexError:
+ print("[b]Usage:[/] python tree.py <DIRECTORY>")
+else:
+ tree = Tree(
+ f":open_file_folder: [link file://{directory}]{directory}",
+ guide_style="bold bright_blue",
+ )
+ walk_directory(pathlib.Path(directory), tree)
+ print(tree)