test_constrainedlayout.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
  1. import gc
  2. import numpy as np
  3. import pytest
  4. import matplotlib as mpl
  5. from matplotlib.testing.decorators import image_comparison
  6. import matplotlib.pyplot as plt
  7. import matplotlib.transforms as mtransforms
  8. from matplotlib import gridspec, ticker
  9. def example_plot(ax, fontsize=12, nodec=False):
  10. ax.plot([1, 2])
  11. ax.locator_params(nbins=3)
  12. if not nodec:
  13. ax.set_xlabel('x-label', fontsize=fontsize)
  14. ax.set_ylabel('y-label', fontsize=fontsize)
  15. ax.set_title('Title', fontsize=fontsize)
  16. else:
  17. ax.set_xticklabels([])
  18. ax.set_yticklabels([])
  19. def example_pcolor(ax, fontsize=12):
  20. dx, dy = 0.6, 0.6
  21. y, x = np.mgrid[slice(-3, 3 + dy, dy),
  22. slice(-3, 3 + dx, dx)]
  23. z = (1 - x / 2. + x ** 5 + y ** 3) * np.exp(-x ** 2 - y ** 2)
  24. pcm = ax.pcolormesh(x, y, z[:-1, :-1], cmap='RdBu_r', vmin=-1., vmax=1.,
  25. rasterized=True)
  26. ax.set_xlabel('x-label', fontsize=fontsize)
  27. ax.set_ylabel('y-label', fontsize=fontsize)
  28. ax.set_title('Title', fontsize=fontsize)
  29. return pcm
  30. @image_comparison(['constrained_layout1.png'])
  31. def test_constrained_layout1():
  32. """Test constrained_layout for a single subplot"""
  33. fig = plt.figure(layout="constrained")
  34. ax = fig.add_subplot()
  35. example_plot(ax, fontsize=24)
  36. @image_comparison(['constrained_layout2.png'])
  37. def test_constrained_layout2():
  38. """Test constrained_layout for 2x2 subplots"""
  39. fig, axs = plt.subplots(2, 2, layout="constrained")
  40. for ax in axs.flat:
  41. example_plot(ax, fontsize=24)
  42. @image_comparison(['constrained_layout3.png'])
  43. def test_constrained_layout3():
  44. """Test constrained_layout for colorbars with subplots"""
  45. fig, axs = plt.subplots(2, 2, layout="constrained")
  46. for nn, ax in enumerate(axs.flat):
  47. pcm = example_pcolor(ax, fontsize=24)
  48. if nn == 3:
  49. pad = 0.08
  50. else:
  51. pad = 0.02 # default
  52. fig.colorbar(pcm, ax=ax, pad=pad)
  53. @image_comparison(['constrained_layout4.png'])
  54. def test_constrained_layout4():
  55. """Test constrained_layout for a single colorbar with subplots"""
  56. fig, axs = plt.subplots(2, 2, layout="constrained")
  57. for ax in axs.flat:
  58. pcm = example_pcolor(ax, fontsize=24)
  59. fig.colorbar(pcm, ax=axs, pad=0.01, shrink=0.6)
  60. @image_comparison(['constrained_layout5.png'], tol=0.002)
  61. def test_constrained_layout5():
  62. """
  63. Test constrained_layout for a single colorbar with subplots,
  64. colorbar bottom
  65. """
  66. fig, axs = plt.subplots(2, 2, layout="constrained")
  67. for ax in axs.flat:
  68. pcm = example_pcolor(ax, fontsize=24)
  69. fig.colorbar(pcm, ax=axs,
  70. use_gridspec=False, pad=0.01, shrink=0.6,
  71. location='bottom')
  72. @image_comparison(['constrained_layout6.png'], tol=0.002)
  73. def test_constrained_layout6():
  74. """Test constrained_layout for nested gridspecs"""
  75. # Remove this line when this test image is regenerated.
  76. plt.rcParams['pcolormesh.snap'] = False
  77. fig = plt.figure(layout="constrained")
  78. gs = fig.add_gridspec(1, 2, figure=fig)
  79. gsl = gs[0].subgridspec(2, 2)
  80. gsr = gs[1].subgridspec(1, 2)
  81. axsl = []
  82. for gs in gsl:
  83. ax = fig.add_subplot(gs)
  84. axsl += [ax]
  85. example_plot(ax, fontsize=12)
  86. ax.set_xlabel('x-label\nMultiLine')
  87. axsr = []
  88. for gs in gsr:
  89. ax = fig.add_subplot(gs)
  90. axsr += [ax]
  91. pcm = example_pcolor(ax, fontsize=12)
  92. fig.colorbar(pcm, ax=axsr,
  93. pad=0.01, shrink=0.99, location='bottom',
  94. ticks=ticker.MaxNLocator(nbins=5))
  95. def test_identical_subgridspec():
  96. fig = plt.figure(constrained_layout=True)
  97. GS = fig.add_gridspec(2, 1)
  98. GSA = GS[0].subgridspec(1, 3)
  99. GSB = GS[1].subgridspec(1, 3)
  100. axa = []
  101. axb = []
  102. for i in range(3):
  103. axa += [fig.add_subplot(GSA[i])]
  104. axb += [fig.add_subplot(GSB[i])]
  105. fig.draw_without_rendering()
  106. # check first row above second
  107. assert axa[0].get_position().y0 > axb[0].get_position().y1
  108. def test_constrained_layout7():
  109. """Test for proper warning if fig not set in GridSpec"""
  110. with pytest.warns(
  111. UserWarning, match=('There are no gridspecs with layoutgrids. '
  112. 'Possibly did not call parent GridSpec with '
  113. 'the "figure" keyword')):
  114. fig = plt.figure(layout="constrained")
  115. gs = gridspec.GridSpec(1, 2)
  116. gsl = gridspec.GridSpecFromSubplotSpec(2, 2, gs[0])
  117. gsr = gridspec.GridSpecFromSubplotSpec(1, 2, gs[1])
  118. for gs in gsl:
  119. fig.add_subplot(gs)
  120. # need to trigger a draw to get warning
  121. fig.draw_without_rendering()
  122. @image_comparison(['constrained_layout8.png'])
  123. def test_constrained_layout8():
  124. """Test for gridspecs that are not completely full"""
  125. fig = plt.figure(figsize=(10, 5), layout="constrained")
  126. gs = gridspec.GridSpec(3, 5, figure=fig)
  127. axs = []
  128. for j in [0, 1]:
  129. if j == 0:
  130. ilist = [1]
  131. else:
  132. ilist = [0, 4]
  133. for i in ilist:
  134. ax = fig.add_subplot(gs[j, i])
  135. axs += [ax]
  136. example_pcolor(ax, fontsize=9)
  137. if i > 0:
  138. ax.set_ylabel('')
  139. if j < 1:
  140. ax.set_xlabel('')
  141. ax.set_title('')
  142. ax = fig.add_subplot(gs[2, :])
  143. axs += [ax]
  144. pcm = example_pcolor(ax, fontsize=9)
  145. fig.colorbar(pcm, ax=axs, pad=0.01, shrink=0.6)
  146. @image_comparison(['constrained_layout9.png'])
  147. def test_constrained_layout9():
  148. """Test for handling suptitle and for sharex and sharey"""
  149. fig, axs = plt.subplots(2, 2, layout="constrained",
  150. sharex=False, sharey=False)
  151. for ax in axs.flat:
  152. pcm = example_pcolor(ax, fontsize=24)
  153. ax.set_xlabel('')
  154. ax.set_ylabel('')
  155. ax.set_aspect(2.)
  156. fig.colorbar(pcm, ax=axs, pad=0.01, shrink=0.6)
  157. fig.suptitle('Test Suptitle', fontsize=28)
  158. @image_comparison(['constrained_layout10.png'])
  159. def test_constrained_layout10():
  160. """Test for handling legend outside axis"""
  161. fig, axs = plt.subplots(2, 2, layout="constrained")
  162. for ax in axs.flat:
  163. ax.plot(np.arange(12), label='This is a label')
  164. ax.legend(loc='center left', bbox_to_anchor=(0.8, 0.5))
  165. @image_comparison(['constrained_layout11.png'])
  166. def test_constrained_layout11():
  167. """Test for multiple nested gridspecs"""
  168. fig = plt.figure(layout="constrained", figsize=(13, 3))
  169. gs0 = gridspec.GridSpec(1, 2, figure=fig)
  170. gsl = gridspec.GridSpecFromSubplotSpec(1, 2, gs0[0])
  171. gsl0 = gridspec.GridSpecFromSubplotSpec(2, 2, gsl[1])
  172. ax = fig.add_subplot(gs0[1])
  173. example_plot(ax, fontsize=9)
  174. axs = []
  175. for gs in gsl0:
  176. ax = fig.add_subplot(gs)
  177. axs += [ax]
  178. pcm = example_pcolor(ax, fontsize=9)
  179. fig.colorbar(pcm, ax=axs, shrink=0.6, aspect=70.)
  180. ax = fig.add_subplot(gsl[0])
  181. example_plot(ax, fontsize=9)
  182. @image_comparison(['constrained_layout11rat.png'])
  183. def test_constrained_layout11rat():
  184. """Test for multiple nested gridspecs with width_ratios"""
  185. fig = plt.figure(layout="constrained", figsize=(10, 3))
  186. gs0 = gridspec.GridSpec(1, 2, figure=fig, width_ratios=[6, 1])
  187. gsl = gridspec.GridSpecFromSubplotSpec(1, 2, gs0[0])
  188. gsl0 = gridspec.GridSpecFromSubplotSpec(2, 2, gsl[1], height_ratios=[2, 1])
  189. ax = fig.add_subplot(gs0[1])
  190. example_plot(ax, fontsize=9)
  191. axs = []
  192. for gs in gsl0:
  193. ax = fig.add_subplot(gs)
  194. axs += [ax]
  195. pcm = example_pcolor(ax, fontsize=9)
  196. fig.colorbar(pcm, ax=axs, shrink=0.6, aspect=70.)
  197. ax = fig.add_subplot(gsl[0])
  198. example_plot(ax, fontsize=9)
  199. @image_comparison(['constrained_layout12.png'])
  200. def test_constrained_layout12():
  201. """Test that very unbalanced labeling still works."""
  202. fig = plt.figure(layout="constrained", figsize=(6, 8))
  203. gs0 = gridspec.GridSpec(6, 2, figure=fig)
  204. ax1 = fig.add_subplot(gs0[:3, 1])
  205. ax2 = fig.add_subplot(gs0[3:, 1])
  206. example_plot(ax1, fontsize=18)
  207. example_plot(ax2, fontsize=18)
  208. ax = fig.add_subplot(gs0[0:2, 0])
  209. example_plot(ax, nodec=True)
  210. ax = fig.add_subplot(gs0[2:4, 0])
  211. example_plot(ax, nodec=True)
  212. ax = fig.add_subplot(gs0[4:, 0])
  213. example_plot(ax, nodec=True)
  214. ax.set_xlabel('x-label')
  215. @image_comparison(['constrained_layout13.png'], tol=2.e-2)
  216. def test_constrained_layout13():
  217. """Test that padding works."""
  218. fig, axs = plt.subplots(2, 2, layout="constrained")
  219. for ax in axs.flat:
  220. pcm = example_pcolor(ax, fontsize=12)
  221. fig.colorbar(pcm, ax=ax, shrink=0.6, aspect=20., pad=0.02)
  222. with pytest.raises(TypeError):
  223. fig.get_layout_engine().set(wpad=1, hpad=2)
  224. fig.get_layout_engine().set(w_pad=24./72., h_pad=24./72.)
  225. @image_comparison(['constrained_layout14.png'])
  226. def test_constrained_layout14():
  227. """Test that padding works."""
  228. fig, axs = plt.subplots(2, 2, layout="constrained")
  229. for ax in axs.flat:
  230. pcm = example_pcolor(ax, fontsize=12)
  231. fig.colorbar(pcm, ax=ax, shrink=0.6, aspect=20., pad=0.02)
  232. fig.get_layout_engine().set(
  233. w_pad=3./72., h_pad=3./72.,
  234. hspace=0.2, wspace=0.2)
  235. @image_comparison(['constrained_layout15.png'])
  236. def test_constrained_layout15():
  237. """Test that rcparams work."""
  238. mpl.rcParams['figure.constrained_layout.use'] = True
  239. fig, axs = plt.subplots(2, 2)
  240. for ax in axs.flat:
  241. example_plot(ax, fontsize=12)
  242. @image_comparison(['constrained_layout16.png'])
  243. def test_constrained_layout16():
  244. """Test ax.set_position."""
  245. fig, ax = plt.subplots(layout="constrained")
  246. example_plot(ax, fontsize=12)
  247. ax2 = fig.add_axes([0.2, 0.2, 0.4, 0.4])
  248. @image_comparison(['constrained_layout17.png'])
  249. def test_constrained_layout17():
  250. """Test uneven gridspecs"""
  251. fig = plt.figure(layout="constrained")
  252. gs = gridspec.GridSpec(3, 3, figure=fig)
  253. ax1 = fig.add_subplot(gs[0, 0])
  254. ax2 = fig.add_subplot(gs[0, 1:])
  255. ax3 = fig.add_subplot(gs[1:, 0:2])
  256. ax4 = fig.add_subplot(gs[1:, -1])
  257. example_plot(ax1)
  258. example_plot(ax2)
  259. example_plot(ax3)
  260. example_plot(ax4)
  261. def test_constrained_layout18():
  262. """Test twinx"""
  263. fig, ax = plt.subplots(layout="constrained")
  264. ax2 = ax.twinx()
  265. example_plot(ax)
  266. example_plot(ax2, fontsize=24)
  267. fig.draw_without_rendering()
  268. assert all(ax.get_position().extents == ax2.get_position().extents)
  269. def test_constrained_layout19():
  270. """Test twiny"""
  271. fig, ax = plt.subplots(layout="constrained")
  272. ax2 = ax.twiny()
  273. example_plot(ax)
  274. example_plot(ax2, fontsize=24)
  275. ax2.set_title('')
  276. ax.set_title('')
  277. fig.draw_without_rendering()
  278. assert all(ax.get_position().extents == ax2.get_position().extents)
  279. def test_constrained_layout20():
  280. """Smoke test cl does not mess up added axes"""
  281. gx = np.linspace(-5, 5, 4)
  282. img = np.hypot(gx, gx[:, None])
  283. fig = plt.figure()
  284. ax = fig.add_axes([0, 0, 1, 1])
  285. mesh = ax.pcolormesh(gx, gx, img[:-1, :-1])
  286. fig.colorbar(mesh)
  287. def test_constrained_layout21():
  288. """#11035: repeated calls to suptitle should not alter the layout"""
  289. fig, ax = plt.subplots(layout="constrained")
  290. fig.suptitle("Suptitle0")
  291. fig.draw_without_rendering()
  292. extents0 = np.copy(ax.get_position().extents)
  293. fig.suptitle("Suptitle1")
  294. fig.draw_without_rendering()
  295. extents1 = np.copy(ax.get_position().extents)
  296. np.testing.assert_allclose(extents0, extents1)
  297. def test_constrained_layout22():
  298. """#11035: suptitle should not be include in CL if manually positioned"""
  299. fig, ax = plt.subplots(layout="constrained")
  300. fig.draw_without_rendering()
  301. extents0 = np.copy(ax.get_position().extents)
  302. fig.suptitle("Suptitle", y=0.5)
  303. fig.draw_without_rendering()
  304. extents1 = np.copy(ax.get_position().extents)
  305. np.testing.assert_allclose(extents0, extents1)
  306. def test_constrained_layout23():
  307. """
  308. Comment in #11035: suptitle used to cause an exception when
  309. reusing a figure w/ CL with ``clear=True``.
  310. """
  311. for i in range(2):
  312. fig = plt.figure(layout="constrained", clear=True, num="123")
  313. gs = fig.add_gridspec(1, 2)
  314. sub = gs[0].subgridspec(2, 2)
  315. fig.suptitle(f"Suptitle{i}")
  316. @image_comparison(['test_colorbar_location.png'],
  317. remove_text=True, style='mpl20')
  318. def test_colorbar_location():
  319. """
  320. Test that colorbar handling is as expected for various complicated
  321. cases...
  322. """
  323. # Remove this line when this test image is regenerated.
  324. plt.rcParams['pcolormesh.snap'] = False
  325. fig, axs = plt.subplots(4, 5, layout="constrained")
  326. for ax in axs.flat:
  327. pcm = example_pcolor(ax)
  328. ax.set_xlabel('')
  329. ax.set_ylabel('')
  330. fig.colorbar(pcm, ax=axs[:, 1], shrink=0.4)
  331. fig.colorbar(pcm, ax=axs[-1, :2], shrink=0.5, location='bottom')
  332. fig.colorbar(pcm, ax=axs[0, 2:], shrink=0.5, location='bottom', pad=0.05)
  333. fig.colorbar(pcm, ax=axs[-2, 3:], shrink=0.5, location='top')
  334. fig.colorbar(pcm, ax=axs[0, 0], shrink=0.5, location='left')
  335. fig.colorbar(pcm, ax=axs[1:3, 2], shrink=0.5, location='right')
  336. def test_hidden_axes():
  337. # test that if we make an Axes not visible that constrained_layout
  338. # still works. Note the axes still takes space in the layout
  339. # (as does a gridspec slot that is empty)
  340. fig, axs = plt.subplots(2, 2, layout="constrained")
  341. axs[0, 1].set_visible(False)
  342. fig.draw_without_rendering()
  343. extents1 = np.copy(axs[0, 0].get_position().extents)
  344. np.testing.assert_allclose(
  345. extents1, [0.045552, 0.543288, 0.47819, 0.982638], rtol=1e-5)
  346. def test_colorbar_align():
  347. for location in ['right', 'left', 'top', 'bottom']:
  348. fig, axs = plt.subplots(2, 2, layout="constrained")
  349. cbs = []
  350. for nn, ax in enumerate(axs.flat):
  351. ax.tick_params(direction='in')
  352. pc = example_pcolor(ax)
  353. cb = fig.colorbar(pc, ax=ax, location=location, shrink=0.6,
  354. pad=0.04)
  355. cbs += [cb]
  356. cb.ax.tick_params(direction='in')
  357. if nn != 1:
  358. cb.ax.xaxis.set_ticks([])
  359. cb.ax.yaxis.set_ticks([])
  360. ax.set_xticklabels([])
  361. ax.set_yticklabels([])
  362. fig.get_layout_engine().set(w_pad=4 / 72, h_pad=4 / 72,
  363. hspace=0.1, wspace=0.1)
  364. fig.draw_without_rendering()
  365. if location in ['left', 'right']:
  366. np.testing.assert_allclose(cbs[0].ax.get_position().x0,
  367. cbs[2].ax.get_position().x0)
  368. np.testing.assert_allclose(cbs[1].ax.get_position().x0,
  369. cbs[3].ax.get_position().x0)
  370. else:
  371. np.testing.assert_allclose(cbs[0].ax.get_position().y0,
  372. cbs[1].ax.get_position().y0)
  373. np.testing.assert_allclose(cbs[2].ax.get_position().y0,
  374. cbs[3].ax.get_position().y0)
  375. @image_comparison(['test_colorbars_no_overlapV.png'], style='mpl20')
  376. def test_colorbars_no_overlapV():
  377. fig = plt.figure(figsize=(2, 4), layout="constrained")
  378. axs = fig.subplots(2, 1, sharex=True, sharey=True)
  379. for ax in axs:
  380. ax.yaxis.set_major_formatter(ticker.NullFormatter())
  381. ax.tick_params(axis='both', direction='in')
  382. im = ax.imshow([[1, 2], [3, 4]])
  383. fig.colorbar(im, ax=ax, orientation="vertical")
  384. fig.suptitle("foo")
  385. @image_comparison(['test_colorbars_no_overlapH.png'], style='mpl20')
  386. def test_colorbars_no_overlapH():
  387. fig = plt.figure(figsize=(4, 2), layout="constrained")
  388. fig.suptitle("foo")
  389. axs = fig.subplots(1, 2, sharex=True, sharey=True)
  390. for ax in axs:
  391. ax.yaxis.set_major_formatter(ticker.NullFormatter())
  392. ax.tick_params(axis='both', direction='in')
  393. im = ax.imshow([[1, 2], [3, 4]])
  394. fig.colorbar(im, ax=ax, orientation="horizontal")
  395. def test_manually_set_position():
  396. fig, axs = plt.subplots(1, 2, layout="constrained")
  397. axs[0].set_position([0.2, 0.2, 0.3, 0.3])
  398. fig.draw_without_rendering()
  399. pp = axs[0].get_position()
  400. np.testing.assert_allclose(pp, [[0.2, 0.2], [0.5, 0.5]])
  401. fig, axs = plt.subplots(1, 2, layout="constrained")
  402. axs[0].set_position([0.2, 0.2, 0.3, 0.3])
  403. pc = axs[0].pcolormesh(np.random.rand(20, 20))
  404. fig.colorbar(pc, ax=axs[0])
  405. fig.draw_without_rendering()
  406. pp = axs[0].get_position()
  407. np.testing.assert_allclose(pp, [[0.2, 0.2], [0.44, 0.5]])
  408. @image_comparison(['test_bboxtight.png'],
  409. remove_text=True, style='mpl20',
  410. savefig_kwarg={'bbox_inches': 'tight'})
  411. def test_bboxtight():
  412. fig, ax = plt.subplots(layout="constrained")
  413. ax.set_aspect(1.)
  414. @image_comparison(['test_bbox.png'],
  415. remove_text=True, style='mpl20',
  416. savefig_kwarg={'bbox_inches':
  417. mtransforms.Bbox([[0.5, 0], [2.5, 2]])})
  418. def test_bbox():
  419. fig, ax = plt.subplots(layout="constrained")
  420. ax.set_aspect(1.)
  421. def test_align_labels():
  422. """
  423. Tests for a bug in which constrained layout and align_ylabels on
  424. three unevenly sized subplots, one of whose y tick labels include
  425. negative numbers, drives the non-negative subplots' y labels off
  426. the edge of the plot
  427. """
  428. fig, (ax3, ax1, ax2) = plt.subplots(3, 1, layout="constrained",
  429. figsize=(6.4, 8),
  430. gridspec_kw={"height_ratios": (1, 1,
  431. 0.7)})
  432. ax1.set_ylim(0, 1)
  433. ax1.set_ylabel("Label")
  434. ax2.set_ylim(-1.5, 1.5)
  435. ax2.set_ylabel("Label")
  436. ax3.set_ylim(0, 1)
  437. ax3.set_ylabel("Label")
  438. fig.align_ylabels(axs=(ax3, ax1, ax2))
  439. fig.draw_without_rendering()
  440. after_align = [ax1.yaxis.label.get_window_extent(),
  441. ax2.yaxis.label.get_window_extent(),
  442. ax3.yaxis.label.get_window_extent()]
  443. # ensure labels are approximately aligned
  444. np.testing.assert_allclose([after_align[0].x0, after_align[2].x0],
  445. after_align[1].x0, rtol=0, atol=1e-05)
  446. # ensure labels do not go off the edge
  447. assert after_align[0].x0 >= 1
  448. def test_suplabels():
  449. fig, ax = plt.subplots(layout="constrained")
  450. fig.draw_without_rendering()
  451. pos0 = ax.get_tightbbox(fig.canvas.get_renderer())
  452. fig.supxlabel('Boo')
  453. fig.supylabel('Booy')
  454. fig.draw_without_rendering()
  455. pos = ax.get_tightbbox(fig.canvas.get_renderer())
  456. assert pos.y0 > pos0.y0 + 10.0
  457. assert pos.x0 > pos0.x0 + 10.0
  458. fig, ax = plt.subplots(layout="constrained")
  459. fig.draw_without_rendering()
  460. pos0 = ax.get_tightbbox(fig.canvas.get_renderer())
  461. # check that specifying x (y) doesn't ruin the layout
  462. fig.supxlabel('Boo', x=0.5)
  463. fig.supylabel('Boo', y=0.5)
  464. fig.draw_without_rendering()
  465. pos = ax.get_tightbbox(fig.canvas.get_renderer())
  466. assert pos.y0 > pos0.y0 + 10.0
  467. assert pos.x0 > pos0.x0 + 10.0
  468. def test_gridspec_addressing():
  469. fig = plt.figure()
  470. gs = fig.add_gridspec(3, 3)
  471. sp = fig.add_subplot(gs[0:, 1:])
  472. fig.draw_without_rendering()
  473. def test_discouraged_api():
  474. fig, ax = plt.subplots(constrained_layout=True)
  475. fig.draw_without_rendering()
  476. with pytest.warns(PendingDeprecationWarning,
  477. match="will be deprecated"):
  478. fig, ax = plt.subplots()
  479. fig.set_constrained_layout(True)
  480. fig.draw_without_rendering()
  481. with pytest.warns(PendingDeprecationWarning,
  482. match="will be deprecated"):
  483. fig, ax = plt.subplots()
  484. fig.set_constrained_layout({'w_pad': 0.02, 'h_pad': 0.02})
  485. fig.draw_without_rendering()
  486. def test_kwargs():
  487. fig, ax = plt.subplots(constrained_layout={'h_pad': 0.02})
  488. fig.draw_without_rendering()
  489. def test_rect():
  490. fig, ax = plt.subplots(layout='constrained')
  491. fig.get_layout_engine().set(rect=[0, 0, 0.5, 0.5])
  492. fig.draw_without_rendering()
  493. ppos = ax.get_position()
  494. assert ppos.x1 < 0.5
  495. assert ppos.y1 < 0.5
  496. fig, ax = plt.subplots(layout='constrained')
  497. fig.get_layout_engine().set(rect=[0.2, 0.2, 0.3, 0.3])
  498. fig.draw_without_rendering()
  499. ppos = ax.get_position()
  500. assert ppos.x1 < 0.5
  501. assert ppos.y1 < 0.5
  502. assert ppos.x0 > 0.2
  503. assert ppos.y0 > 0.2
  504. def test_compressed1():
  505. fig, axs = plt.subplots(3, 2, layout='compressed',
  506. sharex=True, sharey=True)
  507. for ax in axs.flat:
  508. pc = ax.imshow(np.random.randn(20, 20))
  509. fig.colorbar(pc, ax=axs)
  510. fig.draw_without_rendering()
  511. pos = axs[0, 0].get_position()
  512. np.testing.assert_allclose(pos.x0, 0.2344, atol=1e-3)
  513. pos = axs[0, 1].get_position()
  514. np.testing.assert_allclose(pos.x1, 0.7024, atol=1e-3)
  515. # wider than tall
  516. fig, axs = plt.subplots(2, 3, layout='compressed',
  517. sharex=True, sharey=True, figsize=(5, 4))
  518. for ax in axs.flat:
  519. pc = ax.imshow(np.random.randn(20, 20))
  520. fig.colorbar(pc, ax=axs)
  521. fig.draw_without_rendering()
  522. pos = axs[0, 0].get_position()
  523. np.testing.assert_allclose(pos.x0, 0.06195, atol=1e-3)
  524. np.testing.assert_allclose(pos.y1, 0.8537, atol=1e-3)
  525. pos = axs[1, 2].get_position()
  526. np.testing.assert_allclose(pos.x1, 0.8618, atol=1e-3)
  527. np.testing.assert_allclose(pos.y0, 0.1934, atol=1e-3)
  528. @pytest.mark.parametrize('arg, state', [
  529. (True, True),
  530. (False, False),
  531. ({}, True),
  532. ({'rect': None}, True)
  533. ])
  534. def test_set_constrained_layout(arg, state):
  535. fig, ax = plt.subplots(constrained_layout=arg)
  536. assert fig.get_constrained_layout() is state
  537. def test_constrained_toggle():
  538. fig, ax = plt.subplots()
  539. with pytest.warns(PendingDeprecationWarning):
  540. fig.set_constrained_layout(True)
  541. assert fig.get_constrained_layout()
  542. fig.set_constrained_layout(False)
  543. assert not fig.get_constrained_layout()
  544. fig.set_constrained_layout(True)
  545. assert fig.get_constrained_layout()
  546. def test_layout_leak():
  547. # Make sure there aren't any cyclic references when using LayoutGrid
  548. # GH #25853
  549. fig = plt.figure(constrained_layout=True, figsize=(10, 10))
  550. fig.add_subplot()
  551. fig.draw_without_rendering()
  552. plt.close("all")
  553. del fig
  554. gc.collect()
  555. assert not any(isinstance(obj, mpl._layoutgrid.LayoutGrid)
  556. for obj in gc.get_objects())