Source code for phenotypic.sdk_.viz.figures._mpl_theme

"""Matplotlib rcParams mirror of the PhenoTypic chart theme.

The plotly side lives in :mod:`._theme`; this module carries the matplotlib
equivalent from ``DESIGN.md`` "07 -- Code Integration" (the ``rcParams`` block):
the Okabe-Ito ``axes.prop_cycle``, navy-tinted grid / edge colors, hidden top /
right spines, and the role fonts (IBM Plex Sans body, JetBrains Mono numerics).

``matplotlib`` is imported lazily inside the functions so importing this module
(and the ``phenotypic.sdk_.viz.figures`` package) stays cheap for callers that only
need the plotly theme.
"""
from __future__ import annotations

from contextlib import contextmanager
from typing import Any, Iterator

from ._theme import AXIS, BODY, GRID, MUTED, NAVY, OKABE_ITO, WHITE

__all__ = ["phenotypic_rc", "phenotypic_mpl_context"]


[docs] def phenotypic_rc() -> dict[str, Any]: """Return the PhenoTypic matplotlib ``rcParams`` dict (DESIGN.md "07"). Suitable for ``matplotlib.rcParams.update(...)`` or ``matplotlib.rc_context(phenotypic_rc())``. Numeric tick labels render in JetBrains Mono; axis labels / titles in IBM Plex Sans. The Okabe-Ito series order anchors ``axes.prop_cycle`` so the first plotted series is brand navy. Returns: A fresh ``dict`` of rcParam name -> value. """ from cycler import cycler # Drop the overflow "ink" black (index 7) so the prop_cycle is the six # categorical series + the vermilion error color, matching the plotly # ``colorway`` intent. prop_colors = list(OKABE_ITO[:7]) return { "axes.prop_cycle": cycler(color=prop_colors), "figure.facecolor": "#FBFEF8", "axes.facecolor": WHITE, "axes.edgecolor": AXIS, "axes.grid": True, "grid.color": GRID, "grid.linewidth": 0.8, "axes.spines.top": False, "axes.spines.right": False, "font.family": "sans-serif", "font.sans-serif": ["IBM Plex Sans", "Helvetica Neue", "Arial"], "font.monospace": ["JetBrains Mono", "DejaVu Sans Mono", "Courier New"], "axes.labelcolor": BODY, "axes.titlecolor": NAVY, "axes.titleweight": "600", "axes.titlesize": 11, "axes.labelsize": 9, # Tick labels are numeric data -> render in the mono family. "xtick.color": MUTED, "ytick.color": MUTED, "xtick.labelsize": 8, "ytick.labelsize": 8, }
[docs] @contextmanager def phenotypic_mpl_context() -> Iterator[None]: """Context manager applying :func:`phenotypic_rc` via ``rc_context``. Use around figure construction so the theme is scoped and never leaks into a caller's global ``rcParams``:: with phenotypic_mpl_context(): fig = analyzer.show() fig.savefig(buf, format="png") Yields: ``None``; the themed rcParams are active for the duration of the block. """ import matplotlib as mpl with mpl.rc_context(phenotypic_rc()): yield