test_tightlayout.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. import warnings
  2. import numpy as np
  3. from numpy.testing import assert_array_equal
  4. import pytest
  5. from matplotlib.testing.decorators import image_comparison
  6. import matplotlib.pyplot as plt
  7. from matplotlib.offsetbox import AnchoredOffsetbox, DrawingArea
  8. from matplotlib.patches import Rectangle
  9. def example_plot(ax, fontsize=12):
  10. ax.plot([1, 2])
  11. ax.locator_params(nbins=3)
  12. ax.set_xlabel('x-label', fontsize=fontsize)
  13. ax.set_ylabel('y-label', fontsize=fontsize)
  14. ax.set_title('Title', fontsize=fontsize)
  15. @image_comparison(['tight_layout1'])
  16. def test_tight_layout1():
  17. 'Test tight_layout for a single subplot'
  18. fig, ax = plt.subplots()
  19. example_plot(ax, fontsize=24)
  20. plt.tight_layout()
  21. @image_comparison(['tight_layout2'])
  22. def test_tight_layout2():
  23. 'Test tight_layout for multiple subplots'
  24. fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2)
  25. example_plot(ax1)
  26. example_plot(ax2)
  27. example_plot(ax3)
  28. example_plot(ax4)
  29. plt.tight_layout()
  30. @image_comparison(['tight_layout3'])
  31. def test_tight_layout3():
  32. 'Test tight_layout for multiple subplots'
  33. ax1 = plt.subplot(221)
  34. ax2 = plt.subplot(223)
  35. ax3 = plt.subplot(122)
  36. example_plot(ax1)
  37. example_plot(ax2)
  38. example_plot(ax3)
  39. plt.tight_layout()
  40. @image_comparison(['tight_layout4'], freetype_version=('2.5.5', '2.6.1'))
  41. def test_tight_layout4():
  42. 'Test tight_layout for subplot2grid'
  43. ax1 = plt.subplot2grid((3, 3), (0, 0))
  44. ax2 = plt.subplot2grid((3, 3), (0, 1), colspan=2)
  45. ax3 = plt.subplot2grid((3, 3), (1, 0), colspan=2, rowspan=2)
  46. ax4 = plt.subplot2grid((3, 3), (1, 2), rowspan=2)
  47. example_plot(ax1)
  48. example_plot(ax2)
  49. example_plot(ax3)
  50. example_plot(ax4)
  51. plt.tight_layout()
  52. @image_comparison(['tight_layout5'])
  53. def test_tight_layout5():
  54. 'Test tight_layout for image'
  55. ax = plt.subplot(111)
  56. arr = np.arange(100).reshape((10, 10))
  57. ax.imshow(arr, interpolation="none")
  58. plt.tight_layout()
  59. @image_comparison(['tight_layout6'])
  60. def test_tight_layout6():
  61. 'Test tight_layout for gridspec'
  62. # This raises warnings since tight layout cannot
  63. # do this fully automatically. But the test is
  64. # correct since the layout is manually edited
  65. with warnings.catch_warnings():
  66. warnings.simplefilter("ignore", UserWarning)
  67. fig = plt.figure()
  68. import matplotlib.gridspec as gridspec
  69. gs1 = gridspec.GridSpec(2, 1)
  70. ax1 = fig.add_subplot(gs1[0])
  71. ax2 = fig.add_subplot(gs1[1])
  72. example_plot(ax1)
  73. example_plot(ax2)
  74. gs1.tight_layout(fig, rect=[0, 0, 0.5, 1])
  75. gs2 = gridspec.GridSpec(3, 1)
  76. for ss in gs2:
  77. ax = fig.add_subplot(ss)
  78. example_plot(ax)
  79. ax.set_title("")
  80. ax.set_xlabel("")
  81. ax.set_xlabel("x-label", fontsize=12)
  82. gs2.tight_layout(fig, rect=[0.5, 0, 1, 1], h_pad=0.45)
  83. top = min(gs1.top, gs2.top)
  84. bottom = max(gs1.bottom, gs2.bottom)
  85. gs1.tight_layout(fig, rect=[None, 0 + (bottom-gs1.bottom),
  86. 0.5, 1 - (gs1.top-top)])
  87. gs2.tight_layout(fig, rect=[0.5, 0 + (bottom-gs2.bottom),
  88. None, 1 - (gs2.top-top)],
  89. h_pad=0.45)
  90. @image_comparison(['tight_layout7'])
  91. def test_tight_layout7():
  92. # tight layout with left and right titles
  93. fontsize = 24
  94. fig, ax = plt.subplots()
  95. ax.plot([1, 2])
  96. ax.locator_params(nbins=3)
  97. ax.set_xlabel('x-label', fontsize=fontsize)
  98. ax.set_ylabel('y-label', fontsize=fontsize)
  99. ax.set_title('Left Title', loc='left', fontsize=fontsize)
  100. ax.set_title('Right Title', loc='right', fontsize=fontsize)
  101. plt.tight_layout()
  102. @image_comparison(['tight_layout8'])
  103. def test_tight_layout8():
  104. 'Test automatic use of tight_layout'
  105. fig = plt.figure()
  106. fig.set_tight_layout({'pad': .1})
  107. ax = fig.add_subplot(111)
  108. example_plot(ax, fontsize=24)
  109. @image_comparison(['tight_layout9'])
  110. def test_tight_layout9():
  111. # Test tight_layout for non-visible subplots
  112. # GH 8244
  113. f, axarr = plt.subplots(2, 2)
  114. axarr[1][1].set_visible(False)
  115. plt.tight_layout()
  116. def test_outward_ticks():
  117. 'Test automatic use of tight_layout'
  118. fig = plt.figure()
  119. ax = fig.add_subplot(221)
  120. ax.xaxis.set_tick_params(tickdir='out', length=16, width=3)
  121. ax.yaxis.set_tick_params(tickdir='out', length=16, width=3)
  122. ax.xaxis.set_tick_params(
  123. tickdir='out', length=32, width=3, tick1On=True, which='minor')
  124. ax.yaxis.set_tick_params(
  125. tickdir='out', length=32, width=3, tick1On=True, which='minor')
  126. ax.xaxis.set_ticks([0], minor=True)
  127. ax.yaxis.set_ticks([0], minor=True)
  128. ax = fig.add_subplot(222)
  129. ax.xaxis.set_tick_params(tickdir='in', length=32, width=3)
  130. ax.yaxis.set_tick_params(tickdir='in', length=32, width=3)
  131. ax = fig.add_subplot(223)
  132. ax.xaxis.set_tick_params(tickdir='inout', length=32, width=3)
  133. ax.yaxis.set_tick_params(tickdir='inout', length=32, width=3)
  134. ax = fig.add_subplot(224)
  135. ax.xaxis.set_tick_params(tickdir='out', length=32, width=3)
  136. ax.yaxis.set_tick_params(tickdir='out', length=32, width=3)
  137. plt.tight_layout()
  138. # These values were obtained after visual checking that they correspond
  139. # to a tight layouting that did take the ticks into account.
  140. ans = [[[0.091, 0.607], [0.433, 0.933]],
  141. [[0.579, 0.607], [0.922, 0.933]],
  142. [[0.091, 0.140], [0.433, 0.466]],
  143. [[0.579, 0.140], [0.922, 0.466]]]
  144. for nn, ax in enumerate(fig.axes):
  145. assert_array_equal(np.round(ax.get_position().get_points(), 3),
  146. ans[nn])
  147. def add_offsetboxes(ax, size=10, margin=.1, color='black'):
  148. """
  149. Surround ax with OffsetBoxes
  150. """
  151. m, mp = margin, 1+margin
  152. anchor_points = [(-m, -m), (-m, .5), (-m, mp),
  153. (mp, .5), (.5, mp), (mp, mp),
  154. (.5, -m), (mp, -m), (.5, -m)]
  155. for point in anchor_points:
  156. da = DrawingArea(size, size)
  157. background = Rectangle((0, 0), width=size,
  158. height=size,
  159. facecolor=color,
  160. edgecolor='None',
  161. linewidth=0,
  162. antialiased=False)
  163. da.add_artist(background)
  164. anchored_box = AnchoredOffsetbox(
  165. loc='center',
  166. child=da,
  167. pad=0.,
  168. frameon=False,
  169. bbox_to_anchor=point,
  170. bbox_transform=ax.transAxes,
  171. borderpad=0.)
  172. ax.add_artist(anchored_box)
  173. return anchored_box
  174. @image_comparison(['tight_layout_offsetboxes1', 'tight_layout_offsetboxes2'])
  175. def test_tight_layout_offsetboxes():
  176. # 1.
  177. # - Create 4 subplots
  178. # - Plot a diagonal line on them
  179. # - Surround each plot with 7 boxes
  180. # - Use tight_layout
  181. # - See that the squares are included in the tight_layout
  182. # and that the squares in the middle do not overlap
  183. #
  184. # 2.
  185. # - Make the squares around the right side axes invisible
  186. # - See that the invisible squares do not affect the
  187. # tight_layout
  188. rows = cols = 2
  189. colors = ['red', 'blue', 'green', 'yellow']
  190. x = y = [0, 1]
  191. def _subplots():
  192. _, axs = plt.subplots(rows, cols)
  193. axs = axs.flat
  194. for ax, color in zip(axs, colors):
  195. ax.plot(x, y, color=color)
  196. add_offsetboxes(ax, 20, color=color)
  197. return axs
  198. # 1.
  199. axs = _subplots()
  200. plt.tight_layout()
  201. # 2.
  202. axs = _subplots()
  203. for ax in (axs[cols-1::rows]):
  204. for child in ax.get_children():
  205. if isinstance(child, AnchoredOffsetbox):
  206. child.set_visible(False)
  207. plt.tight_layout()
  208. def test_empty_layout():
  209. """Test that tight layout doesn't cause an error when there are no axes."""
  210. fig = plt.gcf()
  211. fig.tight_layout()
  212. @pytest.mark.parametrize("label", ["xlabel", "ylabel"])
  213. def test_verybig_decorators(label):
  214. """Test that warning emitted when xlabel/ylabel too big."""
  215. fig, ax = plt.subplots(figsize=(3, 2))
  216. ax.set(**{label: 'a' * 100})
  217. with pytest.warns(UserWarning):
  218. fig.tight_layout()
  219. def test_big_decorators_horizontal():
  220. "Test that warning emitted when xlabel too big"
  221. fig, axs = plt.subplots(1, 2, figsize=(3, 2))
  222. axs[0].set_xlabel('a' * 30)
  223. axs[1].set_xlabel('b' * 30)
  224. with pytest.warns(UserWarning):
  225. fig.tight_layout()
  226. def test_big_decorators_vertical():
  227. "Test that warning emitted when xlabel too big"
  228. fig, axs = plt.subplots(2, 1, figsize=(3, 2))
  229. axs[0].set_ylabel('a' * 20)
  230. axs[1].set_ylabel('b' * 20)
  231. with pytest.warns(UserWarning):
  232. fig.tight_layout()
  233. def test_badsubplotgrid():
  234. # test that we get warning for mismatched subplot grids, not than an error
  235. plt.subplot2grid((4, 5), (0, 0))
  236. # this is the bad entry:
  237. plt.subplot2grid((5, 5), (0, 3), colspan=3, rowspan=5)
  238. with pytest.warns(UserWarning):
  239. plt.tight_layout()
  240. def test_collapsed():
  241. # test that if a call to tight_layout will collapses the axes that
  242. # it does not get applied:
  243. fig, ax = plt.subplots(tight_layout=True)
  244. ax.set_xlim([0, 1])
  245. ax.set_ylim([0, 1])
  246. ax.annotate('BIG LONG STRING', xy=(1.25, 2), xytext=(10.5, 1.75),)
  247. p1 = ax.get_position()
  248. with pytest.warns(UserWarning):
  249. plt.tight_layout()
  250. p2 = ax.get_position()
  251. assert p1.width == p2.width
  252. # test that passing a rect doesn't crash...
  253. with pytest.warns(UserWarning):
  254. plt.tight_layout(rect=[0, 0, 0.8, 0.8])