396 lines
12 KiB
Python
396 lines
12 KiB
Python
import warnings
|
|
|
|
import numpy as np
|
|
from numpy.testing import assert_array_equal
|
|
import pytest
|
|
|
|
import matplotlib as mpl
|
|
from matplotlib.testing.decorators import image_comparison
|
|
import matplotlib.pyplot as plt
|
|
from matplotlib.offsetbox import AnchoredOffsetbox, DrawingArea
|
|
from matplotlib.patches import Rectangle
|
|
|
|
|
|
def example_plot(ax, fontsize=12):
|
|
ax.plot([1, 2])
|
|
ax.locator_params(nbins=3)
|
|
ax.set_xlabel('x-label', fontsize=fontsize)
|
|
ax.set_ylabel('y-label', fontsize=fontsize)
|
|
ax.set_title('Title', fontsize=fontsize)
|
|
|
|
|
|
@image_comparison(['tight_layout1'], tol=1.9)
|
|
def test_tight_layout1():
|
|
"""Test tight_layout for a single subplot."""
|
|
fig, ax = plt.subplots()
|
|
example_plot(ax, fontsize=24)
|
|
plt.tight_layout()
|
|
|
|
|
|
@image_comparison(['tight_layout2'])
|
|
def test_tight_layout2():
|
|
"""Test tight_layout for multiple subplots."""
|
|
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2)
|
|
example_plot(ax1)
|
|
example_plot(ax2)
|
|
example_plot(ax3)
|
|
example_plot(ax4)
|
|
plt.tight_layout()
|
|
|
|
|
|
@image_comparison(['tight_layout3'])
|
|
def test_tight_layout3():
|
|
"""Test tight_layout for multiple subplots."""
|
|
ax1 = plt.subplot(221)
|
|
ax2 = plt.subplot(223)
|
|
ax3 = plt.subplot(122)
|
|
example_plot(ax1)
|
|
example_plot(ax2)
|
|
example_plot(ax3)
|
|
plt.tight_layout()
|
|
|
|
|
|
@image_comparison(['tight_layout4'], freetype_version=('2.5.5', '2.6.1'),
|
|
tol=0.015)
|
|
def test_tight_layout4():
|
|
"""Test tight_layout for subplot2grid."""
|
|
ax1 = plt.subplot2grid((3, 3), (0, 0))
|
|
ax2 = plt.subplot2grid((3, 3), (0, 1), colspan=2)
|
|
ax3 = plt.subplot2grid((3, 3), (1, 0), colspan=2, rowspan=2)
|
|
ax4 = plt.subplot2grid((3, 3), (1, 2), rowspan=2)
|
|
example_plot(ax1)
|
|
example_plot(ax2)
|
|
example_plot(ax3)
|
|
example_plot(ax4)
|
|
plt.tight_layout()
|
|
|
|
|
|
@image_comparison(['tight_layout5'])
|
|
def test_tight_layout5():
|
|
"""Test tight_layout for image."""
|
|
ax = plt.subplot()
|
|
arr = np.arange(100).reshape((10, 10))
|
|
ax.imshow(arr, interpolation="none")
|
|
plt.tight_layout()
|
|
|
|
|
|
@image_comparison(['tight_layout6'])
|
|
def test_tight_layout6():
|
|
"""Test tight_layout for gridspec."""
|
|
|
|
# This raises warnings since tight layout cannot
|
|
# do this fully automatically. But the test is
|
|
# correct since the layout is manually edited
|
|
with warnings.catch_warnings():
|
|
warnings.simplefilter("ignore", UserWarning)
|
|
fig = plt.figure()
|
|
|
|
gs1 = mpl.gridspec.GridSpec(2, 1)
|
|
ax1 = fig.add_subplot(gs1[0])
|
|
ax2 = fig.add_subplot(gs1[1])
|
|
|
|
example_plot(ax1)
|
|
example_plot(ax2)
|
|
|
|
gs1.tight_layout(fig, rect=[0, 0, 0.5, 1])
|
|
|
|
gs2 = mpl.gridspec.GridSpec(3, 1)
|
|
|
|
for ss in gs2:
|
|
ax = fig.add_subplot(ss)
|
|
example_plot(ax)
|
|
ax.set_title("")
|
|
ax.set_xlabel("")
|
|
|
|
ax.set_xlabel("x-label", fontsize=12)
|
|
|
|
gs2.tight_layout(fig, rect=[0.5, 0, 1, 1], h_pad=0.45)
|
|
|
|
top = min(gs1.top, gs2.top)
|
|
bottom = max(gs1.bottom, gs2.bottom)
|
|
|
|
gs1.tight_layout(fig, rect=[None, 0 + (bottom-gs1.bottom),
|
|
0.5, 1 - (gs1.top-top)])
|
|
gs2.tight_layout(fig, rect=[0.5, 0 + (bottom-gs2.bottom),
|
|
None, 1 - (gs2.top-top)],
|
|
h_pad=0.45)
|
|
|
|
|
|
@image_comparison(['tight_layout7'], tol=1.9)
|
|
def test_tight_layout7():
|
|
# tight layout with left and right titles
|
|
fontsize = 24
|
|
fig, ax = plt.subplots()
|
|
ax.plot([1, 2])
|
|
ax.locator_params(nbins=3)
|
|
ax.set_xlabel('x-label', fontsize=fontsize)
|
|
ax.set_ylabel('y-label', fontsize=fontsize)
|
|
ax.set_title('Left Title', loc='left', fontsize=fontsize)
|
|
ax.set_title('Right Title', loc='right', fontsize=fontsize)
|
|
plt.tight_layout()
|
|
|
|
|
|
@image_comparison(['tight_layout8'], tol=0.005)
|
|
def test_tight_layout8():
|
|
"""Test automatic use of tight_layout."""
|
|
fig = plt.figure()
|
|
fig.set_layout_engine(layout='tight', pad=0.1)
|
|
ax = fig.add_subplot()
|
|
example_plot(ax, fontsize=24)
|
|
fig.draw_without_rendering()
|
|
|
|
|
|
@image_comparison(['tight_layout9'])
|
|
def test_tight_layout9():
|
|
# Test tight_layout for non-visible subplots
|
|
# GH 8244
|
|
f, axarr = plt.subplots(2, 2)
|
|
axarr[1][1].set_visible(False)
|
|
plt.tight_layout()
|
|
|
|
|
|
def test_outward_ticks():
|
|
"""Test automatic use of tight_layout."""
|
|
fig = plt.figure()
|
|
ax = fig.add_subplot(221)
|
|
ax.xaxis.set_tick_params(tickdir='out', length=16, width=3)
|
|
ax.yaxis.set_tick_params(tickdir='out', length=16, width=3)
|
|
ax.xaxis.set_tick_params(
|
|
tickdir='out', length=32, width=3, tick1On=True, which='minor')
|
|
ax.yaxis.set_tick_params(
|
|
tickdir='out', length=32, width=3, tick1On=True, which='minor')
|
|
ax.xaxis.set_ticks([0], minor=True)
|
|
ax.yaxis.set_ticks([0], minor=True)
|
|
ax = fig.add_subplot(222)
|
|
ax.xaxis.set_tick_params(tickdir='in', length=32, width=3)
|
|
ax.yaxis.set_tick_params(tickdir='in', length=32, width=3)
|
|
ax = fig.add_subplot(223)
|
|
ax.xaxis.set_tick_params(tickdir='inout', length=32, width=3)
|
|
ax.yaxis.set_tick_params(tickdir='inout', length=32, width=3)
|
|
ax = fig.add_subplot(224)
|
|
ax.xaxis.set_tick_params(tickdir='out', length=32, width=3)
|
|
ax.yaxis.set_tick_params(tickdir='out', length=32, width=3)
|
|
plt.tight_layout()
|
|
# These values were obtained after visual checking that they correspond
|
|
# to a tight layouting that did take the ticks into account.
|
|
expected = [
|
|
[[0.091, 0.607], [0.433, 0.933]],
|
|
[[0.579, 0.607], [0.922, 0.933]],
|
|
[[0.091, 0.140], [0.433, 0.466]],
|
|
[[0.579, 0.140], [0.922, 0.466]],
|
|
]
|
|
for nn, ax in enumerate(fig.axes):
|
|
assert_array_equal(np.round(ax.get_position().get_points(), 3),
|
|
expected[nn])
|
|
|
|
|
|
def add_offsetboxes(ax, size=10, margin=.1, color='black'):
|
|
"""
|
|
Surround ax with OffsetBoxes
|
|
"""
|
|
m, mp = margin, 1+margin
|
|
anchor_points = [(-m, -m), (-m, .5), (-m, mp),
|
|
(mp, .5), (.5, mp), (mp, mp),
|
|
(.5, -m), (mp, -m), (.5, -m)]
|
|
for point in anchor_points:
|
|
da = DrawingArea(size, size)
|
|
background = Rectangle((0, 0), width=size,
|
|
height=size,
|
|
facecolor=color,
|
|
edgecolor='None',
|
|
linewidth=0,
|
|
antialiased=False)
|
|
da.add_artist(background)
|
|
|
|
anchored_box = AnchoredOffsetbox(
|
|
loc='center',
|
|
child=da,
|
|
pad=0.,
|
|
frameon=False,
|
|
bbox_to_anchor=point,
|
|
bbox_transform=ax.transAxes,
|
|
borderpad=0.)
|
|
ax.add_artist(anchored_box)
|
|
return anchored_box
|
|
|
|
|
|
@image_comparison(['tight_layout_offsetboxes1', 'tight_layout_offsetboxes2'])
|
|
def test_tight_layout_offsetboxes():
|
|
# 1.
|
|
# - Create 4 subplots
|
|
# - Plot a diagonal line on them
|
|
# - Surround each plot with 7 boxes
|
|
# - Use tight_layout
|
|
# - See that the squares are included in the tight_layout
|
|
# and that the squares in the middle do not overlap
|
|
#
|
|
# 2.
|
|
# - Make the squares around the right side axes invisible
|
|
# - See that the invisible squares do not affect the
|
|
# tight_layout
|
|
rows = cols = 2
|
|
colors = ['red', 'blue', 'green', 'yellow']
|
|
x = y = [0, 1]
|
|
|
|
def _subplots():
|
|
_, axs = plt.subplots(rows, cols)
|
|
axs = axs.flat
|
|
for ax, color in zip(axs, colors):
|
|
ax.plot(x, y, color=color)
|
|
add_offsetboxes(ax, 20, color=color)
|
|
return axs
|
|
|
|
# 1.
|
|
axs = _subplots()
|
|
plt.tight_layout()
|
|
|
|
# 2.
|
|
axs = _subplots()
|
|
for ax in (axs[cols-1::rows]):
|
|
for child in ax.get_children():
|
|
if isinstance(child, AnchoredOffsetbox):
|
|
child.set_visible(False)
|
|
|
|
plt.tight_layout()
|
|
|
|
|
|
def test_empty_layout():
|
|
"""Test that tight layout doesn't cause an error when there are no Axes."""
|
|
fig = plt.gcf()
|
|
fig.tight_layout()
|
|
|
|
|
|
@pytest.mark.parametrize("label", ["xlabel", "ylabel"])
|
|
def test_verybig_decorators(label):
|
|
"""Test that no warning emitted when xlabel/ylabel too big."""
|
|
fig, ax = plt.subplots(figsize=(3, 2))
|
|
ax.set(**{label: 'a' * 100})
|
|
|
|
|
|
def test_big_decorators_horizontal():
|
|
"""Test that doesn't warn when xlabel too big."""
|
|
fig, axs = plt.subplots(1, 2, figsize=(3, 2))
|
|
axs[0].set_xlabel('a' * 30)
|
|
axs[1].set_xlabel('b' * 30)
|
|
|
|
|
|
def test_big_decorators_vertical():
|
|
"""Test that doesn't warn when ylabel too big."""
|
|
fig, axs = plt.subplots(2, 1, figsize=(3, 2))
|
|
axs[0].set_ylabel('a' * 20)
|
|
axs[1].set_ylabel('b' * 20)
|
|
|
|
|
|
def test_badsubplotgrid():
|
|
# test that we get warning for mismatched subplot grids, not than an error
|
|
plt.subplot2grid((4, 5), (0, 0))
|
|
# this is the bad entry:
|
|
plt.subplot2grid((5, 5), (0, 3), colspan=3, rowspan=5)
|
|
with pytest.warns(UserWarning):
|
|
plt.tight_layout()
|
|
|
|
|
|
def test_collapsed():
|
|
# test that if the amount of space required to make all the axes
|
|
# decorations fit would mean that the actual Axes would end up with size
|
|
# zero (i.e. margins add up to more than the available width) that a call
|
|
# to tight_layout will not get applied:
|
|
fig, ax = plt.subplots(tight_layout=True)
|
|
ax.set_xlim([0, 1])
|
|
ax.set_ylim([0, 1])
|
|
|
|
ax.annotate('BIG LONG STRING', xy=(1.25, 2), xytext=(10.5, 1.75),
|
|
annotation_clip=False)
|
|
p1 = ax.get_position()
|
|
with pytest.warns(UserWarning):
|
|
plt.tight_layout()
|
|
p2 = ax.get_position()
|
|
assert p1.width == p2.width
|
|
# test that passing a rect doesn't crash...
|
|
with pytest.warns(UserWarning):
|
|
plt.tight_layout(rect=[0, 0, 0.8, 0.8])
|
|
|
|
|
|
def test_suptitle():
|
|
fig, ax = plt.subplots(tight_layout=True)
|
|
st = fig.suptitle("foo")
|
|
t = ax.set_title("bar")
|
|
fig.canvas.draw()
|
|
assert st.get_window_extent().y0 > t.get_window_extent().y1
|
|
|
|
|
|
@pytest.mark.backend("pdf")
|
|
def test_non_agg_renderer(monkeypatch, recwarn):
|
|
unpatched_init = mpl.backend_bases.RendererBase.__init__
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
# Check that we don't instantiate any other renderer than a pdf
|
|
# renderer to perform pdf tight layout.
|
|
assert isinstance(self, mpl.backends.backend_pdf.RendererPdf)
|
|
unpatched_init(self, *args, **kwargs)
|
|
|
|
monkeypatch.setattr(mpl.backend_bases.RendererBase, "__init__", __init__)
|
|
fig, ax = plt.subplots()
|
|
fig.tight_layout()
|
|
|
|
|
|
def test_manual_colorbar():
|
|
# This should warn, but not raise
|
|
fig, axes = plt.subplots(1, 2)
|
|
pts = axes[1].scatter([0, 1], [0, 1], c=[1, 5])
|
|
ax_rect = axes[1].get_position()
|
|
cax = fig.add_axes(
|
|
[ax_rect.x1 + 0.005, ax_rect.y0, 0.015, ax_rect.height]
|
|
)
|
|
fig.colorbar(pts, cax=cax)
|
|
with pytest.warns(UserWarning, match="This figure includes Axes"):
|
|
fig.tight_layout()
|
|
|
|
|
|
def test_clipped_to_axes():
|
|
# Ensure that _fully_clipped_to_axes() returns True under default
|
|
# conditions for all projection types. Axes.get_tightbbox()
|
|
# uses this to skip artists in layout calculations.
|
|
arr = np.arange(100).reshape((10, 10))
|
|
fig = plt.figure(figsize=(6, 2))
|
|
ax1 = fig.add_subplot(131, projection='rectilinear')
|
|
ax2 = fig.add_subplot(132, projection='mollweide')
|
|
ax3 = fig.add_subplot(133, projection='polar')
|
|
for ax in (ax1, ax2, ax3):
|
|
# Default conditions (clipped by ax.bbox or ax.patch)
|
|
ax.grid(False)
|
|
h, = ax.plot(arr[:, 0])
|
|
m = ax.pcolor(arr)
|
|
assert h._fully_clipped_to_axes()
|
|
assert m._fully_clipped_to_axes()
|
|
# Non-default conditions (not clipped by ax.patch)
|
|
rect = Rectangle((0, 0), 0.5, 0.5, transform=ax.transAxes)
|
|
h.set_clip_path(rect)
|
|
m.set_clip_path(rect.get_path(), rect.get_transform())
|
|
assert not h._fully_clipped_to_axes()
|
|
assert not m._fully_clipped_to_axes()
|
|
|
|
|
|
def test_tight_pads():
|
|
fig, ax = plt.subplots()
|
|
with pytest.warns(PendingDeprecationWarning,
|
|
match='will be deprecated'):
|
|
fig.set_tight_layout({'pad': 0.15})
|
|
fig.draw_without_rendering()
|
|
|
|
|
|
def test_tight_kwargs():
|
|
fig, ax = plt.subplots(tight_layout={'pad': 0.15})
|
|
fig.draw_without_rendering()
|
|
|
|
|
|
def test_tight_toggle():
|
|
fig, ax = plt.subplots()
|
|
with pytest.warns(PendingDeprecationWarning):
|
|
fig.set_tight_layout(True)
|
|
assert fig.get_tight_layout()
|
|
fig.set_tight_layout(False)
|
|
assert not fig.get_tight_layout()
|
|
fig.set_tight_layout(True)
|
|
assert fig.get_tight_layout()
|