123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787 |
- from datetime import datetime
- import io
- from pathlib import Path
- import platform
- from types import SimpleNamespace
- import warnings
- try:
- from contextlib import nullcontext
- except ImportError:
- from contextlib import ExitStack as nullcontext # Py3.6
- import matplotlib as mpl
- from matplotlib import cbook, rcParams
- from matplotlib.testing.decorators import image_comparison, check_figures_equal
- from matplotlib.axes import Axes
- from matplotlib.ticker import AutoMinorLocator, FixedFormatter, ScalarFormatter
- import matplotlib.pyplot as plt
- import matplotlib.dates as mdates
- import matplotlib.gridspec as gridspec
- import numpy as np
- import pytest
- @image_comparison(['figure_align_labels'],
- tol=0 if platform.machine() == 'x86_64' else 0.01)
- def test_align_labels():
- fig = plt.figure(tight_layout=True)
- gs = gridspec.GridSpec(3, 3)
- ax = fig.add_subplot(gs[0, :2])
- ax.plot(np.arange(0, 1e6, 1000))
- ax.set_ylabel('Ylabel0 0')
- ax = fig.add_subplot(gs[0, -1])
- ax.plot(np.arange(0, 1e4, 100))
- for i in range(3):
- ax = fig.add_subplot(gs[1, i])
- ax.set_ylabel('YLabel1 %d' % i)
- ax.set_xlabel('XLabel1 %d' % i)
- if i in [0, 2]:
- ax.xaxis.set_label_position("top")
- ax.xaxis.tick_top()
- if i == 0:
- for tick in ax.get_xticklabels():
- tick.set_rotation(90)
- if i == 2:
- ax.yaxis.set_label_position("right")
- ax.yaxis.tick_right()
- for i in range(3):
- ax = fig.add_subplot(gs[2, i])
- ax.set_xlabel(f'XLabel2 {i}')
- ax.set_ylabel(f'YLabel2 {i}')
- if i == 2:
- ax.plot(np.arange(0, 1e4, 10))
- ax.yaxis.set_label_position("right")
- ax.yaxis.tick_right()
- for tick in ax.get_xticklabels():
- tick.set_rotation(90)
- fig.align_labels()
- def test_figure_label():
- # pyplot figure creation, selection and closing with figure label and
- # number
- plt.close('all')
- plt.figure('today')
- plt.figure(3)
- plt.figure('tomorrow')
- plt.figure()
- plt.figure(0)
- plt.figure(1)
- plt.figure(3)
- assert plt.get_fignums() == [0, 1, 3, 4, 5]
- assert plt.get_figlabels() == ['', 'today', '', 'tomorrow', '']
- plt.close(10)
- plt.close()
- plt.close(5)
- plt.close('tomorrow')
- assert plt.get_fignums() == [0, 1]
- assert plt.get_figlabels() == ['', 'today']
- def test_fignum_exists():
- # pyplot figure creation, selection and closing with fignum_exists
- plt.figure('one')
- plt.figure(2)
- plt.figure('three')
- plt.figure()
- assert plt.fignum_exists('one')
- assert plt.fignum_exists(2)
- assert plt.fignum_exists('three')
- assert plt.fignum_exists(4)
- plt.close('one')
- plt.close(4)
- assert not plt.fignum_exists('one')
- assert not plt.fignum_exists(4)
- def test_clf_keyword():
- # test if existing figure is cleared with figure() and subplots()
- text1 = 'A fancy plot'
- text2 = 'Really fancy!'
- fig0 = plt.figure(num=1)
- fig0.suptitle(text1)
- assert [t.get_text() for t in fig0.texts] == [text1]
- fig1 = plt.figure(num=1, clear=False)
- fig1.text(0.5, 0.5, text2)
- assert fig0 is fig1
- assert [t.get_text() for t in fig1.texts] == [text1, text2]
- fig2, ax2 = plt.subplots(2, 1, num=1, clear=True)
- assert fig0 is fig2
- assert [t.get_text() for t in fig2.texts] == []
- @image_comparison(['figure_today'])
- def test_figure():
- # named figure support
- fig = plt.figure('today')
- ax = fig.add_subplot()
- ax.set_title(fig.get_label())
- ax.plot(np.arange(5))
- # plot red line in a different figure.
- plt.figure('tomorrow')
- plt.plot([0, 1], [1, 0], 'r')
- # Return to the original; make sure the red line is not there.
- plt.figure('today')
- plt.close('tomorrow')
- @image_comparison(['figure_legend'])
- def test_figure_legend():
- fig, axs = plt.subplots(2)
- axs[0].plot([0, 1], [1, 0], label='x', color='g')
- axs[0].plot([0, 1], [0, 1], label='y', color='r')
- axs[0].plot([0, 1], [0.5, 0.5], label='y', color='k')
- axs[1].plot([0, 1], [1, 0], label='_y', color='r')
- axs[1].plot([0, 1], [0, 1], label='z', color='b')
- fig.legend()
- def test_gca():
- fig = plt.figure()
- with pytest.warns(UserWarning):
- # empty call to add_axes() will throw deprecation warning
- assert fig.add_axes() is None
- ax0 = fig.add_axes([0, 0, 1, 1])
- assert fig.gca(projection='rectilinear') is ax0
- assert fig.gca() is ax0
- ax1 = fig.add_axes(rect=[0.1, 0.1, 0.8, 0.8])
- assert fig.gca(projection='rectilinear') is ax1
- assert fig.gca() is ax1
- ax2 = fig.add_subplot(121, projection='polar')
- assert fig.gca() is ax2
- assert fig.gca(polar=True) is ax2
- ax3 = fig.add_subplot(122)
- assert fig.gca() is ax3
- # the final request for a polar axes will end up creating one
- # with a spec of 111.
- with pytest.warns(UserWarning):
- # Changing the projection will throw a warning
- assert fig.gca(polar=True) is not ax3
- assert fig.gca(polar=True) is not ax2
- assert fig.gca().get_geometry() == (1, 1, 1)
- fig.sca(ax1)
- assert fig.gca(projection='rectilinear') is ax1
- assert fig.gca() is ax1
- def test_add_subplot_invalid():
- fig = plt.figure()
- with pytest.raises(ValueError,
- match='Number of columns must be a positive integer'):
- fig.add_subplot(2, 0, 1)
- with pytest.raises(ValueError,
- match='Number of rows must be a positive integer'):
- fig.add_subplot(0, 2, 1)
- with pytest.raises(ValueError, match='num must be 1 <= num <= 4'):
- fig.add_subplot(2, 2, 0)
- with pytest.raises(ValueError, match='num must be 1 <= num <= 4'):
- fig.add_subplot(2, 2, 5)
- with pytest.raises(ValueError, match='must be a three-digit integer'):
- fig.add_subplot(42)
- with pytest.raises(ValueError, match='must be a three-digit integer'):
- fig.add_subplot(1000)
- with pytest.raises(TypeError, match='takes 1 or 3 positional arguments '
- 'but 2 were given'):
- fig.add_subplot(2, 2)
- with pytest.raises(TypeError, match='takes 1 or 3 positional arguments '
- 'but 4 were given'):
- fig.add_subplot(1, 2, 3, 4)
- with pytest.warns(cbook.MatplotlibDeprecationWarning,
- match='Passing non-integers as three-element position '
- 'specification is deprecated'):
- fig.add_subplot('2', 2, 1)
- with pytest.warns(cbook.MatplotlibDeprecationWarning,
- match='Passing non-integers as three-element position '
- 'specification is deprecated'):
- fig.add_subplot(2.0, 2, 1)
- @image_comparison(['figure_suptitle'])
- def test_suptitle():
- fig, _ = plt.subplots()
- fig.suptitle('hello', color='r')
- fig.suptitle('title', color='g', rotation='30')
- def test_suptitle_fontproperties():
- fig, ax = plt.subplots()
- fps = mpl.font_manager.FontProperties(size='large', weight='bold')
- txt = fig.suptitle('fontprops title', fontproperties=fps)
- assert txt.get_fontsize() == fps.get_size_in_points()
- assert txt.get_weight() == fps.get_weight()
- @image_comparison(['alpha_background'],
- # only test png and svg. The PDF output appears correct,
- # but Ghostscript does not preserve the background color.
- extensions=['png', 'svg'],
- savefig_kwarg={'facecolor': (0, 1, 0.4),
- 'edgecolor': 'none'})
- def test_alpha():
- # We want an image which has a background color and an alpha of 0.4.
- fig = plt.figure(figsize=[2, 1])
- fig.set_facecolor((0, 1, 0.4))
- fig.patch.set_alpha(0.4)
- fig.patches.append(mpl.patches.CirclePolygon(
- [20, 20], radius=15, alpha=0.6, facecolor='red'))
- def test_too_many_figures():
- with pytest.warns(RuntimeWarning):
- for i in range(rcParams['figure.max_open_warning'] + 1):
- plt.figure()
- def test_iterability_axes_argument():
- # This is a regression test for matplotlib/matplotlib#3196. If one of the
- # arguments returned by _as_mpl_axes defines __getitem__ but is not
- # iterable, this would raise an exception. This is because we check
- # whether the arguments are iterable, and if so we try and convert them
- # to a tuple. However, the ``iterable`` function returns True if
- # __getitem__ is present, but some classes can define __getitem__ without
- # being iterable. The tuple conversion is now done in a try...except in
- # case it fails.
- class MyAxes(Axes):
- def __init__(self, *args, myclass=None, **kwargs):
- return Axes.__init__(self, *args, **kwargs)
- class MyClass:
- def __getitem__(self, item):
- if item != 'a':
- raise ValueError("item should be a")
- def _as_mpl_axes(self):
- return MyAxes, {'myclass': self}
- fig = plt.figure()
- fig.add_subplot(1, 1, 1, projection=MyClass())
- plt.close(fig)
- def test_set_fig_size():
- fig = plt.figure()
- # check figwidth
- fig.set_figwidth(5)
- assert fig.get_figwidth() == 5
- # check figheight
- fig.set_figheight(1)
- assert fig.get_figheight() == 1
- # check using set_size_inches
- fig.set_size_inches(2, 4)
- assert fig.get_figwidth() == 2
- assert fig.get_figheight() == 4
- # check using tuple to first argument
- fig.set_size_inches((1, 3))
- assert fig.get_figwidth() == 1
- assert fig.get_figheight() == 3
- def test_axes_remove():
- fig, axs = plt.subplots(2, 2)
- axs[-1, -1].remove()
- for ax in axs.ravel()[:-1]:
- assert ax in fig.axes
- assert axs[-1, -1] not in fig.axes
- assert len(fig.axes) == 3
- def test_figaspect():
- w, h = plt.figaspect(np.float64(2) / np.float64(1))
- assert h / w == 2
- w, h = plt.figaspect(2)
- assert h / w == 2
- w, h = plt.figaspect(np.zeros((1, 2)))
- assert h / w == 0.5
- w, h = plt.figaspect(np.zeros((2, 2)))
- assert h / w == 1
- @pytest.mark.parametrize('which', [None, 'both', 'major', 'minor'])
- def test_autofmt_xdate(which):
- date = ['3 Jan 2013', '4 Jan 2013', '5 Jan 2013', '6 Jan 2013',
- '7 Jan 2013', '8 Jan 2013', '9 Jan 2013', '10 Jan 2013',
- '11 Jan 2013', '12 Jan 2013', '13 Jan 2013', '14 Jan 2013']
- time = ['16:44:00', '16:45:00', '16:46:00', '16:47:00', '16:48:00',
- '16:49:00', '16:51:00', '16:52:00', '16:53:00', '16:55:00',
- '16:56:00', '16:57:00']
- angle = 60
- minors = [1, 2, 3, 4, 5, 6, 7]
- x = mdates.datestr2num(date)
- y = mdates.datestr2num(time)
- fig, ax = plt.subplots()
- ax.plot(x, y)
- ax.yaxis_date()
- ax.xaxis_date()
- ax.xaxis.set_minor_locator(AutoMinorLocator(2))
- with warnings.catch_warnings():
- warnings.filterwarnings(
- 'ignore',
- 'FixedFormatter should only be used together with FixedLocator')
- ax.xaxis.set_minor_formatter(FixedFormatter(minors))
- with (pytest.warns(mpl.MatplotlibDeprecationWarning) if which is None else
- nullcontext()):
- fig.autofmt_xdate(0.2, angle, 'right', which)
- if which in ('both', 'major', None):
- for label in fig.axes[0].get_xticklabels(False, 'major'):
- assert int(label.get_rotation()) == angle
- if which in ('both', 'minor'):
- for label in fig.axes[0].get_xticklabels(True, 'minor'):
- assert int(label.get_rotation()) == angle
- @pytest.mark.style('default')
- def test_change_dpi():
- fig = plt.figure(figsize=(4, 4))
- fig.canvas.draw()
- assert fig.canvas.renderer.height == 400
- assert fig.canvas.renderer.width == 400
- fig.dpi = 50
- fig.canvas.draw()
- assert fig.canvas.renderer.height == 200
- assert fig.canvas.renderer.width == 200
- @pytest.mark.parametrize('width, height', [
- (1, np.nan),
- (-1, 1),
- (np.inf, 1)
- ])
- def test_invalid_figure_size(width, height):
- with pytest.raises(ValueError):
- plt.figure(figsize=(width, height))
- fig = plt.figure()
- with pytest.raises(ValueError):
- fig.set_size_inches(width, height)
- def test_invalid_figure_add_axes():
- fig = plt.figure()
- with pytest.raises(ValueError):
- fig.add_axes((.1, .1, .5, np.nan))
- with pytest.raises(TypeError, match="multiple values for argument 'rect'"):
- fig.add_axes([0, 0, 1, 1], rect=[0, 0, 1, 1])
- def test_subplots_shareax_loglabels():
- fig, axs = plt.subplots(2, 2, sharex=True, sharey=True, squeeze=False)
- for ax in axs.flat:
- ax.plot([10, 20, 30], [10, 20, 30])
- ax.set_yscale("log")
- ax.set_xscale("log")
- for ax in axs[0, :]:
- assert 0 == len(ax.xaxis.get_ticklabels(which='both'))
- for ax in axs[1, :]:
- assert 0 < len(ax.xaxis.get_ticklabels(which='both'))
- for ax in axs[:, 1]:
- assert 0 == len(ax.yaxis.get_ticklabels(which='both'))
- for ax in axs[:, 0]:
- assert 0 < len(ax.yaxis.get_ticklabels(which='both'))
- def test_savefig():
- fig = plt.figure()
- msg = r"savefig\(\) takes 2 positional arguments but 3 were given"
- with pytest.raises(TypeError, match=msg):
- fig.savefig("fname1.png", "fname2.png")
- def test_savefig_warns():
- fig = plt.figure()
- msg = r'savefig\(\) got unexpected keyword argument "non_existent_kwarg"'
- for format in ['png', 'pdf', 'svg', 'tif', 'jpg']:
- with pytest.warns(cbook.MatplotlibDeprecationWarning, match=msg):
- fig.savefig(io.BytesIO(), format=format, non_existent_kwarg=True)
- def test_savefig_backend():
- fig = plt.figure()
- # Intentionally use an invalid module name.
- with pytest.raises(ModuleNotFoundError, match="No module named '@absent'"):
- fig.savefig("test", backend="module://@absent")
- with pytest.raises(ValueError,
- match="The 'pdf' backend does not support png output"):
- fig.savefig("test.png", backend="pdf")
- def test_figure_repr():
- fig = plt.figure(figsize=(10, 20), dpi=10)
- assert repr(fig) == "<Figure size 100x200 with 0 Axes>"
- def test_warn_cl_plus_tl():
- fig, ax = plt.subplots(constrained_layout=True)
- with pytest.warns(UserWarning):
- # this should warn,
- fig.subplots_adjust(top=0.8)
- assert not(fig.get_constrained_layout())
- @check_figures_equal(extensions=["png", "pdf"])
- def test_add_artist(fig_test, fig_ref):
- fig_test.set_dpi(100)
- fig_ref.set_dpi(100)
- fig_test.subplots()
- l1 = plt.Line2D([.2, .7], [.7, .7], gid='l1')
- l2 = plt.Line2D([.2, .7], [.8, .8], gid='l2')
- r1 = plt.Circle((20, 20), 100, transform=None, gid='C1')
- r2 = plt.Circle((.7, .5), .05, gid='C2')
- r3 = plt.Circle((4.5, .8), .55, transform=fig_test.dpi_scale_trans,
- facecolor='crimson', gid='C3')
- for a in [l1, l2, r1, r2, r3]:
- fig_test.add_artist(a)
- l2.remove()
- ax2 = fig_ref.subplots()
- l1 = plt.Line2D([.2, .7], [.7, .7], transform=fig_ref.transFigure,
- gid='l1', zorder=21)
- r1 = plt.Circle((20, 20), 100, transform=None, clip_on=False, zorder=20,
- gid='C1')
- r2 = plt.Circle((.7, .5), .05, transform=fig_ref.transFigure, gid='C2',
- zorder=20)
- r3 = plt.Circle((4.5, .8), .55, transform=fig_ref.dpi_scale_trans,
- facecolor='crimson', clip_on=False, zorder=20, gid='C3')
- for a in [l1, r1, r2, r3]:
- ax2.add_artist(a)
- @pytest.mark.parametrize("fmt", ["png", "pdf", "ps", "eps", "svg"])
- def test_fspath(fmt, tmpdir):
- out = Path(tmpdir, "test.{}".format(fmt))
- plt.savefig(out)
- with out.open("rb") as file:
- # All the supported formats include the format name (case-insensitive)
- # in the first 100 bytes.
- assert fmt.encode("ascii") in file.read(100).lower()
- def test_tightbbox():
- fig, ax = plt.subplots()
- ax.set_xlim(0, 1)
- t = ax.text(1., 0.5, 'This dangles over end')
- renderer = fig.canvas.get_renderer()
- x1Nom0 = 9.035 # inches
- assert abs(t.get_tightbbox(renderer).x1 - x1Nom0 * fig.dpi) < 2
- assert abs(ax.get_tightbbox(renderer).x1 - x1Nom0 * fig.dpi) < 2
- assert abs(fig.get_tightbbox(renderer).x1 - x1Nom0) < 0.05
- assert abs(fig.get_tightbbox(renderer).x0 - 0.679) < 0.05
- # now exclude t from the tight bbox so now the bbox is quite a bit
- # smaller
- t.set_in_layout(False)
- x1Nom = 7.333
- assert abs(ax.get_tightbbox(renderer).x1 - x1Nom * fig.dpi) < 2
- assert abs(fig.get_tightbbox(renderer).x1 - x1Nom) < 0.05
- t.set_in_layout(True)
- x1Nom = 7.333
- assert abs(ax.get_tightbbox(renderer).x1 - x1Nom0 * fig.dpi) < 2
- # test bbox_extra_artists method...
- assert abs(ax.get_tightbbox(renderer, bbox_extra_artists=[]).x1
- - x1Nom * fig.dpi) < 2
- def test_axes_removal():
- # Check that units can set the formatter after an Axes removal
- fig, axs = plt.subplots(1, 2, sharex=True)
- axs[1].remove()
- axs[0].plot([datetime(2000, 1, 1), datetime(2000, 2, 1)], [0, 1])
- assert isinstance(axs[0].xaxis.get_major_formatter(),
- mdates.AutoDateFormatter)
- # Check that manually setting the formatter, then removing Axes keeps
- # the set formatter.
- fig, axs = plt.subplots(1, 2, sharex=True)
- axs[1].xaxis.set_major_formatter(ScalarFormatter())
- axs[1].remove()
- axs[0].plot([datetime(2000, 1, 1), datetime(2000, 2, 1)], [0, 1])
- assert isinstance(axs[0].xaxis.get_major_formatter(),
- ScalarFormatter)
- def test_removed_axis():
- # Simple smoke test to make sure removing a shared axis works
- fig, axs = plt.subplots(2, sharex=True)
- axs[0].remove()
- fig.canvas.draw()
- @pytest.mark.style('mpl20')
- def test_picking_does_not_stale():
- fig, ax = plt.subplots()
- col = ax.scatter([0], [0], [1000], picker=True)
- fig.canvas.draw()
- assert not fig.stale
- mouse_event = SimpleNamespace(x=ax.bbox.x0 + ax.bbox.width / 2,
- y=ax.bbox.y0 + ax.bbox.height / 2,
- inaxes=ax, guiEvent=None)
- fig.pick(mouse_event)
- assert not fig.stale
- def test_add_subplot_twotuple():
- fig = plt.figure()
- ax1 = fig.add_subplot(3, 2, (3, 5))
- assert ax1.get_subplotspec().rowspan == range(1, 3)
- assert ax1.get_subplotspec().colspan == range(0, 1)
- ax2 = fig.add_subplot(3, 2, (4, 6))
- assert ax2.get_subplotspec().rowspan == range(1, 3)
- assert ax2.get_subplotspec().colspan == range(1, 2)
- ax3 = fig.add_subplot(3, 2, (3, 6))
- assert ax3.get_subplotspec().rowspan == range(1, 3)
- assert ax3.get_subplotspec().colspan == range(0, 2)
- ax4 = fig.add_subplot(3, 2, (4, 5))
- assert ax4.get_subplotspec().rowspan == range(1, 3)
- assert ax4.get_subplotspec().colspan == range(0, 2)
- with pytest.raises(IndexError):
- fig.add_subplot(3, 2, (6, 3))
- @image_comparison(['tightbbox_box_aspect.svg'], style='mpl20',
- savefig_kwarg={'bbox_inches': 'tight',
- 'facecolor': 'teal'},
- remove_text=True)
- def test_tightbbox_box_aspect():
- fig = plt.figure()
- gs = fig.add_gridspec(1, 2)
- ax1 = fig.add_subplot(gs[0, 0])
- ax2 = fig.add_subplot(gs[0, 1], projection='3d')
- ax1.set_box_aspect(.5)
- ax2.set_box_aspect((2, 1, 1))
- @check_figures_equal(extensions=["svg", "pdf", "eps", "png"])
- def test_animated_with_canvas_change(fig_test, fig_ref):
- ax_ref = fig_ref.subplots()
- ax_ref.plot(range(5))
- ax_test = fig_test.subplots()
- ax_test.plot(range(5), animated=True)
- class TestSubplotMosaic:
- @check_figures_equal(extensions=["png"])
- @pytest.mark.parametrize(
- "x", [[["A", "A", "B"], ["C", "D", "B"]], [[1, 1, 2], [3, 4, 2]]]
- )
- def test_basic(self, fig_test, fig_ref, x):
- grid_axes = fig_test.subplot_mosaic(x)
- for k, ax in grid_axes.items():
- ax.set_title(k)
- labels = sorted(np.unique(x))
- assert len(labels) == len(grid_axes)
- gs = fig_ref.add_gridspec(2, 3)
- axA = fig_ref.add_subplot(gs[:1, :2])
- axA.set_title(labels[0])
- axB = fig_ref.add_subplot(gs[:, 2])
- axB.set_title(labels[1])
- axC = fig_ref.add_subplot(gs[1, 0])
- axC.set_title(labels[2])
- axD = fig_ref.add_subplot(gs[1, 1])
- axD.set_title(labels[3])
- @check_figures_equal(extensions=["png"])
- def test_all_nested(self, fig_test, fig_ref):
- x = [["A", "B"], ["C", "D"]]
- y = [["E", "F"], ["G", "H"]]
- fig_ref.set_constrained_layout(True)
- fig_test.set_constrained_layout(True)
- grid_axes = fig_test.subplot_mosaic([[x, y]])
- for ax in grid_axes.values():
- ax.set_title(ax.get_label())
- gs = fig_ref.add_gridspec(1, 2)
- gs_left = gs[0, 0].subgridspec(2, 2)
- for j, r in enumerate(x):
- for k, label in enumerate(r):
- fig_ref.add_subplot(gs_left[j, k]).set_title(label)
- gs_right = gs[0, 1].subgridspec(2, 2)
- for j, r in enumerate(y):
- for k, label in enumerate(r):
- fig_ref.add_subplot(gs_right[j, k]).set_title(label)
- @check_figures_equal(extensions=["png"])
- def test_nested(self, fig_test, fig_ref):
- fig_ref.set_constrained_layout(True)
- fig_test.set_constrained_layout(True)
- x = [["A", "B"], ["C", "D"]]
- y = [["F"], [x]]
- grid_axes = fig_test.subplot_mosaic(y)
- for k, ax in grid_axes.items():
- ax.set_title(k)
- gs = fig_ref.add_gridspec(2, 1)
- gs_n = gs[1, 0].subgridspec(2, 2)
- axA = fig_ref.add_subplot(gs_n[0, 0])
- axA.set_title("A")
- axB = fig_ref.add_subplot(gs_n[0, 1])
- axB.set_title("B")
- axC = fig_ref.add_subplot(gs_n[1, 0])
- axC.set_title("C")
- axD = fig_ref.add_subplot(gs_n[1, 1])
- axD.set_title("D")
- axF = fig_ref.add_subplot(gs[0, 0])
- axF.set_title("F")
- @check_figures_equal(extensions=["png"])
- def test_nested_tuple(self, fig_test, fig_ref):
- x = [["A", "B", "B"], ["C", "C", "D"]]
- xt = (("A", "B", "B"), ("C", "C", "D"))
- fig_ref.subplot_mosaic([["F"], [x]])
- fig_test.subplot_mosaic([["F"], [xt]])
- @check_figures_equal(extensions=["png"])
- @pytest.mark.parametrize(
- "x, empty_sentinel",
- [
- ([["A", None], [None, "B"]], None),
- ([["A", "."], [".", "B"]], "SKIP"),
- ([["A", 0], [0, "B"]], 0),
- ([[1, None], [None, 2]], None),
- ([[1, "."], [".", 2]], "SKIP"),
- ([[1, 0], [0, 2]], 0),
- ],
- )
- def test_empty(self, fig_test, fig_ref, x, empty_sentinel):
- if empty_sentinel != "SKIP":
- kwargs = {"empty_sentinel": empty_sentinel}
- else:
- kwargs = {}
- grid_axes = fig_test.subplot_mosaic(x, **kwargs)
- for k, ax in grid_axes.items():
- ax.set_title(k)
- labels = sorted(
- {name for row in x for name in row} - {empty_sentinel, "."}
- )
- assert len(labels) == len(grid_axes)
- gs = fig_ref.add_gridspec(2, 2)
- axA = fig_ref.add_subplot(gs[0, 0])
- axA.set_title(labels[0])
- axB = fig_ref.add_subplot(gs[1, 1])
- axB.set_title(labels[1])
- def test_fail_list_of_str(self):
- with pytest.raises(ValueError, match='must be 2D'):
- plt.subplot_mosaic(['foo', 'bar'])
- @check_figures_equal(extensions=["png"])
- @pytest.mark.parametrize("subplot_kw", [{}, {"projection": "polar"}, None])
- def test_subplot_kw(self, fig_test, fig_ref, subplot_kw):
- x = [[1, 2]]
- grid_axes = fig_test.subplot_mosaic(x, subplot_kw=subplot_kw)
- subplot_kw = subplot_kw or {}
- gs = fig_ref.add_gridspec(1, 2)
- axA = fig_ref.add_subplot(gs[0, 0], **subplot_kw)
- axB = fig_ref.add_subplot(gs[0, 1], **subplot_kw)
- @check_figures_equal(extensions=["png"])
- @pytest.mark.parametrize("str_pattern",
- ["AAA\nBBB", "\nAAA\nBBB\n", "ABC\nDEF"]
- )
- def test_single_str_input(self, fig_test, fig_ref, str_pattern):
- grid_axes = fig_test.subplot_mosaic(str_pattern)
- grid_axes = fig_ref.subplot_mosaic(
- [list(ln) for ln in str_pattern.strip().split("\n")]
- )
- @pytest.mark.parametrize(
- "x,match",
- [
- (
- [["A", "."], [".", "A"]],
- (
- "(?m)we found that the label .A. specifies a "
- + "non-rectangular or non-contiguous area."
- ),
- ),
- (
- [["A", "B"], [None, [["A", "B"], ["C", "D"]]]],
- "There are duplicate keys .* between the outer layout",
- ),
- ("AAA\nc\nBBB", "All of the rows must be the same length"),
- (
- [["A", [["B", "C"], ["D"]]], ["E", "E"]],
- "All of the rows must be the same length",
- ),
- ],
- )
- def test_fail(self, x, match):
- fig = plt.figure()
- with pytest.raises(ValueError, match=match):
- fig.subplot_mosaic(x)
- @check_figures_equal(extensions=["png"])
- def test_hashable_keys(self, fig_test, fig_ref):
- fig_test.subplot_mosaic([[object(), object()]])
- fig_ref.subplot_mosaic([["A", "B"]])
|