spacr.figures.style

Shared publication and on-screen styling for spaCR figures.

The module provides a fixed data palette, theme-aware foreground colors, and scoped Matplotlib style contexts. Data colors retain the same meaning across panels, while text and axes adapt to screen or print backgrounds. Styles are applied without mutating process-wide rcParams.

Text and lines are two colours, not one. Titles, axis labels, tick labels, annotations and legend entries are TEXT; axis spines and tick marks are LINES. Each follows the matching user preference when one has been chosen and the measured house ink otherwise, so an untouched settings store draws the published look unchanged.

Classes

Palette

Fixed data colors shared by all house-style figure panels.

Functions

annotate(→ None)

An in-panel note: an n, a correlation coefficient, a count.

chosen_ink(→ Optional[str])

The TEXT colour the user picked, or None while it follows the theme.

chosen_line_ink(→ Optional[str])

The LINE colour the user picked, or None while it follows the text.

descriptor(→ None)

Two to four lower-case words above the axes.

figure_style([target])

Draw inside the house style, and put the globals back afterwards.

hide_unused(→ None)

Turn off axes a grid allocated and no panel filled.

panel_letter(→ None)

A bold upper-case letter at the panel's top left. No period.

rc(→ dict)

The rcParams for the house style, as a plain dict.

reference_line(→ None)

A threshold, a limit of detection, a 1:1 diagonal.

resolve_ink(→ str)

The TEXT colour for where this figure is going.

resolve_label_ground(→ str)

The colour behind a text label, for where this figure is going.

resolve_line_ink(→ str)

Resolve the colour used for axis spines and tick marks.

rotate_ticks(→ None)

Long categorical labels rotate 45 degrees, right-aligned.

text_legend(→ None)

A legend as coloured TEXT, with no marker and no frame.

theme_target(→ str)

'screen' or 'print', from the user's own figure preferences.

user_overrides(→ dict)

Return explicit preference changes that override the house style.

Module Contents

class spacr.figures.style.Palette[source]

Fixed data colors shared by all house-style figure panels.

spacr.figures.style.annotate(ax, text: str, *, x: float = 0.02, y: float = 0.97, colour: str | None = None, ha: str = 'left', va: str = 'top') → None[source]

An in-panel note: an n, a correlation coefficient, a count.

No frame, no box. The published figures never draw one.

Parameters:
  • ax – Matplotlib axes that receives the note.

  • text – annotation text to draw inside the axes.

spacr.figures.style.chosen_ink() → str | None[source]

The TEXT colour the user picked, or None while it follows the theme.

Reads the stored TOKEN, never the resolved pair. A resolved pair has already lost the one bit that matters here — whether the user chose the colour or the theme produced it — so seeding the house style from get_figure_colors() would replace the measured publication ink with whatever the current theme happens to answer, for every user who has never opened the dialog.

None when there is no settings store at all: a headless render or a bare unit run, where the house style is the only answer there is.

spacr.figures.style.chosen_line_ink() → str | None[source]

The LINE colour the user picked, or None while it follows the text.

The user’s second colour control: “line color which should change the color of all lines including axis lines and ticks”. Stored as a token like the text half, and read as a token for the same reason — chosen_ink() says which.

spacr.figures.style.descriptor(ax, text: str) → None[source]

Two to four lower-case words above the axes.

NOT a sentence title. The axis labels carry the content; a descriptor only says which condition this panel is.

Parameters:
  • ax – Matplotlib axes whose title is set.

  • text – short descriptor to place above the axes.

spacr.figures.style.figure_style(target: str = 'screen', **kwargs)[source]

Draw inside the house style, and put the globals back afterwards.

THE ONLY SUPPORTED WAY TO APPLY THIS STYLE.

with figure_style("print"):
    figure = build_volcano(results)
figure.savefig(path)          # already styled; the globals are back

A plain rcParams.update would leak: spaCR draws figures from a long-lived GUI, so a style applied once applies to every figure drawn afterwards, in every other module, until the process exits. That failure mode has already cost this repository a day.

spacr.figures.style.hide_unused(axes: Iterable) → None[source]

Turn off axes a grid allocated and no panel filled.

An empty framed box in a figure sheet reads as a panel that failed to draw, which is worse than a gap.

Parameters:

axes – iterable of unused Matplotlib axes to turn off.

spacr.figures.style.panel_letter(ax, letter: str, dx: float = -0.16, dy: float = 1.06) → None[source]

A bold upper-case letter at the panel’s top left. No period.

Sized from the measured 1.9-2.2x of the axis-label tier.

Parameters:
  • ax – Matplotlib axes that receives the panel label.

  • letter – panel identifier, converted to upper case.

spacr.figures.style.rc(target: str = 'screen', *, frame: str = 'L', ink: str | None = None, line: str | None = None, ground: str | None = None, kind: str | None = None) → dict[source]

The rcParams for the house style, as a plain dict.

Returned rather than applied, so a caller can hand it to figure_style() or to plt.rc_context directly. Nothing in this module ever mutates the global rcParams.

Parameters:
  • frame – 'L' draws the left and bottom spines only (the Cell figures); 'box' draws all four (Nature Microbiology). Pick one per figure and hold it — box reads better when panels are small and dense, L when they are sparse.

  • ink – the TEXT colour — titles, labels, tick labels, annotations.

  • line – the LINE colour — the axis spines and the tick marks. Falls back to ink when nobody has said otherwise, which is what the figures did before the two were separable.

  • ground – the figure and axes background. Defaults to transparent, which lets the GUI theme show through.

  • kind – which graph kind this is, so the user’s PER-GRAPH preference for it can be applied on top. See user_overrides().

spacr.figures.style.reference_line(ax, *, x=None, y=None, label: str = '', colour: str | None = None) → None[source]

A threshold, a limit of detection, a 1:1 diagonal.

Thin, dashed and grey — never bold, never coloured. A reference is not a result and must not compete with one.

Parameters:

ax – Matplotlib axes on which to draw the reference line.

spacr.figures.style.resolve_ink(target: str = 'screen', ink: str | None = None) → str[source]

The TEXT colour for where this figure is going.

Titles, axis labels, tick LABELS, annotations and legend entries. The axis spines and the tick MARKS are lines, and they have their own resolver — see resolve_line_ink().

Parameters:
  • target – 'screen' for the GUI, 'print' for a file that will be looked at on paper or in a white-page viewer.

  • ink – an explicit override, which always wins.

spacr.figures.style.resolve_label_ground(target: str = 'screen', ground: str | None = None) → str[source]

The colour behind a text label, for where this figure is going.

Parameters:
  • target – 'screen' for the GUI, 'print' for a file.

  • ground – an explicit override, which always wins.

spacr.figures.style.resolve_line_ink(target: str = 'screen', ink: str | None = None, line: str | None = None) → str[source]

Resolve the colour used for axis spines and tick marks.

Textual elements, including tick labels, use resolve_ink(); the separate resolvers allow line and text colours to be configured independently. If no line colour is configured, the resolved text colour preserves the earlier single-ink behavior. Data-series colours remain governed by ROLES and per-figure styling.

Parameters:
  • target – Output target passed to resolve_ink() when a fallback is required.

  • ink – Explicit text colour used as the fallback for line work.

  • line – Explicit line colour, which takes precedence over all other values.

Returns:

Resolved line colour.

spacr.figures.style.rotate_ticks(ax, degrees: int = 45) → None[source]

Long categorical labels rotate 45 degrees, right-aligned.

Parameters:

ax – Matplotlib axes whose x tick labels are rotated.

spacr.figures.style.text_legend(ax, entries: Sequence, x: float = 0.02, y: float = 0.97, dy: float = 0.075) → None[source]

A legend as coloured TEXT, with no marker and no frame.

What the published figures do. A framed legend with sample markers costs a corner of the axes and adds a box the style has no other boxes to match.

Parameters:
  • ax – matplotlib axes that receives the labels in axes-relative coordinates.

  • entries – [(label, colour), ...].

spacr.figures.style.theme_target() → str[source]

'screen' or 'print', from the user’s own figure preferences.

The GROUND decides this, and only the ground: it is the question “what is this figure going to sit on”, which is what picks between the two measured house inks. The colours the user may have CHOSEN are a different question and are read where they are used — chosen_ink() for the text, chosen_line_ink() for the lines — because a chosen colour outranks whichever house ink this returns.

Falls back to 'screen' when there is no settings store — a headless render or a bare unit test — because spaCR’s themes are dark and ink that is slightly wrong is better than ink that is invisible.

spacr.figures.style.user_overrides(kind: str | None = None) → dict[source]

Return explicit preference changes that override the house style.

Only settings that differ from spaCR’s figure defaults are returned. This preserves the house style for untouched preferences while allowing general and graph-specific choices to take precedence.

Parameters:

kind (str or None, default=None) – Graph kind from spacr.figure_style.GRAPH_KINDS. None uses only the general preference layer.

Returns:

dict – Matplotlib rcParams overrides. An empty dictionary is returned when no preference differs or preferences cannot be read safely.