Skip to content

Commit 7a65e28

Browse files
iangowhas2k1
authored andcommitted
Move axes setup to coord classes
1 parent 87422c5 commit 7a65e28

5 files changed

Lines changed: 64 additions & 71 deletions

File tree

plotnine/coords/coord.py

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@
1010
if typing.TYPE_CHECKING:
1111
from typing import Any
1212

13+
from matplotlib.axes import Axes
1314
import numpy.typing as npt
1415
import pandas as pd
1516

16-
from plotnine import ggplot
17+
from plotnine import ggplot, theme
1718
from plotnine.iapi import labels_view, panel_view
1819
from plotnine.scales.scale import scale
1920
from plotnine.typing import (
@@ -115,13 +116,50 @@ def draw(self, axs: list) -> None:
115116
add elements such as polar grid lines.
116117
"""
117118

118-
def post_setup_ax(self, ax: Any) -> None:
119+
def setup_ax(
120+
self, ax: Axes, panel_params: panel_view, theme: theme
121+
) -> None:
119122
"""
120-
Hook called for each axes after set_limits_breaks_and_labels.
123+
Set limits, breaks and labels for one panel axes.
121124
122-
Override in subclasses to apply per-axes settings that must
123-
run after the facet has set tick positions and label padding.
125+
Subclasses can override this to customize axes setup, or call
126+
`super().setup_ax(...)` and add coordinate-specific behavior.
124127
"""
128+
from .._mpl.ticker import MyFixedFormatter
129+
130+
def _inf_to_none(
131+
t: tuple[float, float],
132+
) -> tuple[float | None, float | None]:
133+
"""
134+
Replace infinities with None
135+
"""
136+
a = t[0] if np.isfinite(t[0]) else None
137+
b = t[1] if np.isfinite(t[1]) else None
138+
return (a, b)
139+
140+
# limits
141+
ax.set_xlim(*_inf_to_none(panel_params.x.range))
142+
ax.set_ylim(*_inf_to_none(panel_params.y.range))
143+
144+
# breaks, labels
145+
ax.set_xticks(panel_params.x.breaks, panel_params.x.labels)
146+
ax.set_yticks(panel_params.y.breaks, panel_params.y.labels)
147+
148+
# minor breaks
149+
ax.set_xticks(panel_params.x.minor_breaks, minor=True)
150+
ax.set_yticks(panel_params.y.minor_breaks, minor=True)
151+
152+
# When you manually set the tick labels MPL changes the locator
153+
# so that it no longer reports the x & y positions
154+
# Fixes https://github.com/has2k1/plotnine/issues/187
155+
ax.xaxis.set_major_formatter(MyFixedFormatter(panel_params.x.labels))
156+
ax.yaxis.set_major_formatter(MyFixedFormatter(panel_params.y.labels))
157+
158+
pad_x = theme.get_margin("axis_text_x").pt.t
159+
pad_y = theme.get_margin("axis_text_y").pt.r
160+
161+
ax.tick_params(axis="x", which="major", pad=pad_x)
162+
ax.tick_params(axis="y", which="major", pad=pad_y)
125163

126164
def labels(self, cur_labels: labels_view) -> labels_view:
127165
"""

plotnine/coords/coord_radial.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from matplotlib.axes import Axes
1313
from matplotlib.projections.polar import PolarAxes
1414

15+
from plotnine import theme
1516
from plotnine.iapi import panel_view
1617
from plotnine.scales.scale import scale
1718

@@ -221,8 +222,8 @@ def setup_panel_params(self, scale_x: scale, scale_y: scale) -> panel_view:
221222
x_updates["labels"] = theta_labels
222223

223224
# Partial arc: x panel range must match [arc_lo, arc_hi] so that
224-
# set_limits_breaks_and_labels calls ax.set_xlim(arc_lo, arc_hi) rather
225-
# than ax.set_xlim(0, 2π), which would override set_thetalim.
225+
# coord.setup_ax calls ax.set_xlim(arc_lo, arc_hi) rather than
226+
# ax.set_xlim(0, 2π), which would override set_thetalim.
226227
if arc_lo is not None:
227228
x_updates["limits"] = (arc_lo, arc_hi)
228229
x_updates["range"] = (arc_lo, arc_hi)
@@ -324,10 +325,13 @@ def draw(self, axs: list[Axes]) -> None:
324325
np.degrees(float(self.r_axis_inside))
325326
)
326327

327-
def post_setup_ax(self, ax: Axes) -> None:
328+
def setup_ax(
329+
self, ax: Axes, panel_params: panel_view, theme: theme
330+
) -> None:
328331
"""
329-
Apply theta label pad after facet has set tick positions and padding.
332+
Apply theta label pad after setting tick positions and padding.
330333
"""
334+
super().setup_ax(ax, panel_params, theme)
331335
if self.theta_labels or self.end is not None:
332336
ax.tick_params(axis="x", pad=self.theta_label_pad)
333337
# Allow geom_text labels to extend past the polar axes bounding box

plotnine/facets/facet.py

Lines changed: 1 addition & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from plotnine.coords.coord import coord
2727
from plotnine.facets.labelling import CanBeStripLabellingFunc
2828
from plotnine.facets.layout import Layout
29-
from plotnine.iapi import layout_details, panel_view
29+
from plotnine.iapi import layout_details
3030
from plotnine.layer import Layers
3131
from plotnine.mapping import Environment
3232
from plotnine.scales.scale import scale
@@ -303,60 +303,6 @@ def make_strips(self, layout_info: layout_details, ax: Axes) -> Strips:
303303
"""
304304
return Strips()
305305

306-
def set_limits_breaks_and_labels(self, panel_params: panel_view, ax: Axes):
307-
"""
308-
Add limits, breaks and labels to the axes
309-
310-
Parameters
311-
----------
312-
panel_params :
313-
range information for the axes
314-
ax :
315-
Axes
316-
"""
317-
from .._mpl.ticker import MyFixedFormatter
318-
319-
def _inf_to_none(
320-
t: tuple[float, float],
321-
) -> tuple[float | None, float | None]:
322-
"""
323-
Replace infinities with None
324-
"""
325-
a = t[0] if np.isfinite(t[0]) else None
326-
b = t[1] if np.isfinite(t[1]) else None
327-
return (a, b)
328-
329-
theme = self.theme
330-
331-
# limits
332-
ax.set_xlim(*_inf_to_none(panel_params.x.range))
333-
ax.set_ylim(*_inf_to_none(panel_params.y.range))
334-
335-
if typing.TYPE_CHECKING:
336-
assert callable(ax.set_xticks)
337-
assert callable(ax.set_yticks)
338-
339-
# breaks, labels
340-
ax.set_xticks(panel_params.x.breaks, panel_params.x.labels)
341-
ax.set_yticks(panel_params.y.breaks, panel_params.y.labels)
342-
343-
# minor breaks
344-
ax.set_xticks(panel_params.x.minor_breaks, minor=True)
345-
ax.set_yticks(panel_params.y.minor_breaks, minor=True)
346-
347-
# When you manually set the tick labels MPL changes the locator
348-
# so that it no longer reports the x & y positions
349-
# Fixes https://github.com/has2k1/plotnine/issues/187
350-
ax.xaxis.set_major_formatter(MyFixedFormatter(panel_params.x.labels))
351-
ax.yaxis.set_major_formatter(MyFixedFormatter(panel_params.y.labels))
352-
353-
pad_x = theme.get_margin("axis_text_x").pt.t
354-
pad_y = theme.get_margin("axis_text_y").pt.r
355-
356-
ax.tick_params(axis="x", which="major", pad=pad_x)
357-
ax.tick_params(axis="y", which="major", pad=pad_y)
358-
self.coordinates.post_setup_ax(ax)
359-
360306
def __deepcopy__(self, memo: dict[Any, Any]) -> facet:
361307
"""
362308
Deep copy without copying the dataframe and environment

plotnine/ggplot.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,7 @@ def _draw_breaks_and_labels(self):
567567
pidx = layout_info.panel_index
568568
ax = self.axs[pidx]
569569
panel_params = self.layout.panel_params[pidx]
570-
self.facet.set_limits_breaks_and_labels(panel_params, ax)
570+
self.coordinates.setup_ax(ax, panel_params, self.theme)
571571

572572
# Remove unnecessary ticks and labels
573573
if not layout_info.axis_x:

tests/test_coord_polar.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
element_line,
1111
geom_col,
1212
geom_point,
13+
geom_text,
1314
ggplot,
1415
theme,
1516
)
@@ -291,14 +292,18 @@ def test_coord_radial_draw_float_r_axis_position():
291292
plt.close(fig)
292293

293294

294-
def test_coord_radial_post_setup_ax_sets_pad_and_unclips_text():
295-
coord = coord_radial(theta_label_pad=17, theta_labels=True)
296-
fig, ax = plt.subplots(subplot_kw={"projection": "polar"})
297-
text = ax.text(0, 1, "label", clip_on=True)
295+
def test_coord_radial_setup_ax_sets_pad_and_unclips_text():
296+
data = pd.DataFrame({"x": [1], "y": [1], "label": ["label"]})
297+
p = (
298+
ggplot(data, aes("x", "y", label="label"))
299+
+ geom_text()
300+
+ coord_radial(theta_label_pad=17, theta_labels=True)
301+
)
298302

303+
fig = p.draw()
299304
try:
300-
coord.post_setup_ax(ax)
305+
ax = fig.axes[0]
301306
assert ax.xaxis.get_tick_params()["pad"] == 17
302-
assert not text.get_clip_on()
307+
assert not ax.texts[0].get_clip_on()
303308
finally:
304309
plt.close(fig)

0 commit comments

Comments
 (0)