mirror of https://github.com/Textualize/rich.git
theme improvements and docs
This commit is contained in:
parent
c55b357f37
commit
433ee13234
|
@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
- Added tab_size to Console and Text
|
||||
- Added protocol.is_renderable for runtime check
|
||||
- Added emoji switch to Console
|
||||
- Added inherit boolean to Theme
|
||||
- Made Console thread safe, with a thread local buffer
|
||||
|
||||
### Changed
|
||||
|
||||
|
|
|
@ -63,20 +63,35 @@ It is slightly quicker to construct a Style class like this, since a style defin
|
|||
You can parse a style definition explicitly with the :meth:`~rich.style.Style.parse` method.
|
||||
|
||||
|
||||
Custom Styles
|
||||
-------------
|
||||
Style Themes
|
||||
------------
|
||||
|
||||
While it is possible to explicitly specify styles each time you use them, it is discouraged. Rich allows you to predefine style names which you can use in place on an explicit style. For instance, you may want to define styles for info, warning, danger etc.
|
||||
If you re-use styles it can be a maintenance headache if you ever want to modify an attribute or color -- you would have to change every line where the style is used. Rich provides a :class:`rich.theme.Theme` class which you can use to pre-define styles, so if you ever need to modify a style you only need change one file.
|
||||
|
||||
You can define a mapping of names to styles in the :class:`~rich.console.Console` constructor. These styles will be merged in to the default styles, potentially overwriting them. Here's an example::
|
||||
Style themes can also make your code more semantic, for instance a style called ``"warning"`` better expresses intent that ``"italic magenta underline"``.
|
||||
|
||||
To use a style theme, construct a :class:`rich.theme.Theme` instance and pass it to the :class:`~rich.console.Console` constructor. Here's an example::
|
||||
|
||||
from rich.console import Console
|
||||
styles = {
|
||||
from rich.theme import Theme
|
||||
custom_theme = Theme({
|
||||
"info" : Style.parse("dim cyan"),
|
||||
"warning": Style.parse("magenta"),
|
||||
"danger": Style.parse("bold red")
|
||||
}
|
||||
console = Console(styles=styles)
|
||||
})
|
||||
console = Console(theme=custom_theme)
|
||||
console.print("This is information", style="info")
|
||||
console.print("Something terrible happened!", style="danger")
|
||||
|
||||
You can also use these custom styles via markup. For example::
|
||||
|
||||
console.print("[warning]The pod bay doors are locked[/warning]")
|
||||
|
||||
If you prefer you can write your styles in an external config file rather than in Python. Here's an example of the format::
|
||||
|
||||
[styles]
|
||||
info = dim cyan
|
||||
warning = magenta
|
||||
danger bold red
|
||||
|
||||
You can read these files with the :method:`~rich.theme.Theme.read` method.
|
||||
|
|
|
@ -12,6 +12,7 @@ import platform
|
|||
import re
|
||||
import shutil
|
||||
import sys
|
||||
import threading
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
|
@ -286,7 +287,7 @@ class Console:
|
|||
else:
|
||||
self._color_system = COLOR_SYSTEMS[color_system]
|
||||
|
||||
self.buffer: List[Segment] = []
|
||||
self._thread_locals = threading.local()
|
||||
self._buffer_index = 0
|
||||
self._record_buffer: List[Segment] = []
|
||||
|
||||
|
@ -302,6 +303,14 @@ class Console:
|
|||
def __repr__(self) -> str:
|
||||
return f"<console width={self.width} {str(self._color_system)}>"
|
||||
|
||||
@property
|
||||
def _buffer(self) -> List[Segment]:
|
||||
"""Get a thread local buffer."""
|
||||
buffer = getattr(self._thread_locals, "buffer", None)
|
||||
if buffer is None:
|
||||
buffer = self._thread_locals.buffer = []
|
||||
return buffer
|
||||
|
||||
def _detect_color_system(self,) -> Optional[ColorSystem]:
|
||||
"""Detect color system from env vars."""
|
||||
if not self.is_terminal:
|
||||
|
@ -423,7 +432,7 @@ class Console:
|
|||
|
||||
assert count >= 0, "count must be >= 0"
|
||||
if count:
|
||||
self.buffer.append(Segment("\n" * count))
|
||||
self._buffer.append(Segment("\n" * count))
|
||||
self._check_buffer()
|
||||
|
||||
def _render(
|
||||
|
@ -771,7 +780,7 @@ class Console:
|
|||
)
|
||||
|
||||
render_options = self.options
|
||||
extend = self.buffer.extend
|
||||
extend = self._buffer.extend
|
||||
render = self.render
|
||||
with self.style(style):
|
||||
for renderable in renderables:
|
||||
|
@ -838,7 +847,7 @@ class Console:
|
|||
renderables.append(tabulate_mapping(locals_map, title="Locals"))
|
||||
|
||||
with self:
|
||||
self.buffer.extend(
|
||||
self._buffer.extend(
|
||||
self.render(
|
||||
self._log_render(self, renderables, path=path, line_no=line_no),
|
||||
self.options,
|
||||
|
@ -856,10 +865,10 @@ class Console:
|
|||
output: List[str] = []
|
||||
append = output.append
|
||||
color_system = self._color_system
|
||||
buffer = self.buffer[:]
|
||||
buffer = self._buffer[:]
|
||||
if self.record:
|
||||
self._record_buffer.extend(buffer)
|
||||
del self.buffer[:]
|
||||
del self._buffer[:]
|
||||
for line in Segment.split_and_crop_lines(buffer, self.width, pad=False):
|
||||
for text, style in line:
|
||||
if style:
|
||||
|
|
|
@ -1,12 +1,25 @@
|
|||
import configparser
|
||||
from typing import Dict, IO
|
||||
|
||||
from .default_styles import DEFAULT_STYLES
|
||||
from .style import Style
|
||||
|
||||
|
||||
class Theme:
|
||||
def __init__(self, styles: Dict[str, Style] = None):
|
||||
self.styles = styles or {}
|
||||
"""An container for style information, used by :class:`~rich.console.Console`.
|
||||
|
||||
Args:
|
||||
styles (Dict[str, Style], optional): A mapping of style names on to styles. Defaults to None for empty styles.
|
||||
inherit (bool, optional): Switch to inherit default styles. Defaults to True.
|
||||
"""
|
||||
|
||||
def __init__(self, styles: Dict[str, Style] = None, inherit: bool = True):
|
||||
if inherit:
|
||||
self.styles = DEFAULT_STYLES
|
||||
else:
|
||||
self.styles = {}
|
||||
if styles is not None:
|
||||
self.styles.update(styles)
|
||||
|
||||
@property
|
||||
def config(self) -> str:
|
||||
|
@ -19,14 +32,35 @@ class Theme:
|
|||
return config
|
||||
|
||||
@classmethod
|
||||
def from_file(cls, config_file: IO[str], source: str = None) -> "Theme":
|
||||
def from_file(
|
||||
cls, config_file: IO[str], source: str = None, inherit: bool = True
|
||||
) -> "Theme":
|
||||
"""Load a theme from a text mode file.
|
||||
|
||||
Args:
|
||||
config_file (IO[str]): An open conf file.
|
||||
source (str, optional): The filename of the open file. Defaults to None.
|
||||
inherit (bool, optional): Switch to inherit default styles. Defaults to True.
|
||||
|
||||
Returns:
|
||||
Theme: A New theme instance.
|
||||
"""
|
||||
config = configparser.ConfigParser()
|
||||
config.read_file(config_file, source=source)
|
||||
styles = {name: Style.parse(value) for name, value in config.items("styles")}
|
||||
theme = Theme(styles)
|
||||
theme = Theme(styles, inherit=inherit)
|
||||
return theme
|
||||
|
||||
@classmethod
|
||||
def read(cls, path: str) -> "Theme":
|
||||
def read(cls, path: str, inherit: bool = True) -> "Theme":
|
||||
"""Read a theme from a path.
|
||||
|
||||
Args:
|
||||
path (str): Path to a config file readable by Python configparser module.
|
||||
inherit (bool, optional): Switch to inherit default styles. Defaults to True.
|
||||
|
||||
Returns:
|
||||
Theme: A new theme instance.
|
||||
"""
|
||||
with open(path, "rt") as config_file:
|
||||
return cls.from_file(config_file, source=path)
|
||||
return cls.from_file(config_file, source=path, inherit=inherit)
|
||||
|
|
Loading…
Reference in New Issue