Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5cc5541

Browse files
committed
FIX: return the actual ax.get_window_extent
1 parent 867c3dd commit 5cc5541

File tree

15 files changed

+311
-29
lines changed

15 files changed

+311
-29
lines changed
Lines 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

Lines 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

Lines 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
Binary file not shown.

lib/matplotlib/tests/test_axes.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5891,9 +5891,9 @@ def test_tick_padding_tightbbox():
58915891
plt.rcParams["xtick.direction"] = "out"
58925892
plt.rcParams["ytick.direction"] = "out"
58935893
fig, ax = plt.subplots()
5894-
bb = ax.get_window_extent(fig.canvas.get_renderer())
5894+
bb = ax.get_tightbbox(fig.canvas.get_renderer())
58955895
ax.axis('off')
5896-
bb2 = ax.get_window_extent(fig.canvas.get_renderer())
5896+
bb2 = ax.get_tightbbox(fig.canvas.get_renderer())
58975897
assert bb.x0 < bb2.x0
58985898
assert bb.y0 < bb2.y0
58995899

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
import numpy as np
2+
from numpy.testing import (assert_allclose, assert_almost_equal,
3+
assert_array_equal, assert_array_almost_equal)
4+
5+
from matplotlib import rc_context
6+
import matplotlib.pyplot as plt
7+
import matplotlib.transforms as mtransforms
8+
9+
10+
def color_boxes(fig, axs):
11+
fig.canvas.draw()
12+
13+
renderer = fig.canvas.get_renderer()
14+
bbaxis = []
15+
for nn, axx in enumerate([axs.xaxis, axs.yaxis]):
16+
bb = axx.get_tightbbox(renderer)
17+
if bb:
18+
axisr = plt.Rectangle((bb.x0, bb.y0), width=bb.width,
19+
height=bb.height, linewidth=0.7, edgecolor='y',
20+
facecolor="none", transform=None, zorder=3)
21+
fig.add_artist(axisr)
22+
bbaxis += [bb]
23+
24+
bbspines = []
25+
for nn, a in enumerate(['bottom', 'top', 'left', 'right']):
26+
bb = axs.spines[a].get_window_extent(renderer)
27+
if bb:
28+
spiner = plt.Rectangle((bb.x0, bb.y0), width=bb.width,
29+
height=bb.height, linewidth=0.7,
30+
edgecolor="green", facecolor="none",
31+
transform=None, zorder=3)
32+
fig.add_artist(spiner)
33+
bbspines += [bb]
34+
35+
bb = axs.get_window_extent()
36+
if bb:
37+
rect2 = plt.Rectangle((bb.x0, bb.y0), width=bb.width, height=bb.height,
38+
linewidth=1.5, edgecolor="magenta",
39+
facecolor="none", transform=None, zorder=2)
40+
fig.add_artist(rect2)
41+
bbax = bb
42+
43+
bb2 = axs.get_tightbbox(renderer)
44+
if bb2:
45+
rect2 = plt.Rectangle((bb2.x0, bb2.y0), width=bb2.width,
46+
height=bb2.height, linewidth=3, edgecolor="red",
47+
facecolor="none", transform=None, zorder=1)
48+
fig.add_artist(rect2)
49+
bbtb = bb2
50+
return bbaxis, bbspines, bbax, bbtb
51+
52+
53+
def test_normal_axes():
54+
with rc_context({'_internal.classic_mode': False}):
55+
fig, ax = plt.subplots(dpi=200, figsize=(6, 6))
56+
fig.canvas.draw()
57+
plt.close(fig)
58+
bbaxis, bbspines, bbax, bbtb = color_boxes(fig, ax)
59+
60+
# test the axis bboxes
61+
target = [
62+
[123.375, 75.88888888888886, 983.25, 33.0],
63+
[85.51388888888889, 99.99999999999997, 53.375, 993.0]
64+
]
65+
for nn, b in enumerate(bbaxis):
66+
targetbb = mtransforms.Bbox.from_bounds(*target[nn])
67+
assert_array_almost_equal(b.bounds, targetbb.bounds, decimal=2)
68+
69+
target = [
70+
[150.0, 119.999, 930.0, 11.111],
71+
[150.0, 1080.0, 930.0, 0.0],
72+
[150.0, 119.9999, 11.111, 960.0],
73+
[1068.8888, 119.9999, 11.111, 960.0]
74+
]
75+
for nn, b in enumerate(bbspines):
76+
targetbb = mtransforms.Bbox.from_bounds(*target[nn])
77+
assert_array_almost_equal(b.bounds, targetbb.bounds, decimal=2)
78+
79+
target = [150.0, 119.99999999999997, 930.0, 960.0]
80+
targetbb = mtransforms.Bbox.from_bounds(*target)
81+
assert_array_almost_equal(bbax.bounds, targetbb.bounds, decimal=2)
82+
83+
target = [85.5138, 75.88888, 1021.11, 1017.11]
84+
targetbb = mtransforms.Bbox.from_bounds(*target)
85+
assert_array_almost_equal(bbtb.bounds, targetbb.bounds, decimal=2)
86+
87+
# test that get_position roundtrips to get_window_extent
88+
axbb = ax.get_position().transformed(fig.transFigure).bounds
89+
assert_array_almost_equal(axbb, ax.get_window_extent().bounds, decimal=2)
90+
91+
92+
def test_nodecorator():
93+
with rc_context({'_internal.classic_mode': False}):
94+
fig, ax = plt.subplots(dpi=200, figsize=(6, 6))
95+
fig.canvas.draw()
96+
ax.set(xticklabels=[], yticklabels=[])
97+
bbaxis, bbspines, bbax, bbtb = color_boxes(fig, ax)
98+
99+
# test the axis bboxes
100+
target = [
101+
None,
102+
None
103+
]
104+
for nn, b in enumerate(bbaxis):
105+
assert b is None
106+
107+
target = [
108+
[150.0, 119.999, 930.0, 11.111],
109+
[150.0, 1080.0, 930.0, 0.0],
110+
[150.0, 119.9999, 11.111, 960.0],
111+
[1068.8888, 119.9999, 11.111, 960.0]
112+
]
113+
for nn, b in enumerate(bbspines):
114+
targetbb = mtransforms.Bbox.from_bounds(*target[nn])
115+
assert_allclose(b.bounds, targetbb.bounds, atol=1e-2)
116+
117+
target = [150.0, 119.99999999999997, 930.0, 960.0]
118+
targetbb = mtransforms.Bbox.from_bounds(*target)
119+
assert_allclose(bbax.bounds, targetbb.bounds, atol=1e-2)
120+
121+
target = [150., 120., 930., 960.]
122+
targetbb = mtransforms.Bbox.from_bounds(*target)
123+
assert_allclose(bbtb.bounds, targetbb.bounds, atol=1e-2)
124+
125+
126+
def test_displaced_spine():
127+
with rc_context({'_internal.classic_mode': False}):
128+
fig, ax = plt.subplots(dpi=200, figsize=(6, 6))
129+
ax.set(xticklabels=[], yticklabels=[])
130+
ax.spines['bottom'].set_position(('axes', -0.1))
131+
fig.canvas.draw()
132+
bbaxis, bbspines, bbax, bbtb = color_boxes(fig, ax)
133+
134+
target = [
135+
[150., 24., 930., 11.111111],
136+
[150.0, 1080.0, 930.0, 0.0],
137+
[150.0, 119.9999, 11.111, 960.0],
138+
[1068.8888, 119.9999, 11.111, 960.0]
139+
]
140+
for nn, b in enumerate(bbspines):
141+
targetbb = mtransforms.Bbox.from_bounds(*target[nn])
142+
143+
target = [150.0, 119.99999999999997, 930.0, 960.0]
144+
targetbb = mtransforms.Bbox.from_bounds(*target)
145+
assert_allclose(bbax.bounds, targetbb.bounds, atol=1e-2)
146+
147+
target = [150., 24., 930., 1056.]
148+
targetbb = mtransforms.Bbox.from_bounds(*target)
149+
assert_allclose(bbtb.bounds, targetbb.bounds, atol=1e-2)
150+
151+
152+
def test_tickdirs():
153+
"""
154+
Switch the tickdirs and make sure the bboxes switch with them
155+
"""
156+
targets = [[[150.0, 120.0, 930.0, 11.1111],
157+
[150.0, 120.0, 11.111, 960.0]],
158+
[[150.0, 108.8889, 930.0, 11.111111111111114],
159+
[138.889, 120, 11.111, 960.0]],
160+
[[150.0, 114.44444444444441, 930.0, 11.111111111111114],
161+
[144.44444444444446, 119.999, 11.111, 960.0]]]
162+
for dnum, dirs in enumerate(['in', 'out', 'inout']):
163+
with rc_context({'_internal.classic_mode': False}):
164+
fig, ax = plt.subplots(dpi=200, figsize=(6, 6))
165+
ax.tick_params(direction=dirs)
166+
fig.canvas.draw()
167+
bbaxis, bbspines, bbax, bbtb = color_boxes(fig, ax)
168+
for nn, num in enumerate([0, 2]):
169+
targetbb = mtransforms.Bbox.from_bounds(*targets[dnum][nn])
170+
assert_allclose(bbspines[num].bounds, targetbb.bounds,
171+
atol=1e-2)
172+
173+
174+
def test_minor_accountedfor():
175+
with rc_context({'_internal.classic_mode': False}):
176+
fig, ax = plt.subplots(dpi=200, figsize=(6, 6))
177+
fig.canvas.draw()
178+
ax.tick_params(which='both', direction='out')
179+
180+
bbaxis, bbspines, bbax, bbtb = color_boxes(fig, ax)
181+
bbaxis, bbspines, bbax, bbtb = color_boxes(fig, ax)
182+
targets = [[150.0, 108.88888888888886, 930.0, 11.111111111111114],
183+
[138.8889, 119.9999, 11.1111, 960.0]]
184+
for n in range(2):
185+
targetbb = mtransforms.Bbox.from_bounds(*targets[n])
186+
assert_allclose(bbspines[n * 2].bounds, targetbb.bounds,
187+
atol=1e-2)
188+
189+
fig, ax = plt.subplots(dpi=200, figsize=(6, 6))
190+
fig.canvas.draw()
191+
ax.tick_params(which='both', direction='out')
192+
ax.minorticks_on()
193+
ax.tick_params(axis='both', which='minor', length=30)
194+
fig.canvas.draw()
195+
bbaxis, bbspines, bbax, bbtb = color_boxes(fig, ax)
196+
targets = [[150.0, 36.66666666666663, 930.0, 83.33333333333334],
197+
[66.6667, 120.0, 83.3333, 960.0]]
198+
199+
for n in range(2):
200+
targetbb = mtransforms.Bbox.from_bounds(*targets[n])
201+
assert_allclose(bbspines[n * 2].bounds, targetbb.bounds,
202+
atol=1e-2)

lib/matplotlib/tests/test_tightlayout.py

Lines 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'):

0 commit comments

Comments
 (0)