Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 44f6900

Browse filesBrowse files
tacaswellMeeseeksDev[bot]
authored andcommitted
Backport PR #12716: FIX: return the actual ax.get_window_extent
1 parent 820ea41 commit 44f6900
Copy full SHA for 44f6900

File tree

Expand file treeCollapse file tree

14 files changed

+306
-29
lines changed
Filter options
Expand file treeCollapse file tree

14 files changed

+306
-29
lines changed
+18Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
get_window_extents changes:
2+
---------------------------
3+
4+
`.matplotlib.axes.Axes.get_window_extent` used to return a bounding box
5+
that was slightly larger than the axes, presumably to take into account
6+
the ticks that may be on a spine. However, it was not scaling the tick sizes
7+
according to the dpi of the canvas, and it did not check if the ticks were
8+
visible, or on the spine.
9+
10+
Now `.matplotlib.axes.Axes.get_window_extent` just returns the axes extent
11+
with no padding for ticks.
12+
13+
This affects `.matplotlib.axes.Axes.get_tightbbox` in cases where there are
14+
outward ticks with no tick labels, and it also removes the (small) pad around
15+
axes in that case.
16+
17+
`.spines.get_window_extent` now takes into account ticks that are on the
18+
spine.

‎lib/matplotlib/axes/_base.py

Copy file name to clipboardExpand all lines: lib/matplotlib/axes/_base.py
+29-18Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -576,18 +576,21 @@ def __setstate__(self, state):
576576

577577
def get_window_extent(self, *args, **kwargs):
578578
"""
579-
get the axes bounding box in display space; *args* and
580-
*kwargs* are empty
581-
"""
582-
bbox = self.bbox
583-
x_pad = 0
584-
if self.axison and self.xaxis.get_visible():
585-
x_pad = self.xaxis.get_tick_padding()
586-
y_pad = 0
587-
if self.axison and self.yaxis.get_visible():
588-
y_pad = self.yaxis.get_tick_padding()
589-
return mtransforms.Bbox([[bbox.x0 - x_pad, bbox.y0 - y_pad],
590-
[bbox.x1 + x_pad, bbox.y1 + y_pad]])
579+
Return the axes bounding box in display space; *args* and *kwargs*
580+
are empty.
581+
582+
This bounding box does not include the spines, ticks, ticklables,
583+
or other labels. For a bounding box including these elements use
584+
`~matplotlib.axes.Axes.get_tightbbox`.
585+
586+
See Also
587+
--------
588+
matplotlib.axes.Axes.get_tightbbox
589+
matplotlib.axis.Axis.get_tightbbox
590+
matplotlib.spines.get_window_extent
591+
592+
"""
593+
return self.bbox
591594

592595
def _init_axis(self):
593596
"move this out of __init__ because non-separable axes don't use it"
@@ -4286,6 +4289,13 @@ def get_tightbbox(self, renderer, call_axes_locator=True,
42864289
-------
42874290
bbox : `.BboxBase`
42884291
bounding box in figure pixel coordinates.
4292+
4293+
See Also
4294+
--------
4295+
matplotlib.axis.Axes.get_window_extent
4296+
matplotlib.axis.Axis.get_tightbbox
4297+
matplotlib.spines.get_window_extent
4298+
42894299
"""
42904300

42914301
bb = []
@@ -4300,13 +4310,14 @@ def get_tightbbox(self, renderer, call_axes_locator=True,
43004310
else:
43014311
self.apply_aspect()
43024312

4303-
bb_xaxis = self.xaxis.get_tightbbox(renderer)
4304-
if bb_xaxis:
4305-
bb.append(bb_xaxis)
4313+
if self.axison:
4314+
bb_xaxis = self.xaxis.get_tightbbox(renderer)
4315+
if bb_xaxis:
4316+
bb.append(bb_xaxis)
43064317

4307-
bb_yaxis = self.yaxis.get_tightbbox(renderer)
4308-
if bb_yaxis:
4309-
bb.append(bb_yaxis)
4318+
bb_yaxis = self.yaxis.get_tightbbox(renderer)
4319+
if bb_yaxis:
4320+
bb.append(bb_yaxis)
43104321

43114322
self._update_title_position(renderer)
43124323
bb.append(self.get_window_extent(renderer))

‎lib/matplotlib/spines.py

Copy file name to clipboardExpand all lines: lib/matplotlib/spines.py
+51-1Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,60 @@ def get_patch_transform(self):
145145
return super().get_patch_transform()
146146

147147
def get_window_extent(self, renderer=None):
148+
"""
149+
Return the window extent of the spines in display space, including
150+
padding for ticks (but not their labels)
151+
152+
See Also
153+
--------
154+
matplotlib.axes.Axes.get_tightbbox
155+
matplotlib.axes.Axes.get_window_extent
156+
157+
"""
148158
# make sure the location is updated so that transforms etc are
149159
# correct:
150160
self._adjust_location()
151-
return super().get_window_extent(renderer=renderer)
161+
bb = super().get_window_extent(renderer=renderer)
162+
bboxes = [bb]
163+
tickstocheck = [self.axis.majorTicks[0]]
164+
if len(self.axis.minorTicks) > 1:
165+
# only pad for minor ticks if there are more than one
166+
# of them. There is always one...
167+
tickstocheck.append(self.axis.minorTicks[1])
168+
for tick in tickstocheck:
169+
bb0 = bb.frozen()
170+
tickl = tick._size
171+
tickdir = tick._tickdir
172+
if tickdir == 'out':
173+
padout = 1
174+
padin = 0
175+
elif tickdir == 'in':
176+
padout = 0
177+
padin = 1
178+
else:
179+
padout = 0.5
180+
padin = 0.5
181+
padout = padout * tickl / 72 * self.figure.dpi
182+
padin = padin * tickl / 72 * self.figure.dpi
183+
184+
if tick.tick1line.get_visible():
185+
if self.spine_type in ['left']:
186+
bb0.x0 = bb0.x0 - padout
187+
bb0.x1 = bb0.x1 + padin
188+
elif self.spine_type in ['bottom']:
189+
bb0.y0 = bb0.y0 - padout
190+
bb0.y1 = bb0.y1 + padin
191+
192+
if tick.tick2line.get_visible():
193+
if self.spine_type in ['right']:
194+
bb0.x1 = bb0.x1 + padout
195+
bb0.x0 = bb0.x0 - padin
196+
elif self.spine_type in ['top']:
197+
bb0.y1 = bb0.y1 + padout
198+
bb0.y0 = bb0.y0 - padout
199+
bboxes.append(bb0)
200+
201+
return mtransforms.Bbox.union(bboxes)
152202

153203
def get_path(self):
154204
return self._path
Loading
Binary file not shown.
Loading
Loading
Loading
Loading
Loading
Loading

‎lib/matplotlib/tests/test_axes.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_axes.py
+199-2Lines changed: 199 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@
2323
import matplotlib.markers as mmarkers
2424
import matplotlib.patches as mpatches
2525
import matplotlib.colors as mcolors
26+
import matplotlib.transforms as mtransforms
2627
from numpy.testing import (
2728
assert_allclose, assert_array_equal, assert_array_almost_equal)
29+
from matplotlib import rc_context
2830
from matplotlib.cbook import (
2931
IgnoredKeywordWarning, MatplotlibDeprecationWarning)
3032

@@ -5891,9 +5893,9 @@ def test_tick_padding_tightbbox():
58915893
plt.rcParams["xtick.direction"] = "out"
58925894
plt.rcParams["ytick.direction"] = "out"
58935895
fig, ax = plt.subplots()
5894-
bb = ax.get_window_extent(fig.canvas.get_renderer())
5896+
bb = ax.get_tightbbox(fig.canvas.get_renderer())
58955897
ax.axis('off')
5896-
bb2 = ax.get_window_extent(fig.canvas.get_renderer())
5898+
bb2 = ax.get_tightbbox(fig.canvas.get_renderer())
58975899
assert bb.x0 < bb2.x0
58985900
assert bb.y0 < bb2.y0
58995901

@@ -6063,3 +6065,198 @@ def invert(x):
60636065
fig.canvas.draw()
60646066
fig.set_size_inches((7, 4))
60656067
assert_allclose(ax.get_position().extents, [0.125, 0.1, 0.9, 0.9])
6068+
6069+
6070+
def color_boxes(fig, axs):
6071+
"""
6072+
Helper for the tests below that test the extents of various axes elements
6073+
"""
6074+
fig.canvas.draw()
6075+
6076+
renderer = fig.canvas.get_renderer()
6077+
bbaxis = []
6078+
for nn, axx in enumerate([axs.xaxis, axs.yaxis]):
6079+
bb = axx.get_tightbbox(renderer)
6080+
if bb:
6081+
axisr = plt.Rectangle((bb.x0, bb.y0), width=bb.width,
6082+
height=bb.height, linewidth=0.7, edgecolor='y',
6083+
facecolor="none", transform=None, zorder=3)
6084+
fig.add_artist(axisr)
6085+
bbaxis += [bb]
6086+
6087+
bbspines = []
6088+
for nn, a in enumerate(['bottom', 'top', 'left', 'right']):
6089+
bb = axs.spines[a].get_window_extent(renderer)
6090+
spiner = plt.Rectangle((bb.x0, bb.y0), width=bb.width,
6091+
height=bb.height, linewidth=0.7,
6092+
edgecolor="green", facecolor="none",
6093+
transform=None, zorder=3)
6094+
fig.add_artist(spiner)
6095+
bbspines += [bb]
6096+
6097+
bb = axs.get_window_extent()
6098+
rect2 = plt.Rectangle((bb.x0, bb.y0), width=bb.width, height=bb.height,
6099+
linewidth=1.5, edgecolor="magenta",
6100+
facecolor="none", transform=None, zorder=2)
6101+
fig.add_artist(rect2)
6102+
bbax = bb
6103+
6104+
bb2 = axs.get_tightbbox(renderer)
6105+
rect2 = plt.Rectangle((bb2.x0, bb2.y0), width=bb2.width,
6106+
height=bb2.height, linewidth=3, edgecolor="red",
6107+
facecolor="none", transform=None, zorder=1)
6108+
fig.add_artist(rect2)
6109+
bbtb = bb2
6110+
return bbaxis, bbspines, bbax, bbtb
6111+
6112+
6113+
def test_normal_axes():
6114+
with rc_context({'_internal.classic_mode': False}):
6115+
fig, ax = plt.subplots(dpi=200, figsize=(6, 6))
6116+
fig.canvas.draw()
6117+
plt.close(fig)
6118+
bbaxis, bbspines, bbax, bbtb = color_boxes(fig, ax)
6119+
6120+
# test the axis bboxes
6121+
target = [
6122+
[123.375, 75.88888888888886, 983.25, 33.0],
6123+
[85.51388888888889, 99.99999999999997, 53.375, 993.0]
6124+
]
6125+
for nn, b in enumerate(bbaxis):
6126+
targetbb = mtransforms.Bbox.from_bounds(*target[nn])
6127+
assert_array_almost_equal(b.bounds, targetbb.bounds, decimal=2)
6128+
6129+
target = [
6130+
[150.0, 119.999, 930.0, 11.111],
6131+
[150.0, 1080.0, 930.0, 0.0],
6132+
[150.0, 119.9999, 11.111, 960.0],
6133+
[1068.8888, 119.9999, 11.111, 960.0]
6134+
]
6135+
for nn, b in enumerate(bbspines):
6136+
targetbb = mtransforms.Bbox.from_bounds(*target[nn])
6137+
assert_array_almost_equal(b.bounds, targetbb.bounds, decimal=2)
6138+
6139+
target = [150.0, 119.99999999999997, 930.0, 960.0]
6140+
targetbb = mtransforms.Bbox.from_bounds(*target)
6141+
assert_array_almost_equal(bbax.bounds, targetbb.bounds, decimal=2)
6142+
6143+
target = [85.5138, 75.88888, 1021.11, 1017.11]
6144+
targetbb = mtransforms.Bbox.from_bounds(*target)
6145+
assert_array_almost_equal(bbtb.bounds, targetbb.bounds, decimal=2)
6146+
6147+
# test that get_position roundtrips to get_window_extent
6148+
axbb = ax.get_position().transformed(fig.transFigure).bounds
6149+
assert_array_almost_equal(axbb, ax.get_window_extent().bounds, decimal=2)
6150+
6151+
6152+
def test_nodecorator():
6153+
with rc_context({'_internal.classic_mode': False}):
6154+
fig, ax = plt.subplots(dpi=200, figsize=(6, 6))
6155+
fig.canvas.draw()
6156+
ax.set(xticklabels=[], yticklabels=[])
6157+
bbaxis, bbspines, bbax, bbtb = color_boxes(fig, ax)
6158+
6159+
# test the axis bboxes
6160+
target = [
6161+
None,
6162+
None
6163+
]
6164+
for nn, b in enumerate(bbaxis):
6165+
assert b is None
6166+
6167+
target = [
6168+
[150.0, 119.999, 930.0, 11.111],
6169+
[150.0, 1080.0, 930.0, 0.0],
6170+
[150.0, 119.9999, 11.111, 960.0],
6171+
[1068.8888, 119.9999, 11.111, 960.0]
6172+
]
6173+
for nn, b in enumerate(bbspines):
6174+
targetbb = mtransforms.Bbox.from_bounds(*target[nn])
6175+
assert_allclose(b.bounds, targetbb.bounds, atol=1e-2)
6176+
6177+
target = [150.0, 119.99999999999997, 930.0, 960.0]
6178+
targetbb = mtransforms.Bbox.from_bounds(*target)
6179+
assert_allclose(bbax.bounds, targetbb.bounds, atol=1e-2)
6180+
6181+
target = [150., 120., 930., 960.]
6182+
targetbb = mtransforms.Bbox.from_bounds(*target)
6183+
assert_allclose(bbtb.bounds, targetbb.bounds, atol=1e-2)
6184+
6185+
6186+
def test_displaced_spine():
6187+
with rc_context({'_internal.classic_mode': False}):
6188+
fig, ax = plt.subplots(dpi=200, figsize=(6, 6))
6189+
ax.set(xticklabels=[], yticklabels=[])
6190+
ax.spines['bottom'].set_position(('axes', -0.1))
6191+
fig.canvas.draw()
6192+
bbaxis, bbspines, bbax, bbtb = color_boxes(fig, ax)
6193+
6194+
target = [
6195+
[150., 24., 930., 11.111111],
6196+
[150.0, 1080.0, 930.0, 0.0],
6197+
[150.0, 119.9999, 11.111, 960.0],
6198+
[1068.8888, 119.9999, 11.111, 960.0]
6199+
]
6200+
for nn, b in enumerate(bbspines):
6201+
targetbb = mtransforms.Bbox.from_bounds(*target[nn])
6202+
6203+
target = [150.0, 119.99999999999997, 930.0, 960.0]
6204+
targetbb = mtransforms.Bbox.from_bounds(*target)
6205+
assert_allclose(bbax.bounds, targetbb.bounds, atol=1e-2)
6206+
6207+
target = [150., 24., 930., 1056.]
6208+
targetbb = mtransforms.Bbox.from_bounds(*target)
6209+
assert_allclose(bbtb.bounds, targetbb.bounds, atol=1e-2)
6210+
6211+
6212+
def test_tickdirs():
6213+
"""
6214+
Switch the tickdirs and make sure the bboxes switch with them
6215+
"""
6216+
targets = [[[150.0, 120.0, 930.0, 11.1111],
6217+
[150.0, 120.0, 11.111, 960.0]],
6218+
[[150.0, 108.8889, 930.0, 11.111111111111114],
6219+
[138.889, 120, 11.111, 960.0]],
6220+
[[150.0, 114.44444444444441, 930.0, 11.111111111111114],
6221+
[144.44444444444446, 119.999, 11.111, 960.0]]]
6222+
for dnum, dirs in enumerate(['in', 'out', 'inout']):
6223+
with rc_context({'_internal.classic_mode': False}):
6224+
fig, ax = plt.subplots(dpi=200, figsize=(6, 6))
6225+
ax.tick_params(direction=dirs)
6226+
fig.canvas.draw()
6227+
bbaxis, bbspines, bbax, bbtb = color_boxes(fig, ax)
6228+
for nn, num in enumerate([0, 2]):
6229+
targetbb = mtransforms.Bbox.from_bounds(*targets[dnum][nn])
6230+
assert_allclose(bbspines[num].bounds, targetbb.bounds,
6231+
atol=1e-2)
6232+
6233+
6234+
def test_minor_accountedfor():
6235+
with rc_context({'_internal.classic_mode': False}):
6236+
fig, ax = plt.subplots(dpi=200, figsize=(6, 6))
6237+
fig.canvas.draw()
6238+
ax.tick_params(which='both', direction='out')
6239+
6240+
bbaxis, bbspines, bbax, bbtb = color_boxes(fig, ax)
6241+
bbaxis, bbspines, bbax, bbtb = color_boxes(fig, ax)
6242+
targets = [[150.0, 108.88888888888886, 930.0, 11.111111111111114],
6243+
[138.8889, 119.9999, 11.1111, 960.0]]
6244+
for n in range(2):
6245+
targetbb = mtransforms.Bbox.from_bounds(*targets[n])
6246+
assert_allclose(bbspines[n * 2].bounds, targetbb.bounds,
6247+
atol=1e-2)
6248+
6249+
fig, ax = plt.subplots(dpi=200, figsize=(6, 6))
6250+
fig.canvas.draw()
6251+
ax.tick_params(which='both', direction='out')
6252+
ax.minorticks_on()
6253+
ax.tick_params(axis='both', which='minor', length=30)
6254+
fig.canvas.draw()
6255+
bbaxis, bbspines, bbax, bbtb = color_boxes(fig, ax)
6256+
targets = [[150.0, 36.66666666666663, 930.0, 83.33333333333334],
6257+
[66.6667, 120.0, 83.3333, 960.0]]
6258+
6259+
for n in range(2):
6260+
targetbb = mtransforms.Bbox.from_bounds(*targets[n])
6261+
assert_allclose(bbspines[n * 2].bounds, targetbb.bounds,
6262+
atol=1e-2)

‎lib/matplotlib/tests/test_tightlayout.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_tightlayout.py
+9-8Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -184,14 +184,15 @@ def test_outward_ticks():
184184
ax.xaxis.set_tick_params(tickdir='out', length=32, width=3)
185185
ax.yaxis.set_tick_params(tickdir='out', length=32, width=3)
186186
plt.tight_layout()
187-
assert_array_equal(
188-
np.round([ax.get_position().get_points() for ax in fig.axes], 3),
189-
# These values were obtained after visual checking that they correspond
190-
# to a tight layouting that did take the ticks into account.
191-
[[[0.091, 0.590], [0.437, 0.903]],
192-
[[0.581, 0.590], [0.927, 0.903]],
193-
[[0.091, 0.140], [0.437, 0.454]],
194-
[[0.581, 0.140], [0.927, 0.454]]])
187+
# These values were obtained after visual checking that they correspond
188+
# to a tight layouting that did take the ticks into account.
189+
ans = [[[0.091, 0.607], [0.433, 0.933]],
190+
[[0.579, 0.607], [0.922, 0.933]],
191+
[[0.091, 0.140], [0.433, 0.466]],
192+
[[0.579, 0.140], [0.922, 0.466]]]
193+
for nn, ax in enumerate(fig.axes):
194+
assert_array_equal(np.round(ax.get_position().get_points(), 3),
195+
ans[nn])
195196

196197

197198
def add_offsetboxes(ax, size=10, margin=.1, color='black'):
Loading

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.