Skip to content

Wire scalebar_dx/scalebar_units into pl.show()#648

Merged
timtreis merged 7 commits intomainfrom
fix/issue-614-scalebar
May 8, 2026
Merged

Wire scalebar_dx/scalebar_units into pl.show()#648
timtreis merged 7 commits intomainfrom
fix/issue-614-scalebar

Conversation

@timtreis
Copy link
Copy Markdown
Member

@timtreis timtreis commented May 8, 2026

Summary

  • The scalebar machinery existed but was unreachable: pl.show() did not declare or forward scalebar_dx / scalebar_units, so any attempt raised TypeError. The downstream ScaleBar(dx=[...], units=[...]) call in _add_decorations_to_ax also passed broadcast lists where ScaleBar requires scalars, and ran once per render layer (so multi-layer plots would have stacked duplicates).
  • Adds three keyword-only params to show(): scalebar_dx: float | None = None, scalebar_units: str = "um", scalebar_params: dict | None = None (kwargs dict mirroring the existing colorbar_params idiom; names match scanpy/squidpy/matplotlib_scalebar).
  • Centralizes drawing in a new _draw_scalebar(ax, params, panel_idx) helper invoked once per axis at the tail of show()'s panel loop. Removes the broken scalebar block from _add_decorations_to_ax and the now-unused scalebar_params argument from _render_{shapes,points,images,labels} and _add_legend_and_colorbar.

UX rationale

SpatialData coordinate systems carry no unit metadata, so automatic detection of dx is impossible (unlike LazySlide, which reads MPP from WSI metadata). User-supplied dx is the only option — same constraint scanpy/squidpy operate under. scalebar_params instead of carving out individual styling kwargs keeps the signature tight and matches colorbar_params already in the same function.

Closes #614.

The scalebar machinery (ScalebarParams, _get_scalebar, ScaleBar import) was
present but unreachable: show() did not declare or forward scalebar_dx /
scalebar_units, so any user attempt raised TypeError. The downstream
ScaleBar(dx=[...], units=[...]) call in _add_decorations_to_ax also passed
broadcast lists where ScaleBar requires scalars, and ran once per render
layer (so multi-layer plots would have stacked duplicates).

- Add scalebar_dx, scalebar_units (default "um"), and scalebar_params
  (kwargs dict, mirroring colorbar_params) to show()'s signature.
- Centralize drawing in _draw_scalebar(ax, params, panel_idx); call it once
  per axis at the tail of show()'s panel loop.
- Drop the broken scalebar block + scalebar_dx/units kwargs from
  _add_decorations_to_ax; drop the now-unused scalebar_params arg from
  _render_{shapes,points,images,labels} and _add_legend_and_colorbar.
- Validate scalebar_dx/units/params types and sign in
  _validate_show_parameters.
- Add 9 non-visual regression tests + 2 visual tests (default and styled).
@timtreis timtreis changed the title Wire scalebar_dx/scalebar_units into pl.show() (closes #614) Wire scalebar_dx/scalebar_units into pl.show() May 8, 2026
timtreis added 3 commits May 8, 2026 15:19
Cover three orthogonal scalebar knobs that users routinely tune:
- frameon=False (no surrounding box)
- length_fraction + pad + border_pad (compact footprint)
- fixed_value + label (pin bar length, override displayed text)

Also drops a transient explanatory comment block above the non-visual
scalebar tests; the test names and docstrings are self-explanatory.
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 8, 2026

Codecov Report

❌ Patch coverage is 83.33333% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.83%. Comparing base (e1f8863) to head (2f3d4a0).
⚠️ Report is 8 commits behind head on main.

Files with missing lines Patch % Lines
src/spatialdata_plot/pl/utils.py 72.72% 3 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #648      +/-   ##
==========================================
+ Coverage   76.39%   76.83%   +0.43%     
==========================================
  Files          11       11              
  Lines        3237     3276      +39     
  Branches      759      773      +14     
==========================================
+ Hits         2473     2517      +44     
+ Misses        466      458       -8     
- Partials      298      301       +3     
Files with missing lines Coverage Δ
src/spatialdata_plot/pl/basic.py 85.93% <100.00%> (+0.44%) ⬆️
src/spatialdata_plot/pl/render.py 86.41% <ø> (-0.18%) ⬇️
src/spatialdata_plot/pl/render_params.py 86.76% <100.00%> (+0.06%) ⬆️
src/spatialdata_plot/pl/utils.py 67.19% <72.72%> (+0.75%) ⬆️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

timtreis added 3 commits May 8, 2026 15:51
Mirrors the colorbar_params / scalebar_params escape-hatch pattern. The
five flat legend_* kwargs continue to work unchanged; legend_params is
purely additive sugar so existing scripts and the wider scverse muscle
memory (legend_fontsize, legend_loc, na_in_legend) keep functioning.

Inside the dict, keys use matplotlib-native bare names (loc, fontsize,
fontweight, fontoutline, na_in_legend) rather than the prefixed flat
names — the dict label already provides the namespace, and bare keys
match matplotlib.legend.Legend documentation that users are most likely
to copy from. Unknown keys raise ValueError to surface typos early.

When the same option is set both as a flat kwarg and inside the dict,
the dict wins. No DeprecationWarning is emitted; this is Phase 1 of a
potential multi-phase migration but does not commit to deprecating the
flat kwargs.

Adds 7 tests covering dict form, override precedence, None no-op, and
validation (parametrized x4 over bad types and unknown keys).
matplotlib.legend.Legend natively uses 'loc' while Figure.colorbar and
matplotlib_scalebar both use 'location', which would force users to
remember a different key name for legend_params than for colorbar_params
and scalebar_params. We accept both spellings so the three escape-hatch
dicts read consistently; 'location' is documented as canonical and wins
when both are passed.

Inline comments at the merge site and the validation whitelist explain
the matplotlib quirk so future maintainers don't 'simplify' the alias
away.
@timtreis timtreis merged commit ea7d76e into main May 8, 2026
7 of 8 checks passed
@timtreis timtreis deleted the fix/issue-614-scalebar branch May 8, 2026 14:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

scalebar_dx / scalebar_units not wired into show() — scalebar feature is unreachable

2 participants