Skip to content

Rich text

text

Rich text: styling a substring of a label.

Every place pyplotrs takes a label - titles, axis labels, tick labels, legend entries, Axes.text/annotate - also takes a rich-text object built from the helpers here, so one word can be bold, tinted, highlighted or struck through while the rest of the line is not::

import pyplotrs as pp

ax.set(title=pp.rich("Growth ", pp.bold("+42%", color="teal"),
                     " over ", pp.italic("6 months")))

The helpers nest, and an inner style wins over an outer one::

pp.bold("total ", pp.rich("(estimated)", weight="normal", color="#888"))

Styles. weight ("normal"/"bold"), style ("normal"/"italic"), color, bgcolor (a highlight panel behind the run), underline, strike, and either scale (a multiple of the label's own type size - usually what you want, since a title and a tick label are set at different sizes) or size (absolute points). Colors accept everything pyplotrs.theme.parse_color does, "C0" palette indices included, and are resolved against the figure's theme at draw time.

Math. A span may contain $...$; the span's weight and slant become the ambient face the math is set in, so pp.bold(r"$E = mc^2$") comes out bold throughout - variables included - rather than half-bold. Inside a single expression, use \textcolor{...}{...} and \colorbox{...}{...} to tint one term::

ax.set(xlabel=r"$\textcolor{C1}{\sigma} / \sqrt{N}$")

Kerning. Each run is shaped independently, so a kern pair that straddles a style boundary is lost - pp.rich("W", pp.bold("a")) sets a hair wider than "Wa". That is inherent to changing face mid-word and matches every other plotting library; it does not apply within a run.

Span dataclass

Span(parts: tuple[Any, ...], style: Mapping[str, Any])

A run of text plus the style to draw it in, and the node type every rich-text helper returns.

Build these with rich and its shorthands rather than directly. parts may hold plain strings and further Spans, so a span is a tree; style applies to everything under it that does not override the same key.

text property

text: str

The plain text of this span and everything under it, unstyled.

This is what a figure's accessible description, get_title() and anything else that needs characters rather than glyphs should use.

rich

rich(*parts: Any, **style: Any) -> Span

A styled span of parts (strings and/or nested spans).

With no style it is a plain container, which is how you concatenate differently-styled pieces into one label::

pp.rich("mean ", pp.bold("12.4"), " ± 0.3")

See the module docstring for the style keys.

Source code in python/pyplotrs/text.py
def rich(*parts: Any, **style: Any) -> Span:
    """A styled span of ``parts`` (strings and/or nested spans).

    With no ``style`` it is a plain container, which is how you concatenate
    differently-styled pieces into one label::

        pp.rich("mean ", pp.bold("12.4"), " ± 0.3")

    See the module docstring for the style keys.
    """
    return Span(tuple(parts), MappingProxyType(dict(style)))

bold

bold(*parts: Any, **style: Any) -> Span

rich(...) in the body family's real bold face (not a synthetic one).

Source code in python/pyplotrs/text.py
def bold(*parts: Any, **style: Any) -> Span:
    """``rich(...)`` in the body family's real bold face (not a synthetic one)."""
    return rich(*parts, **{"weight": "bold", **style})

italic

italic(*parts: Any, **style: Any) -> Span

rich(...) in the body family's real italic face.

Source code in python/pyplotrs/text.py
def italic(*parts: Any, **style: Any) -> Span:
    """``rich(...)`` in the body family's real italic face."""
    return rich(*parts, **{"style": "italic", **style})

underline

underline(*parts: Any, **style: Any) -> Span

rich(...) underlined, on the rule the face's own metrics specify.

Source code in python/pyplotrs/text.py
def underline(*parts: Any, **style: Any) -> Span:
    """``rich(...)`` underlined, on the rule the face's own metrics specify."""
    return rich(*parts, **{"underline": True, **style})

strike

strike(*parts: Any, **style: Any) -> Span

rich(...) struck through, on the rule the face's own metrics specify.

Source code in python/pyplotrs/text.py
def strike(*parts: Any, **style: Any) -> Span:
    """``rich(...)`` struck through, on the rule the face's own metrics specify."""
    return rich(*parts, **{"strike": True, **style})

mark

mark(*parts: Any, **style: Any) -> Span

rich(...) highlighted - drawn over a panel of bgcolor.

Pass bgcolor= to change the panel; color= still means the ink, as it does on every other helper. A dark page wants both::

pp.mark("peak", bgcolor="#22303f", color="#e6f0ff")

The panel spans the whole line's height, not each run's own ink, so several marks in one label line up instead of stepping up and down with their letters.

Source code in python/pyplotrs/text.py
def mark(*parts: Any, **style: Any) -> Span:
    """``rich(...)`` highlighted - drawn over a panel of ``bgcolor``.

    Pass ``bgcolor=`` to change the panel; ``color=`` still means the ink, as
    it does on every other helper. A dark page wants both::

        pp.mark("peak", bgcolor="#22303f", color="#e6f0ff")

    The panel spans the whole line's height, not each run's own ink, so several
    marks in one label line up instead of stepping up and down with their
    letters.
    """
    return rich(*parts, **{"bgcolor": _MARK_COLOR, **style})

plain

plain(s: Any) -> str

The unstyled text of s, whether it is a Span or already a string.

Use it wherever characters are wanted rather than glyphs - alt text, a filename, a dict key.

Source code in python/pyplotrs/text.py
def plain(s: Any) -> str:
    """The unstyled text of ``s``, whether it is a ``Span`` or already a string.

    Use it wherever characters are wanted rather than glyphs - alt text, a
    filename, a dict key.
    """
    return s.text if isinstance(s, Span) else str(s)