mirror of https://github.com/Textualize/rich.git
added transient switch to progress and live render
This commit is contained in:
parent
f9ac1c2f54
commit
c6c197da56
|
@ -5,7 +5,11 @@ 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).
|
||||
|
||||
## [2.0.2] - Unreleased
|
||||
## [2.1.0] - Unreleased
|
||||
|
||||
### Added
|
||||
|
||||
- Added 'transient' option to Progress
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -470,4 +474,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
### Added
|
||||
|
||||
- First official release, API still bto be stabilized
|
||||
- First official release, API still to be stabilized
|
||||
|
|
|
@ -44,13 +44,24 @@ Here's a simple example::
|
|||
progress.update(task3, advance=0.9)
|
||||
time.sleep(0.02)
|
||||
|
||||
Starting Tasks
|
||||
Transient progress
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Normally when you exit the progress context manager (or call :meth:`~rich.progress.Progress.stop`) the last refreshed display remains in the terminal with the cursor on the following line. You can also make the progress display disappear on exit by setting ``transient=False`` on the Progress constructor. Here's an example
|
||||
|
||||
with Progress(transient=False) as progress:
|
||||
task = progress.add_task("Working", total=100)
|
||||
do_work(task)
|
||||
|
||||
Transient progress displays are useful if you want more minimal output in the terminal when tasks are complete.
|
||||
|
||||
Starting tasks
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
When you add a task it is automatically *started* which means it will show a progress bar at 0% and the time remaining will be calculated from the current time. Occasionally this may not work well if there is a long delay before you can start updating progress, you may need to wait for a response from a server, or count files in a directory (for example) before you can begin tracking progress. In these cases you can call :meth:`~rich.progress.Progress.add_task` with ``start=False`` which will display a pulsing animation that lets the user know something is working. When you have the number of steps you can call :meth:`~rich.progress.Progress.start_task` which will display the progress bar at 0%, then :meth:`~rich.progress.Progress.update` as normal.
|
||||
|
||||
|
||||
Updating Tasks
|
||||
Updating tasks
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
When you add a task you get back a `Task ID`. Use this ID to call :meth:`~rich.progress.Progress.update` whenever you have completed some work, or any information has changed.
|
||||
|
@ -58,7 +69,9 @@ When you add a task you get back a `Task ID`. Use this ID to call :meth:`~rich.p
|
|||
Auto refresh
|
||||
~~~~~~~~~~~~
|
||||
|
||||
By default, the progress information will auto refresh at 10 times a second. Refreshing in a predictable rate can make numbers more readable if they are updating very quickly. Auto refresh can also prevent excessive rendering to the terminal.
|
||||
By default, the progress information will refresh 10 times a second. Refreshing at a predictable rate can make numbers more readable if they are updating quickly. Auto refresh can also prevent excessive rendering to the terminal.
|
||||
|
||||
You can set the refresh rate with the ``refresh_per_second`` argument on the :class:`~rich.progress.Progress` constructor. You could set this to something lower than 10 if you know your updates will not be that frequent.
|
||||
|
||||
You can disable auto-refresh by setting ``auto_refresh=False`` on the constructor and call :meth:`~rich.progress.Progress.refresh` manually when there are updates to display.
|
||||
|
||||
|
@ -93,7 +106,6 @@ Print / log
|
|||
|
||||
When a progress display is running, printing or logging anything directly to the console will break the visuals. To work around this, the Progress class provides :meth:`~rich.progress.Progress.print` and :meth:`~rich.progress.Progress.log` which work the same as their counterparts on :class:`~rich.console.Console` but will move the cursor and refresh automatically -- ensure that everything renders properly.
|
||||
|
||||
|
||||
Extending
|
||||
~~~~~~~~~
|
||||
|
||||
|
@ -106,7 +118,6 @@ If the progress API doesn't offer exactly what you need in terms of a progress d
|
|||
def get_renderables(self):
|
||||
yield Panel(self.make_tasks_table(self.tasks))
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
name = "rich"
|
||||
homepage = "https://github.com/willmcgugan/rich"
|
||||
documentation = "https://rich.readthedocs.io/en/latest/"
|
||||
version = "2.0.1"
|
||||
version = "2.1.0"
|
||||
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
|
||||
authors = ["Will McGugan <willmcgugan@gmail.com>"]
|
||||
license = "MIT"
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
from typing import Optional
|
||||
|
||||
from .console import Console, ConsoleOptions, RenderableType, RenderResult
|
||||
from .jupyter import JupyterMixin
|
||||
from .measure import Measurement
|
||||
|
||||
|
||||
class Constrain:
|
||||
class Constrain(JupyterMixin):
|
||||
"""Constrain the width of a renderable to a given number of characters.
|
||||
|
||||
Args:
|
||||
|
|
|
@ -8,19 +8,31 @@ from ._loop import loop_last
|
|||
|
||||
|
||||
class LiveRender:
|
||||
"""Creates a renderable that may be updated.
|
||||
|
||||
Args:
|
||||
renderable (RenderableType): Any renderable object.
|
||||
style (StyleType, optional): An optional style to apply to the renderable. Defaults to "".
|
||||
"""
|
||||
|
||||
def __init__(self, renderable: RenderableType, style: StyleType = "") -> None:
|
||||
self.renderable = renderable
|
||||
self.style = style
|
||||
self._shape: Optional[Tuple[int, int]] = None
|
||||
|
||||
def set_renderable(self, renderable: RenderableType) -> None:
|
||||
"""Set a new renderable.
|
||||
|
||||
Args:
|
||||
renderable (RenderableType): Any renderable object, including str.
|
||||
"""
|
||||
self.renderable = renderable
|
||||
|
||||
def position_cursor(self) -> Control:
|
||||
"""Get control codes to move cursor to beggining of live render.
|
||||
|
||||
Returns:
|
||||
str: String containing control codes.
|
||||
Control: A control instance that may be printed.
|
||||
"""
|
||||
if self._shape is not None:
|
||||
_, height = self._shape
|
||||
|
@ -30,6 +42,17 @@ class LiveRender:
|
|||
return Control("\r\x1b[2K")
|
||||
return Control("")
|
||||
|
||||
def restore_cursor(self) -> Control:
|
||||
"""Get control codes to clear the render and restore the cursor to its previous position.
|
||||
|
||||
Returns:
|
||||
Control: A Control instance that may be printed.
|
||||
"""
|
||||
if self._shape is not None:
|
||||
_, height = self._shape
|
||||
return Control("\r" + "\x1b[1A\x1b[2K" * height)
|
||||
return Control("")
|
||||
|
||||
def __rich_console__(
|
||||
self, console: Console, options: ConsoleOptions
|
||||
) -> RenderResult:
|
||||
|
|
|
@ -50,7 +50,6 @@ def _parse(markup: str) -> Iterable[Tuple[int, Optional[str], Optional[Tag]]]:
|
|||
|
||||
"""
|
||||
position = 0
|
||||
normalize = Style.normalize
|
||||
for match in re_tags.finditer(markup):
|
||||
escape_open, escape_close, tag_text = match.groups()
|
||||
start, end = match.span()
|
||||
|
|
|
@ -50,6 +50,7 @@ def track(
|
|||
total: int = None,
|
||||
auto_refresh=True,
|
||||
console: Optional[Console] = None,
|
||||
transient: bool = False,
|
||||
get_time: Callable[[], float] = None,
|
||||
) -> Iterable[ProgressType]:
|
||||
"""Track progress of processing a sequence.
|
||||
|
@ -59,13 +60,19 @@ def track(
|
|||
description (str, optional): Description of task show next to progress bar. Defaults to "Working".
|
||||
total: (int, optional): Total number of steps. Default is len(sequence).
|
||||
auto_refresh (bool, optional): Automatic refresh, disable to force a refresh after each iteration. Default is True.
|
||||
transient: (bool, optional): Clear the progress on exit. Defaults to False.
|
||||
console (Console, optional): Console to write to. Default creates internal Console instance.
|
||||
|
||||
Returns:
|
||||
Iterable[ProgressType]: An iterable of the values in the sequence.
|
||||
|
||||
"""
|
||||
progress = Progress(auto_refresh=auto_refresh, console=console, get_time=get_time)
|
||||
progress = Progress(
|
||||
auto_refresh=auto_refresh,
|
||||
console=console,
|
||||
transient=transient,
|
||||
get_time=get_time,
|
||||
)
|
||||
|
||||
task_total = total
|
||||
if task_total is None:
|
||||
|
@ -369,6 +376,8 @@ class Progress:
|
|||
auto_refresh (bool, optional): Enable auto refresh. If disabled, you will need to call `refresh()`.
|
||||
refresh_per_second (int, optional): Number of times per second to refresh the progress information. Defaults to 10.
|
||||
speed_estimate_period: (float, optional): Period (in seconds) used to calculate the speed estimate. Defaults to 30.
|
||||
transient: (bool, optional): Clear the progress on exit. Defaults to False.
|
||||
get_time: (Callable, optional): A callable that gets the current time, or None to use time.monotonic. Defaults to None.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
|
@ -378,6 +387,7 @@ class Progress:
|
|||
auto_refresh: bool = True,
|
||||
refresh_per_second: int = 10,
|
||||
speed_estimate_period: float = 30.0,
|
||||
transient: bool = False,
|
||||
get_time: GetTimeCallable = None,
|
||||
) -> None:
|
||||
assert refresh_per_second > 0, "refresh_per_second must be > 0"
|
||||
|
@ -392,6 +402,7 @@ class Progress:
|
|||
self.auto_refresh = auto_refresh
|
||||
self.refresh_per_second = refresh_per_second
|
||||
self.speed_estimate_period = speed_estimate_period
|
||||
self.transient = transient
|
||||
self.get_time = get_time or monotonic
|
||||
self._tasks: Dict[TaskID, Task] = {}
|
||||
self._live_render = LiveRender(self.get_renderable())
|
||||
|
@ -449,6 +460,8 @@ class Progress:
|
|||
if self._refresh_thread is not None:
|
||||
self._refresh_thread.join()
|
||||
self._refresh_thread = None
|
||||
if self.transient:
|
||||
self.console.control(self._live_render.restore_cursor())
|
||||
|
||||
def __enter__(self) -> "Progress":
|
||||
self.start()
|
||||
|
@ -467,13 +480,13 @@ class Progress:
|
|||
"""[summary]
|
||||
|
||||
Args:
|
||||
sequence (Sequence[ProgressType]): [description]
|
||||
sequence (Sequence[ProgressType]): A sequence of values you want to iterate over and track progress.
|
||||
total: (int, optional): Total number of steps. Default is len(sequence).
|
||||
task_id: (TaskID): Task to track. Default is new task.
|
||||
description: (str, optional): Description of task, if new task is created.
|
||||
|
||||
Returns:
|
||||
Iterable[ProgressType]: [description]
|
||||
Iterable[ProgressType]: An iterable of values taken from the provided sequence.
|
||||
"""
|
||||
if total is None:
|
||||
if isinstance(sequence, Sized):
|
||||
|
@ -786,7 +799,7 @@ yield True, previous_value''',
|
|||
|
||||
examples = cycle(progress_renderables)
|
||||
|
||||
with Progress() as progress:
|
||||
with Progress(transient=True) as progress:
|
||||
|
||||
task1 = progress.add_task(" [red]Downloading", total=1000)
|
||||
task2 = progress.add_task(" [green]Processing", total=1000)
|
||||
|
|
Loading…
Reference in New Issue