Skip to content

Commit 7d85258

Browse files
committed
Keep plot_spacer invisible when a theme is applied via & or *
Compose.__and__/__mul__ apply themes to items via item += rhs, which dispatches to ggplot.__iadd__ → theme.__radd__. For a complete theme that takes the "replace" branch, overwriting the spacer's theme_void() foundation so the spacer paints a panel. Override __iadd__ on plot_spacer to merge only plot_background into the existing theme_void(), and have __add__ delegate via deepcopy → __iadd__ so the merge logic lives in one place. plot_spacer.__add__ used to silently swallow any non-PlotAddable rhs, so spacer + plot was a no-op. Match ggplot.__add__'s contract: ggplot or Compose on the right composes into a Wrap, just like plot + plot. A theme still contributes only its plot_background fill into the spacer's theme_void() foundation. closes #1055
1 parent 537ecc3 commit 7d85258

4 files changed

Lines changed: 42 additions & 10 deletions

File tree

plotnine/composition/_plot_spacer.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
from __future__ import annotations
22

3-
from copy import deepcopy
3+
from typing import TYPE_CHECKING
44

55
from plotnine import element_rect, ggplot, theme, theme_void
66

7+
if TYPE_CHECKING:
8+
from typing_extensions import Self
9+
10+
from plotnine.ggplot import PlotAddable
11+
712

813
class plot_spacer(ggplot):
914
"""
@@ -17,6 +22,12 @@ class plot_spacer(ggplot):
1722
1823
The color can also be modified by adding a [](`~plotnine.theme`)
1924
and setting the [](`~plotnine.themes.themeable.plot_background`).
25+
alpha :
26+
Opacity of the background fill, between 0 (transparent) and 1
27+
(opaque). The default leaves the area transparent.
28+
29+
The opacity can also be modified by adding a [](`~plotnine.theme`)
30+
and setting the [](`~plotnine.themes.themeable.plot_background`).
2031
2132
See Also
2233
--------
@@ -33,21 +44,26 @@ def __init__(
3344
| tuple[float, float, float, float]
3445
| None
3546
) = None,
47+
alpha: float | None = None,
3648
):
3749
super().__init__()
38-
self.theme = theme_void()
39-
if fill:
40-
self.theme += theme(plot_background=element_rect(fill=fill))
50+
self.theme = theme_void() + theme(
51+
plot_background=element_rect(fill=fill, alpha=alpha)
52+
)
4153

42-
def __add__(self, rhs) -> plot_spacer: # pyright: ignore[reportIncompatibleMethodOverride]
54+
def __iadd__(self, rhs: PlotAddable | list[PlotAddable] | None) -> Self:
4355
"""
4456
Add to spacer
4557
46-
All added objects are no ops except the `plot_background` in
47-
in a theme.
58+
Only the `plot_background` of a [](`~plotnine.theme`) on the
59+
right-hand side is merged into the spacer's own theme; every
60+
other addable is dropped so the spacer keeps its blank
61+
appearance.
4862
"""
49-
self = deepcopy(self)
5063
if isinstance(rhs, theme):
5164
fill = rhs.getp(("plot_background", "facecolor"))
52-
self.theme += theme(plot_background=element_rect(fill=fill))
65+
alpha = rhs.getp(("plot_background", "alpha"))
66+
self.theme += theme(
67+
plot_background=element_rect(fill=fill, alpha=alpha)
68+
)
5369
return self
4.04 KB
Loading
7.07 KB
Loading

tests/test_plot_composition.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
)
1313
from plotnine._utils.yippie import geom as g
1414
from plotnine._utils.yippie import legend, plot, rotate, tag
15-
from plotnine.composition import plot_annotation, plot_layout
15+
from plotnine.composition import plot_annotation, plot_layout, plot_spacer
1616

1717

1818
def test_basic_horizontal_align_resize():
@@ -435,3 +435,19 @@ def _make_footer(color):
435435
)
436436
p = ((p1 | p2) / p3) + ann
437437
assert p == "footers"
438+
439+
440+
def test_spacer_under_and_theme():
441+
# theme_gray is applied to all plots,
442+
# for the spacer is only applied to the plot background.
443+
p1 = plot.red
444+
p2 = plot.green
445+
p = (p1 | p2) / (p1 | plot_spacer() | p2) & theme_gray()
446+
assert p == "spacer_under_and_theme"
447+
448+
449+
def test_spacer_first_plus_plot():
450+
# spacer + plot composes into a Wrap, matching ggplot + ggplot.
451+
# Putting the spacer on the LHS exercises plot_spacer.__add__.
452+
p = plot_spacer() + plot.red
453+
assert p == "spacer_first_plus_plot"

0 commit comments

Comments
 (0)