Skip to content

Commit 0a121c9

Browse files
Test/update label sharing tests (#372)
* Refactor: Improve tick and label sharing logic This commit introduces a major refactoring of the tick and label sharing mechanism in UltraPlot. The previous implementation had complex and distributed logic for determining tick and label visibility, which was difficult to maintain and extend. This refactoring centralizes the logic within the `Figure` class, making it more robust and easier to understand. Key changes: - A new `_share_ticklabels` method in `Figure` now handles all tick label sharing. - The `_get_border_axes` method has been improved to be more accurate. - The `_Crawler` utility in `ultraplot/utils.py` has been rewritten to better handle complex layouts with panels and mixed axes types. - Redundant and complex logic has been removed from `CartesianAxes`, `GeoAxes`, and other modules. * Test: Update tests for new label sharing logic This commit updates the test suite to align with the new tick and label sharing mechanism. Key changes: - Added `fig.canvas.draw()` calls in numerous tests to ensure that the new, deferred label sharing logic is triggered before assertions. - Updated assertions in tests for `CartesianAxes`, `GeoAxes`, and subplots to match the expected behavior of the refactored implementation. - Added new tests to cover more complex scenarios with panels and mixed axes types. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix edge cases * stash * restore gridspec hidden parameter defaults * revert explicit axis limit setting * bump merge * use new grid * rm ticks for twin * add scale override * bump * auto update limits * propagate panels for geo properly * propagate panels for geo properly * init * propagate grid type mismatch * rm debug * bump test * default lim to auto * make mpl compatible and refactor into smaller functions * skip colorbars on crawl * forgot this chunk * restore test and remove debug * make panel sharing symmetric * add additional guards * more tests to check if guards are working * guard against non-rectilinear shares * Ensure that colorbars and legend do no interfere with layout * Check that colorbars do not interfere with layout * more stress tests * bump tests * fix spelling * bump tests * bump test * bump test * remove show * add simple share test --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent c297c43 commit 0a121c9

File tree

15 files changed

+1067
-315
lines changed

15 files changed

+1067
-315
lines changed

ultraplot/axes/base.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1545,6 +1545,8 @@ def shared(paxs):
15451545
iax._panel_sharex_group = True
15461546
iax._sharex_setup(bottom) # parent is bottom-most
15471547
paxs = shared(self._panel_dict["top"])
1548+
if paxs and self.figure._sharex > 0:
1549+
self._panel_sharex_group = True
15481550
for iax in paxs:
15491551
iax._panel_sharex_group = True
15501552
iax._sharex_setup(bottom)
@@ -1559,6 +1561,8 @@ def shared(paxs):
15591561
iax._panel_sharey_group = True
15601562
iax._sharey_setup(left) # parent is left-most
15611563
paxs = shared(self._panel_dict["right"])
1564+
if paxs and self.figure._sharey > 0:
1565+
self._panel_sharey_group = True
15621566
for iax in paxs:
15631567
iax._panel_sharey_group = True
15641568
iax._sharey_setup(left)
@@ -3261,6 +3265,27 @@ def _is_panel_group_member(self, other: "Axes") -> bool:
32613265
# Not in the same panel group
32623266
return False
32633267

3268+
def _label_key(self, side: str) -> str:
3269+
"""
3270+
Map requested side name to the correct tick_params key across mpl versions.
3271+
3272+
This accounts for the API change around Matplotlib 3.10 where labeltop/labelbottom
3273+
became first-class tick parameter keys. For older versions, these map to
3274+
labelright/labelleft respectively.
3275+
"""
3276+
from packaging import version
3277+
from ..internals import _version_mpl
3278+
3279+
# TODO: internal deprecation warning when we drop 3.9, we need to remove this
3280+
3281+
use_new = version.parse(str(_version_mpl)) >= version.parse("3.10")
3282+
if side == "labeltop":
3283+
return "labeltop" if use_new else "labelright"
3284+
if side == "labelbottom":
3285+
return "labelbottom" if use_new else "labelleft"
3286+
# "labelleft" and "labelright" are stable across versions
3287+
return side
3288+
32643289
def _is_ticklabel_on(self, side: str) -> bool:
32653290
"""
32663291
Check if tick labels are on for the specified sides.
@@ -3274,10 +3299,8 @@ def _is_ticklabel_on(self, side: str) -> bool:
32743299
label = "label1"
32753300
if side in ["labelright", "labeltop"]:
32763301
label = "label2"
3277-
for tick in axis.get_major_ticks():
3278-
if getattr(tick, label).get_visible():
3279-
return True
3280-
return False
3302+
3303+
return axis.get_tick_params().get(self._label_key(side), False)
32813304

32823305
@docstring._snippet_manager
32833306
def inset(self, *args, **kwargs):

ultraplot/axes/geo.py

Lines changed: 31 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -652,27 +652,16 @@ def _apply_axis_sharing(self):
652652
or to the *right* of the leftmost panel. But the sharing level used for
653653
the leftmost and bottommost is the *figure* sharing level.
654654
"""
655-
# Handle X axis sharing
656-
if self._sharex:
657-
self._handle_axis_sharing(
658-
source_axis=self._sharex._lonaxis,
659-
target_axis=self._lonaxis,
660-
)
661655

662-
# Handle Y axis sharing
663-
if self._sharey:
664-
self._handle_axis_sharing(
665-
source_axis=self._sharey._lataxis,
666-
target_axis=self._lataxis,
667-
)
656+
# Share interval x
657+
if self._sharex and self.figure._sharex >= 2:
658+
self._lonaxis.set_view_interval(*self._sharex._lonaxis.get_view_interval())
659+
self._lonaxis.set_minor_locator(self._sharex._lonaxis.get_minor_locator())
668660

669-
# This block is apart of the draw sequence as the
670-
# gridliner object is created late in the
671-
# build chain.
672-
if not self.stale:
673-
return
674-
if self.figure._get_sharing_level() == 0:
675-
return
661+
# Share interval y
662+
if self._sharey and self.figure._sharey >= 2:
663+
self._lataxis.set_view_interval(*self._sharey._lataxis.get_view_interval())
664+
self._lataxis.set_minor_locator(self._sharey._lataxis.get_minor_locator())
676665

677666
def _get_gridliner_labels(
678667
self,
@@ -691,38 +680,36 @@ def _toggle_gridliner_labels(
691680
labelright=None,
692681
geo=None,
693682
):
694-
# For BasemapAxes the gridlines are dicts with key as the coordinate and keys the line and label
695-
# We override the dict here assuming the labels are mut excl due to the N S E W extra chars
683+
"""
684+
Toggle visibility of gridliner labels for each direction.
685+
686+
Parameters
687+
----------
688+
labeltop, labelbottom, labelleft, labelright : bool or None
689+
Whether to show labels on each side. If None, do not change.
690+
geo : optional
691+
Not used in this method.
692+
"""
693+
# Ensure gridlines_major is fully initialized
696694
if any(i is None for i in self.gridlines_major):
697695
return
696+
698697
gridlabels = self._get_gridliner_labels(
699698
bottom=labelbottom, top=labeltop, left=labelleft, right=labelright
700699
)
701-
bools = [labelbottom, labeltop, labelleft, labelright]
702-
directions = "bottom top left right".split()
703-
for direction, toggle in zip(directions, bools):
700+
701+
toggles = {
702+
"bottom": labelbottom,
703+
"top": labeltop,
704+
"left": labelleft,
705+
"right": labelright,
706+
}
707+
708+
for direction, toggle in toggles.items():
704709
if toggle is None:
705710
continue
706711
for label in gridlabels.get(direction, []):
707-
label.set_visible(toggle)
708-
709-
def _handle_axis_sharing(
710-
self,
711-
source_axis: "GeoAxes",
712-
target_axis: "GeoAxes",
713-
):
714-
"""
715-
Helper method to handle axis sharing for both X and Y axes.
716-
717-
Args:
718-
source_axis: The source axis to share from
719-
target_axis: The target axis to apply sharing to
720-
"""
721-
# Copy view interval and minor locator from source to target
722-
723-
if self.figure._get_sharing_level() >= 2:
724-
target_axis.set_view_interval(*source_axis.get_view_interval())
725-
target_axis.set_minor_locator(source_axis.get_minor_locator())
712+
label.set_visible(bool(toggle) or toggle in ("x", "y"))
726713

727714
@override
728715
def draw(self, renderer=None, *args, **kwargs):
@@ -1441,6 +1428,7 @@ def _is_ticklabel_on(self, side: str) -> bool:
14411428
"""
14421429
# Deal with different cartopy versions
14431430
left_labels, right_labels, bottom_labels, top_labels = self._get_side_labels()
1431+
14441432
if self.gridlines_major is None:
14451433
return False
14461434
elif side == "labelleft":

ultraplot/axes/polar.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
"""
55
import inspect
66

7+
try:
8+
from typing import override
9+
except:
10+
from typing_extensions import override
11+
712
import matplotlib.projections.polar as mpolar
813
import numpy as np
914

@@ -138,6 +143,11 @@ def __init__(self, *args, **kwargs):
138143
for axis in (self.xaxis, self.yaxis):
139144
axis.set_tick_params(which="both", size=0)
140145

146+
@override
147+
def _apply_axis_sharing(self):
148+
# Not implemented. Silently pass
149+
return
150+
141151
def _update_formatter(self, x, *, formatter=None, formatter_kw=None):
142152
"""
143153
Update the gridline label formatter.

ultraplot/axes/shared.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,6 @@ def _share_axis_with(self, other: "Axes", *, which: str):
212212
)
213213

214214
self._shared_axes[which].join(self, other)
215-
216215
# Get axis objects
217216
this_axis = getattr(self, f"{which}axis")
218217
other_axis = getattr(other, f"{which}axis")
@@ -227,7 +226,7 @@ def _share_axis_with(self, other: "Axes", *, which: str):
227226
get_autoscale = getattr(other, f"get_autoscale{which}_on")
228227

229228
lim0, lim1 = limits
230-
set_lim(lim0, lim1, emit=False, auto=get_autoscale())
229+
set_lim(lim0, lim1, emit=False, auto=get_autoscale()) # Set scale
231230

232-
# Set scale
231+
# Override scale
233232
this_axis._scale = other_axis._scale

0 commit comments

Comments
 (0)