Skip to content

Commit b0b9792

Browse files
committed
ENH: reuse gridspec if possible
1 parent 969513e commit b0b9792

File tree

3 files changed

+55
-39
lines changed

3 files changed

+55
-39
lines changed

lib/matplotlib/gridspec.py

+33-3
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,31 @@ def get_grid_positions(self, fig, raw=False):
202202
fig_lefts, fig_rights = (left + cell_ws).reshape((-1, 2)).T
203203
return fig_bottoms, fig_tops, fig_lefts, fig_rights
204204

205+
@staticmethod
206+
def _check_gridspec_exists(figure, nrows, ncols):
207+
"""
208+
Check if the figure already has a gridspec with these dimensions,
209+
or create a new one
210+
"""
211+
gs = None
212+
for ax in figure.get_axes():
213+
if hasattr(ax, 'get_subplotspec'):
214+
ggs = ax.get_subplotspec().get_gridspec()
215+
if hasattr(ggs, 'get_topmost_subplotspec'):
216+
# This is needed for colorbar gridspec layouts.
217+
# This is probably OK becase this whole logic tree
218+
# is for when the user is doing simple things with the
219+
# add_subplot command. Complicated stuff, the proper
220+
# gridspec is passed in...
221+
ggs = ggs.get_topmost_subplotspec().get_gridspec()
222+
(nrow, ncol) = ggs.get_geometry()
223+
if nrow == nrows and ncol == ncols:
224+
gs = ggs
225+
if gs is None:
226+
gs = GridSpec(nrows, ncols, figure=figure)
227+
return gs
228+
229+
205230
def __getitem__(self, key):
206231
"""Create and return a `.SubplotSpec` instance."""
207232
nrows, ncols = self.get_geometry()
@@ -665,7 +690,7 @@ def _from_subplot_args(figure, args):
665690
f"Single argument to subplot must be a three-digit "
666691
f"integer, not {arg}") from None
667692
# num - 1 for converting from MATLAB to python indexing
668-
return GridSpec(rows, cols, figure=figure)[num - 1]
693+
i = j = num
669694
elif len(args) == 3:
670695
rows, cols, num = args
671696
if not (isinstance(rows, Integral) and isinstance(cols, Integral)):
@@ -678,19 +703,24 @@ def _from_subplot_args(figure, args):
678703
i, j = map(int, num)
679704
else:
680705
i, j = num
681-
return gs[i-1:j]
682706
else:
683707
if not isinstance(num, Integral):
684708
cbook.warn_deprecated("3.3", message=message)
685709
num = int(num)
686710
if num < 1 or num > rows*cols:
687711
raise ValueError(
688712
f"num must be 1 <= num <= {rows*cols}, not {num}")
689-
return gs[num - 1] # -1 due to MATLAB indexing.
713+
else:
714+
i = j = num
690715
else:
691716
raise TypeError(f"subplot() takes 1 or 3 positional arguments but "
692717
f"{len(args)} were given")
693718

719+
gs = GridSpec._check_gridspec_exists(figure, rows, cols)
720+
if gs is None:
721+
gs = GridSpec(rows, cols, figure=figure)
722+
return gs[i-1:j]
723+
694724
# num2 is a property only to handle the case where it is None and someone
695725
# mutates num1.
696726

lib/matplotlib/pyplot.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -1311,10 +1311,10 @@ def subplot2grid(shape, loc, rowspan=1, colspan=1, fig=None, **kwargs):
13111311
if fig is None:
13121312
fig = gcf()
13131313

1314-
s1, s2 = shape
1315-
subplotspec = GridSpec(s1, s2).new_subplotspec(loc,
1316-
rowspan=rowspan,
1317-
colspan=colspan)
1314+
rows, cols = shape
1315+
gs = GridSpec._check_gridspec_exists(fig, rows, cols)
1316+
1317+
subplotspec = gs.new_subplotspec(loc, rowspan=rowspan, colspan=colspan)
13181318
ax = fig.add_subplot(subplotspec, **kwargs)
13191319
bbox = ax.bbox
13201320
axes_to_delete = []

tutorials/intermediate/constrainedlayout_guide.py

+18-32
Original file line numberDiff line numberDiff line change
@@ -492,40 +492,42 @@ def docomplicated(suptitle=None):
492492
# Incompatible functions
493493
# ----------------------
494494
#
495-
# ``constrained_layout`` will not work on subplots created via
496-
# `.pyplot.subplot`. The reason is that each call to `.pyplot.subplot` creates
497-
# a separate `.GridSpec` instance and ``constrained_layout`` uses (nested)
498-
# gridspecs to carry out the layout. So the following fails to yield a nice
499-
# layout:
495+
# ``constrained_layout`` will work with `.pyplot.subplot`, but only if the
496+
# number of rows and columns is the same for each call.
497+
# The reason is that each call to `.pyplot.subplot` will create a new
498+
# `.GridSpec` instance if the geometry is not the same, and
499+
# ``constrained_layout``. So the following works fine:
500+
500501

501502
fig = plt.figure()
502503

503-
ax1 = plt.subplot(221)
504-
ax2 = plt.subplot(223)
505-
ax3 = plt.subplot(122)
504+
ax1 = plt.subplot(2, 2, 1)
505+
ax2 = plt.subplot(2, 2, 3)
506+
ax3 = plt.subplot(2, 2, (2, 4))
506507

507508
example_plot(ax1)
508509
example_plot(ax2)
509510
example_plot(ax3)
511+
plt.suptitle('Homogenous nrows, ncols')
510512

511513
###############################################################################
512-
# Of course that layout is possible using a gridspec:
514+
# but the following leads to a poor layout:
513515

514516
fig = plt.figure()
515-
gs = fig.add_gridspec(2, 2)
516517

517-
ax1 = fig.add_subplot(gs[0, 0])
518-
ax2 = fig.add_subplot(gs[1, 0])
519-
ax3 = fig.add_subplot(gs[:, 1])
518+
ax1 = plt.subplot(2, 2, 1)
519+
ax2 = plt.subplot(2, 2, 3)
520+
ax3 = plt.subplot(1, 2, 2)
520521

521522
example_plot(ax1)
522523
example_plot(ax2)
523524
example_plot(ax3)
525+
plt.suptitle('Mixed nrows, ncols')
524526

525527
###############################################################################
526528
# Similarly,
527-
# :func:`~matplotlib.pyplot.subplot2grid` doesn't work for the same reason:
528-
# each call creates a different parent gridspec.
529+
# :func:`~matplotlib.pyplot.subplot2grid` works with the same limitation
530+
# that nrows and ncols cannot change for the layout to look good.
529531

530532
fig = plt.figure()
531533

@@ -538,23 +540,7 @@ def docomplicated(suptitle=None):
538540
example_plot(ax2)
539541
example_plot(ax3)
540542
example_plot(ax4)
541-
542-
###############################################################################
543-
# The way to make this plot compatible with ``constrained_layout`` is again
544-
# to use ``gridspec`` directly
545-
546-
fig = plt.figure()
547-
gs = fig.add_gridspec(3, 3)
548-
549-
ax1 = fig.add_subplot(gs[0, 0])
550-
ax2 = fig.add_subplot(gs[0, 1:])
551-
ax3 = fig.add_subplot(gs[1:, 0:2])
552-
ax4 = fig.add_subplot(gs[1:, -1])
553-
554-
example_plot(ax1)
555-
example_plot(ax2)
556-
example_plot(ax3)
557-
example_plot(ax4)
543+
fig.suptitle('subplot2grid')
558544

559545
###############################################################################
560546
# Other Caveats

0 commit comments

Comments
 (0)