10000 Deprecate axes_divider.AxesLocator. · matplotlib/matplotlib@c9a674d · GitHub
[go: up one dir, main page]

Skip to content

Commit c9a674d

Browse files
committed
Deprecate axes_divider.AxesLocator.
The axes_divider module creates axes locator objects, allowing the following pattern. ``` divider = Divider(<divider-args>) # or a subclass of Divider locator = divider.new_locator(<locator-args>) ax.set_axes_locator(locator) # will call locator(ax, renderer) at draw time ``` `locator` is actually an AxesLocator instance, which gets passed `divider` and `<locator-args>`; `locator(ax, renderer)` then simply calls (something like) `divider.locate(<locator-args>, ax, renderer)`, i.e. the AxesLocator instance simply exists to hold arguments for calling back into a divider method. One issue of this approach is that all the communication between the Divider and the AxesLocator class becomes public API (e.g., the locate method), even though these are essentially internal implementation details. This makes e.g. tightening of the locate API need to go through deprecation cycles (6eeb9ba). Instead, this PR completely gets rid of the AxesLocator class, and lets new_locator return an essentially opaque callable object (a functools.partial over a private method of AxesLocator), for which the only documented API is "this can be used as an axes locator callable, i.e. passed to ax.set_axes.locator". This PR also clarifies the role of _xrefindex and _yrefindex and consolidates all their handling into append_size, letting new_horizontal and new_vertical call that method instead of manipulating these indices themselves.
1 parent 5a34696 commit c9a674d

File tree

4 files changed

+102
-56
lines changed

4 files changed

+102
-56
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
``axes_grid1.axes_divider`` API changes
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
The ``AxesLocator`` class is deprecated. The ``new_locator`` method of divider
5+
instances now instead returns an opaque callable (which can still be passed to
6+
``ax.set_axes_locator``).
7+
8+
``Divider.locate`` is deprecated; use ``Divider.new_locator(...)(ax, renderer)``
9+
instead.

galleries/users_explain/toolkits/axes_grid.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@
302302
horiz = [...] # Some other horizontal constraints.
303303
divider = Divider(fig, rect, horiz, vert)
304304
305-
then use `.Divider.new_locator` to create an `.AxesLocator` instance for a
305+
then use `.Divider.new_locator` to create an axes locator callable for a
306306
given grid entry::
307307
308308
locator = divider.new_locator(nx=0, ny=1) # Grid entry (1, 0).
@@ -311,7 +311,7 @@
311311
312312
ax.set_axes_locator(locator)
313313
314-
The `.AxesLocator` is a callable object that returns the location and size of
314+
The axes locator callable returns the location and size of
315315
the cell at the first column and the second row.
316316
317317
Locators that spans over multiple cells can be created with, e.g.::

lib/mpl_toolkits/axes_grid1/axes_divider.py

Lines changed: 83 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
Helper classes to adjust the positions of multiple axes at drawing time.
33
"""
44

5+
import functools
6+
57
import numpy as np
68

79
import matplotlib as mpl
@@ -99,6 +101,9 @@ def get_anchor(self):
99101
"""Return the anchor."""
100102
return self._anchor
101103

104+
def get_subplotspec(self):
105+
return None
106+
102107
def set_horizontal(self, h):
103108
"""
104109
Parameters
@@ -162,8 +167,44 @@ def _calc_offsets(sizes, k):
162167
# the resulting cumulative offset positions.
163168
return np.cumsum([0, *(sizes @ [k, 1])])
164169

170+
def new_locator(self, nx, ny, nx1=None, ny1=None):
171+
"""
172+
Return an axes locator callable for the specified cell.
173+
174+
Parameters
175+
----------
176+
nx, nx1 : int
177+
Integers specifying the column-position of the
178+
cell. When *nx1* is None, a single *nx*-th column is
179+
specified. Otherwise, location of columns spanning between *nx*
180+
to *nx1* (but excluding *nx1*-th column) is specified.
181+
ny, ny1 : int
182+
Same as *nx* and *nx1*, but for row positions.
183+
"""
184+
if nx1 is None:
185+
nx1 = nx + 1
186+
if ny1 is None:
187+
ny1 = ny + 1
188+
# append_size("left") adds a new size at the beginning of the
189+
# horizontal size lists; this shift transforms e.g.
190+
# new_locator(nx=2, ...) into effectively new_locator(nx=3, ...). To
191+
# take that into account, instead of recording nx, we record
192+
# nx-self._xrefindex, where _xrefindex is shifted by 1 by each
193+
# append_size("left"), and re-add self._xrefindex back to nx in
194+
# _locate, when the actual axes position is computed. Ditto for y.
195+
xref = self._xrefindex
196+
yref = self._yrefindex
197+
locator = functools.partial(
198+
self._locate, nx - xref, ny - yref, nx1 - xref, ny1 - yref)
199+
locator.get_subplotspec = self.get_subplotspec
200+
return locator
201+
202+
@_api.deprecated(
203+
"3.7", alternative="divider.new_locator(...)(ax, renderer)")
165204
def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None):
166205
"""
206+
Implementation of ``divider.new_locator().__call__``.
207+
167208
Parameters
168209
----------
169210
nx, nx1 : int
@@ -176,6 +217,25 @@ def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None):
176217
axes
177218
renderer
178219
"""
220+
xref = self._xrefindex
221+
yref = self._yrefindex
222+
return self._locate(
223+
nx - xref, (nx + 1 if nx1 is None else nx1) - xref,
224+
ny - yref, (ny + 1 if ny1 is None else ny1) - yref,
225+
axes, renderer)
226+
227+
def _locate(self, nx, ny, nx1, ny1, axes, renderer):
228+
"""
229+
Implementation of ``divider.new_locator().__call__``.
230+
231+
The axes locator callable returned by ``new_locator()`` is created as
232+
a `functools.partial` of this method with *nx*, *ny*, *nx1*, and *ny1*
233+
specifying the requested cell.
234+
"""
235+
nx += self._xrefindex
236+
nx1 += self._xrefindex
237+
ny += self._yrefindex
238+
ny1 += self._yrefindex
179239

180240
fig_w, fig_h = self._fig.bbox.size / self._fig.dpi
181241
x, y, w, h = self.get_position_runtime(axes, renderer)
@@ -211,25 +271,6 @@ def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None):
211271

212272
return mtransforms.Bbox.from_bounds(x1, y1, w1, h1)
213273

214-
def new_locator(self, nx, ny, nx1=None, ny1=None):
215-
"""
216-
Return a new `.AxesLocator` for the specified cell.
217-
218-
Parameters
219-
----------
220-
nx, nx1 : int
221-
Integers specifying the column-position of the
222-
cell. When *nx1* is None, a single *nx*-th column is
223-
specified. Otherwise, location of columns spanning between *nx*
224-
to *nx1* (but excluding *nx1*-th column) is specified.
225-
ny, ny1 : int
226-
Same as *nx* and *nx1*, but for row positions.
227-
"""
228-
return AxesLocator(
229-
self, nx, ny,
230-
nx1 if nx1 is not None else nx + 1,
231-
ny1 if ny1 is not None else ny + 1)
232-
233274
def append_size(self, position, size):
234275
_api.check_in_list(["left", "right", "bottom", "top"],
235276
position=position)
@@ -264,6 +305,7 @@ def add_auto_adjustable_area(self, use_axes, pad=0.1, adjust_dirs=None):
264305
self.append_size(d, Size._AxesDecorationsSize(use_axes, d) + pad)
265306

266307

308+
@_api.deprecated("3.7")
267309
class AxesLocator:
268310
"""
269311
A callable object which returns the position and size of a given
@@ -400,24 +442,17 @@ def new_horizontal(self, size, pad=None, pack_start=False, **kwargs):
400442
"""
401443
if pad is None:
402444
pad = mpl.rcParams["figure.subplot.wspace"] * self._xref
445+
pos = "left" if pack_start else "right"
403446
if pad:
404447
if not isinstance(pad, Size._Base):
405448
pad = Size.from_any(pad, fraction_ref=self._xref)
406-
if pack_start:
407-
self._horizontal.insert(0, pad)
408-
self._xrefindex += 1
409-
else:
410-
self._horizontal.append(pad)
449+
self.append_size(pos, pad)
411450
if not isinstance(size, Size._Base):
412451
size = Size.from_any(size, fraction_ref=self._xref)
413-
if pack_start:
414-
self._horizontal.insert(0, size)
415-
self._xrefindex += 1
416-
locator = self.new_locator(nx=0, ny=self._yrefindex)
417-
else:
418-
self._horizontal.append(size)
419-
locator = self.new_locator(
420-
nx=len(self._horizontal) - 1, ny=self._yrefindex)
452+
self.append_size(pos, size)
453+
locator = self.new_locator(
454+
nx=0 if pack_start else len(self._horizontal) - 1,
455+
ny=self._yrefindex)
421456
ax = self._get_new_axes(**kwargs)
422457
ax.set_axes_locator(locator)
423458
return ax
@@ -432,24 +467,17 @@ def new_vertical(self, size, pad=None, pack_start=False, **kwargs):
432467
"""
433468
if pad is None:
434469
pad = mpl.rcParams["figure.subplot.hspace"] * self._yref
470+
pos = "bottom" if pack_start else "top"
435471
if pad:
436472
if not isinstance(pad, Size._Base):
437473
pad = Size.from_any(pad, fraction_ref=self._yref)
438-
if pack_start:
439-
self._vertical.insert(0, pad)
440-
self._yrefindex += 1
441-
else:
442-
self._vertical.append(pad)
474+
self.append_size(pos, pad)
443475
if not isinstance(size, Size._Base):
444476
size = Size.from_any(size, fraction_ref=self._yref)
445-
if pack_start:
446-
self._vertical.insert(0, size)
447-
self._yrefindex += 1
448-
locator = self.new_locator(nx=self._xrefindex, ny=0)
449-
else:
450-
self._vertical.append(size)
451-
locator = self.new_locator(
452-
nx=self._xrefindex, ny=len(self._vertical) - 1)
477+
self.append_size(pos, size)
478+
locator = self.new_locator(
479+
nx=self._xrefindex,
480+
ny=0 if pack_start else len(self._vertical) - 1)
453481
ax = self._get_new_axes(**kwargs)
454482
ax.set_axes_locator(locator)
455483
return ax
@@ -563,7 +591,7 @@ class HBoxDivider(SubplotDivider):
563591

564592
def new_locator(self, nx, nx1=None):
565593
"""
566-
Create a new `.AxesLocator` for the specified cell.
594+
Create an axes locator callable for the specified cell.
567595
568596
Parameters
569597
----------
@@ -573,10 +601,12 @@ def new_locator(self, nx, nx1=None):
573601
specified. Otherwise, location of columns spanning between *nx*
574602
to *nx1* (but excluding *nx1*-th column) is specified.
575603
"""
576-
return AxesLocator(self, nx, 0, nx1 if nx1 is not None else nx + 1, 1)
604+
return super().new_locator(nx, 0, nx1, 0)
577605

578-
def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None):
606+
def _locate(self, nx, ny, nx1, ny1, axes, renderer):
579607
# docstring inherited
608+
nx += self._xrefindex
609+
nx1 += self._xrefindex
580610
fig_w, fig_h = self._fig.bbox.size / self._fig.dpi
581611
x, y, w, h = self.get_position_runtime(axes, renderer)
582612
summed_ws = self.get_horizontal_sizes(renderer)
@@ -598,7 +628,7 @@ class VBoxDivider(SubplotDivider):
598628

599629
def new_locator(self, ny, ny1=None):
600630
"""
601-
Create a new `.AxesLocator` for the specified cell.
631+
Create an axes locator callable for the specified cell.
602632
603633
Parameters
604634
----------
@@ -608,10 +638,12 @@ def new_locator(self, ny, ny1=None):
608638
specified. Otherwise, location of rows spanning between *ny*
609639
to *ny1* (but excluding *ny1*-th row) is specified.
610640
"""
611-
return AxesLocator(self, 0, ny, 1, ny1 if ny1 is not None else ny + 1)
641+
return super().new_locator(0, ny, 0, ny1)
612642

613-
def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None):
643+
def _locate(self, nx, ny, nx1, ny1, axes, renderer):
614644
# docstring inherited
645+
ny += self._yrefindex
646+
ny1 += self._yrefindex
615647
fig_w, fig_h = self._fig.bbox.size / self._fig.dpi
616648
x, y, w, h = self.get_position_runtime(axes, renderer)
617649
summed_hs = self.get_vertical_sizes(renderer)

lib/mpl_toolkits/axes_grid1/tests/test_axes_grid1.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -608,9 +608,14 @@ def test_grid_axes_position(direction):
608608
fig = plt.figure()
609609
grid = Grid(fig, 111, (2, 2), direction=direction)
610610
loc = [ax.get_axes_locator() for ax in np.ravel(grid.axes_row)]
611-
assert loc[1]._nx > loc[0]._nx and loc[2]._ny < loc[0]._ny
612-
assert loc[0]._nx == loc[2]._nx and loc[0]._ny == loc[1]._ny
613-
assert loc[3]._nx == loc[1]._nx and loc[3]._ny == loc[2]._ny
611+
# Test nx.
612+
assert loc[1].args[0] > loc[0].args[0]
613+
assert loc[0].args[0] == loc[2].args[0]
614+
assert loc[3].args[0] == loc[1].args[0]
615+
# Test ny.
616+
assert loc[2].args[1] < loc[0].args[1]
617+
assert loc[0].args[1] == loc[1].args[1]
618+
assert loc[3].args[1] == loc[2].args[1]
614619

615620

616621
@pytest.mark.parametrize('rect, ngrids, error, message', (

0 commit comments

Comments
 (0)
0