Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 6a675d0

Browse filesBrowse files
committed
Deprecate SubplotSpec.get_rows_columns.
and simplify constrained_layout by using rowspan/colspan instead.
1 parent 70d59ae commit 6a675d0
Copy full SHA for 6a675d0

File tree

Expand file treeCollapse file tree

4 files changed

+144
-205
lines changed
Filter options
Expand file treeCollapse file tree

4 files changed

+144
-205
lines changed

‎doc/api/next_api_changes/deprecations.rst

Copy file name to clipboardExpand all lines: doc/api/next_api_changes/deprecations.rst
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,3 +236,8 @@ The following related APIs are also deprecated:
236236
``matplotlib.test(recursionlimit=...)``
237237
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
238238
The *recursionlimit* parameter of ``matplotlib.test`` is deprecated.
239+
240+
``SubplotSpec.get_rows_columns``
241+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
242+
This method is deprecated. Use the ``GridSpec.nrows``, ``GridSpec.ncols``,
243+
``SubplotSpec.rowspan``, and ``SubplotSpec.colspan`` properties instead.

‎lib/matplotlib/_constrained_layout.py

Copy file name to clipboardExpand all lines: lib/matplotlib/_constrained_layout.py
+116-167Lines changed: 116 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,8 @@
5555
_log = logging.getLogger(__name__)
5656

5757

58-
def _in_same_column(colnum0min, colnum0max, colnumCmin, colnumCmax):
59-
return (colnumCmin <= colnum0min <= colnumCmax
60-
or colnumCmin <= colnum0max <= colnumCmax)
61-
62-
63-
def _in_same_row(rownum0min, rownum0max, rownumCmin, rownumCmax):
64-
return (rownumCmin <= rownum0min <= rownumCmax
65-
or rownumCmin <= rownum0max <= rownumCmax)
58+
def _spans_overlap(span0, span1):
59+
return span0.start in span1 or span1.start in span0
6660

6761

6862
def _axes_all_finite_sized(fig):
@@ -155,7 +149,7 @@ def do_constrained_layout(fig, renderer, h_pad, w_pad,
155149
# fill in any empty gridspec slots w/ ghost axes...
156150
_make_ghost_gridspec_slots(fig, gs)
157151

158-
for nnn in range(2):
152+
for _ in range(2):
159153
# do the algorithm twice. This has to be done because decorators
160154
# change size after the first re-position (i.e. x/yticklabels get
161155
# larger/smaller). This second reposition tends to be much milder,
@@ -329,131 +323,110 @@ def _align_spines(fig, gs):
329323
if (hasattr(ax, 'get_subplotspec')
330324
and ax._layoutbox is not None
331325
and ax.get_subplotspec().get_gridspec() == gs)]
332-
rownummin = np.zeros(len(axs), dtype=np.int8)
333-
rownummax = np.zeros(len(axs), dtype=np.int8)
334-
colnummin = np.zeros(len(axs), dtype=np.int8)
335-
colnummax = np.zeros(len(axs), dtype=np.int8)
336-
width = np.zeros(len(axs))
337-
height = np.zeros(len(axs))
326+
rowspans = []
327+
colspans = []
328+
heights = []
329+
widths = []
338330

339331
for n, ax in enumerate(axs):
340332
ss0 = ax.get_subplotspec()
341-
rownummin[n], colnummin[n] = divmod(ss0.num1, ncols)
342-
rownummax[n], colnummax[n] = divmod(ss0.num2, ncols)
343-
width[n] = np.sum(
344-
width_ratios[colnummin[n]:(colnummax[n] + 1)])
345-
height[n] = np.sum(
346-
height_ratios[rownummin[n]:(rownummax[n] + 1)])
347-
348-
for nn, ax in enumerate(axs[:-1]):
349-
# now compare ax to all the axs:
350-
#
351-
# If the subplotspecs have the same colnumXmax, then line
352-
# up their right sides. If they have the same min, then
353-
# line up their left sides (and vertical equivalents).
354-
rownum0min, colnum0min = rownummin[nn], colnummin[nn]
355-
rownum0max, colnum0max = rownummax[nn], colnummax[nn]
356-
width0, height0 = width[nn], height[nn]
333+
rowspan = ss0.rowspan
334+
colspan = ss0.colspan
335+
rowspans.append(rowspan)
336+
colspans.append(colspan)
337+
heights.append(sum(height_ratios[rowspan.start:rowspan.stop]))
338+
widths.append(sum(width_ratios[colspan.start:colspan.stop]))
339+
340+
for idx0, ax0 in enumerate(axs):
341+
# Compare ax to all other axs: If the subplotspecs start (/stop) at
342+
# the same column, then line up their left (/right) sides; likewise
343+
# for rows/top/bottom.
344+
rowspan0 = rowspans[idx0]
345+
colspan0 = colspans[idx0]
346+
height0 = heights[idx0]
347+
width0 = widths[idx0]
357348
alignleft = False
358349
alignright = False
359350
alignbot = False
360351
aligntop = False
361352
alignheight = False
362353
alignwidth = False
363-
for mm in range(nn+1, len(axs)):
364-
axc = axs[mm]
365-
rownumCmin, colnumCmin = rownummin[mm], colnummin[mm]
366-
rownumCmax, colnumCmax = rownummax[mm], colnummax[mm]
367-
widthC, heightC = width[mm], height[mm]
368-
# Horizontally align axes spines if they have the
369-
# same min or max:
370-
if not alignleft and colnum0min == colnumCmin:
371-
# we want the _poslayoutboxes to line up on left
372-
# side of the axes spines...
373-
layoutbox.align([ax._poslayoutbox, axc._poslayoutbox],
354+
for idx1 in range(idx0 + 1, len(axs)):
355+
ax1 = axs[idx1]
356+
rowspan1 = rowspans[idx1]
357+
colspan1 = colspans[idx1]
358+
width1 = widths[idx1]
359+
height1 = heights[idx1]
360+
# Horizontally align axes spines if they have the same min or max:
361+
if not alignleft and colspan0.start == colspan1.start:
362+
_log.debug('same start columns; line up layoutbox lefts')
363+
layoutbox.align([ax0._poslayoutbox, ax1._poslayoutbox],
374364
'left')
375365
alignleft = True
376-
if not alignright and colnum0max == colnumCmax:
377-
# line up right sides of _poslayoutbox
378-
layoutbox.align([ax._poslayoutbox, axc._poslayoutbox],
366+
if not alignright and colspan0.stop == colspan1.stop:
367+
_log.debug('same stop columns; line up layoutbox rights')
368+
layoutbox.align([ax0._poslayoutbox, ax1._poslayoutbox],
379369
'right')
380370
alignright = True
381-
# Vertically align axes spines if they have the
382-
# same min or max:
383-
if not aligntop and rownum0min == rownumCmin:
384-
# line up top of _poslayoutbox
385-
_log.debug('rownum0min == rownumCmin')
386-
layoutbox.align([ax._poslayoutbox, axc._poslayoutbox],
371+
# Vertically align axes spines if they have the same min or max:
372+
if not aligntop and rowspan0.start == rowspan1.start:
373+
_log.debug('same start rows; line up layoutbox tops')
374+
layoutbox.align([ax0._poslayoutbox, ax1._poslayoutbox],
387375
'top')
388376
aligntop = True
389-
if not alignbot and rownum0max == rownumCmax:
390-
# line up bottom of _poslayoutbox
391-
_log.debug('rownum0max == rownumCmax')
392-
layoutbox.align([ax._poslayoutbox, axc._poslayoutbox],
377+
if not alignbot and rowspan0.stop == rowspan1.stop:
378+
_log.debug('same stop rows; line up layoutbox bottoms')
379+
layoutbox.align([ax0._poslayoutbox, ax1._poslayoutbox],
393380
'bottom')
394381
alignbot = True
395-
###########
382+
396383
# Now we make the widths and heights of position boxes
397384
# similar. (i.e the spine locations)
398-
# This allows vertically stacked subplots to have
399-
# different sizes if they occupy different amounts
400-
# of the gridspec: i.e.
385+
# This allows vertically stacked subplots to have different sizes
386+
# if they occupy different amounts of the gridspec: i.e.
401387
# gs = gridspec.GridSpec(3, 1)
402388
# ax1 = gs[0, :]
403389
# ax2 = gs[1:, :]
404390
# then drows0 = 1, and drowsC = 2, and ax2
405391
# should be at least twice as large as ax1.
406392
# But it can be more than twice as large because
407393
# it needs less room for the labeling.
408-
#
409-
# For height, this only needs to be done if the
410-
# subplots share a column. For width if they
411-
# share a row.
412-
413-
drowsC = (rownumCmax - rownumCmin + 1)
414-
drows0 = (rownum0max - rownum0min + 1)
415-
dcolsC = (colnumCmax - colnumCmin + 1)
416-
dcols0 = (colnum0max - colnum0min + 1)
417-
418-
if not alignheight and drows0 == drowsC:
419-
ax._poslayoutbox.constrain_height(
420-
axc._poslayoutbox.height * height0 / heightC)
394+
395+
# For height, this only needs to be done if the subplots share a
396+
# column.
397+
if not alignheight and len(rowspan0) == len(rowspan1):
398+
ax0._poslayoutbox.constrain_height(
399+
ax1._poslayoutbox.height * height0 / height1)
421400
alignheight = True
422-
elif _in_same_column(colnum0min, colnum0max,
423-
colnumCmin, colnumCmax):
424-
if height0 > heightC:
425-
ax._poslayoutbox.constrain_height_min(
426-
axc._poslayoutbox.height * height0 / heightC)
401+
elif _spans_overlap(colspan0, colspan1):
402+
if height0 > height1:
403+
ax0._poslayoutbox.constrain_height_min(
404+
ax1._poslayoutbox.height * height0 / height1)
427405
# these constraints stop the smaller axes from
428406
# being allowed to go to zero height...
429-
axc._poslayoutbox.constrain_height_min(
430-
ax._poslayoutbox.height * heightC /
431-
(height0*1.8))
432-
elif height0 < heightC:
433-
axc._poslayoutbox.constrain_height_min(
434-
ax._poslayoutbox.height * heightC / height0)
435-
ax._poslayoutbox.constrain_height_min(
436-
ax._poslayoutbox.height * height0 /
437-
(heightC*1.8))
438-
# widths...
439-
if not alignwidth and dcols0 == dcolsC:
440-
ax._poslayoutbox.constrain_width(
441-
axc._poslayoutbox.width * width0 / widthC)
407+
ax1._poslayoutbox.constrain_height_min(
408+
ax0._poslayoutbox.height * height1 / (height0*1.8))
409+
elif height0 < height1:
410+
ax1._poslayoutbox.constrain_height_min(
411+
ax0._poslayoutbox.height * height1 / height0)
412+
ax0._poslayoutbox.constrain_height_min(
413+
ax0._poslayoutbox.height * height0 / (height1*1.8))
414+
# For width, if they share a row.
415+
if not alignwidth and len(colspan0) == len(colspan1):
416+
ax0._poslayoutbox.constrain_width(
417+
ax1._poslayoutbox.width * width0 / width1)
442418
alignwidth = True
443-
elif _in_same_row(rownum0min, rownum0max,
444-
rownumCmin, rownumCmax):
445-
if width0 > widthC:
446-
ax._poslayoutbox.constrain_width_min(
447-
axc._poslayoutbox.width * width0 / widthC)
448-
axc._poslayoutbox.constrain_width_min(
449-
ax._poslayoutbox.width * widthC /
450-
(width0*1.8))
451-
elif width0 < widthC:
452-
axc._poslayoutbox.constrain_width_min(
453-
ax._poslayoutbox.width * widthC / width0)
454-
ax._poslayoutbox.constrain_width_min(
455-
axc._poslayoutbox.width * width0 /
456-
(widthC*1.8))
419+
elif _spans_overlap(rowspan0, rowspan1):
420+
if width0 > width1:
421+
ax0._poslayoutbox.constrain_width_min(
422+
ax1._poslayoutbox.width * width0 / width1)
423+
ax1._poslayoutbox.constrain_width_min(
424+
ax0._poslayoutbox.width * width1 / (width0*1.8))
425+
elif width0 < width1:
426+
ax1._poslayoutbox.constrain_width_min(
427+
ax0._poslayoutbox.width * width1 / width0)
428+
ax0._poslayoutbox.constrain_width_min(
429+
ax1._poslayoutbox.width * width0 / (width1*1.8))
457430

458431

459432
def _arrange_subplotspecs(gs, hspace=0, wspace=0):
@@ -470,34 +443,25 @@ def _arrange_subplotspecs(gs, hspace=0, wspace=0):
470443
for child0 in sschildren:
471444
ss0 = child0.artist
472445
nrows, ncols = ss0.get_gridspec().get_geometry()
473-
rowNum0min, colNum0min = divmod(ss0.num1, ncols)
474-
rowNum0max, colNum0max = divmod(ss0.num2, ncols)
446+
rowspan0 = ss0.rowspan
447+
colspan0 = ss0.colspan
475448
sschildren = sschildren[1:]
476-
for childc in sschildren:
477-
ssc = childc.artist
478-
rowNumCmin, colNumCmin = divmod(ssc.num1, ncols)
479-
rowNumCmax, colNumCmax = divmod(ssc.num2, ncols)
480-
# OK, this tells us the relative layout of ax
481-
# with axc
482-
thepad = wspace / ncols
483-
if colNum0max < colNumCmin:
484-
layoutbox.hstack([ss0._layoutbox, ssc._layoutbox],
485-
padding=thepad)
486-
if colNumCmax < colNum0min:
487-
layoutbox.hstack([ssc._layoutbox, ss0._layoutbox],
488-
padding=thepad)
489-
490-
####
449+
for child1 in sschildren:
450+
ss1 = child1.artist
451+
rowspan1 = ss1.rowspan
452+
colspan1 = ss1.colspan
453+
# OK, this tells us the relative layout of ax with ax1
454+
pad = wspace / ncols
455+
if colspan0.stop <= colspan1.start:
456+
layoutbox.hstack([ss0._layoutbox, ss1._layoutbox], padding=pad)
457+
if colspan1.stop <= colspan0.start:
458+
layoutbox.hstack([ss1._layoutbox, ss0._layoutbox], padding=pad)
491459
# vertical alignment
492-
thepad = hspace / nrows
493-
if rowNum0max < rowNumCmin:
494-
layoutbox.vstack([ss0._layoutbox,
495-
ssc._layoutbox],
496-
padding=thepad)
497-
if rowNumCmax < rowNum0min:
498-
layoutbox.vstack([ssc._layoutbox,
499-
ss0._layoutbox],
500-
padding=thepad)
460+
pad = hspace / nrows
461+
if rowspan0.stop <= rowspan1.start:
462+
layoutbox.vstack([ss0._layoutbox, ss1._layoutbox], padding=pad)
463+
if rowspan1.stop <= rowspan0.start:
464+
layoutbox.vstack([ss1._layoutbox, ss0._layoutbox], padding=pad)
501465

502466

503467
def layoutcolorbarsingle(ax, cax, shrink, aspect, location, pad=0.05):
@@ -561,32 +525,21 @@ def layoutcolorbarsingle(ax, cax, shrink, aspect, location, pad=0.05):
561525

562526
def _getmaxminrowcolumn(axs):
563527
# helper to get the min/max rows and columns of a list of axes.
564-
maxrow = -100000
565-
minrow = 1000000
566-
maxax = None
567-
minax = None
568-
maxcol = -100000
569-
mincol = 1000000
570-
maxax_col = None
571-
minax_col = None
572528

573-
for ax in axs:
574-
subspec = ax.get_subplotspec()
575-
nrows, ncols, row_start, row_stop, col_start, col_stop = \
576-
subspec.get_rows_columns()
577-
if row_stop > maxrow:
578-
maxrow = row_stop
579-
maxax = ax
580-
if row_start < minrow:
581-
minrow = row_start
582-
minax = ax
583-
if col_stop > maxcol:
584-
maxcol = col_stop
585-
maxax_col = ax
586-
if col_start < mincol:
587-
mincol = col_start
588-
minax_col = ax
589-
return (minrow, maxrow, minax, maxax, mincol, maxcol, minax_col, maxax_col)
529+
def key(pos_ax):
530+
pos, ax = pos_ax
531+
return pos
532+
533+
minrow, minrow_ax = min(
534+
((ax.get_subplotspec().rowspan.start, ax) for ax in axs), key=key)
535+
maxrow, maxrow_ax = max(
536+
((ax.get_subplotspec().rowspan.stop - 1, ax) for ax in axs), key=key)
537+
mincol, mincol_ax = min(
538+
((ax.get_subplotspec().colspan.start, ax) for ax in axs), key=key)
539+
maxcol, maxcol_ax = max(
540+
((ax.get_subplotspec().colspan.stop - 1, ax) for ax in axs), key=key)
541+
return (minrow, maxrow, minrow_ax, maxrow_ax,
542+
mincol, maxcol, mincol_ax, maxcol_ax)
590543

591544

592545
def layoutcolorbargridspec(parents, cax, shrink, aspect, location, pad=0.05):
@@ -630,18 +583,16 @@ def layoutcolorbargridspec(parents, cax, shrink, aspect, location, pad=0.05):
630583
# Horizontal Layout: need to check all the axes in this gridspec
631584
for ch in gslb.children:
632585
subspec = ch.artist
633-
nrows, ncols, row_start, row_stop, col_start, col_stop = \
634-
subspec.get_rows_columns()
635586
if location == 'right':
636-
if col_stop <= maxcol:
587+
if subspec.colspan.stop - 1 <= maxcol:
637588
order = [subspec._layoutbox, lb]
638589
# arrange to right of the parents
639-
if col_start > maxcol:
590+
elif subspec.colspan.start > maxcol:
640591
order = [lb, subspec._layoutbox]
641592
elif location == 'left':
642-
if col_start >= mincol:
593+
if subspec.colspan.start >= mincol:
643594
order = [lb, subspec._layoutbox]
644-
if col_stop < mincol:
595+
elif subspec.colspan.stop - 1 < mincol:
645596
order = [subspec._layoutbox, lb]
646597
layoutbox.hstack(order, padding=pad * gslb.width,
647598
strength='strong')
@@ -686,17 +637,15 @@ def layoutcolorbargridspec(parents, cax, shrink, aspect, location, pad=0.05):
686637
# Vertical Layout: need to check all the axes in this gridspec
687638
for ch in gslb.children:
688639
subspec = ch.artist
689-
nrows, ncols, row_start, row_stop, col_start, col_stop = \
690-
subspec.get_rows_columns()
691640
if location == 'bottom':
692-
if row_stop <= minrow:
641+
if subspec.rowspan.stop - 1 <= minrow:
693642
order = [subspec._layoutbox, lb]
694-
if row_start > maxrow:
643+
elif subspec.rowspan.start > maxrow:
695644
order = [lb, subspec._layoutbox]
696645
elif location == 'top':
697-
if row_stop < minrow:
646+
if subspec.rowspan.stop - 1 < minrow:
698647
order = [subspec._layoutbox, lb]
699-
if row_start >= maxrow:
648+
elif subspec.rowspan.start >= maxrow:
700649
order = [lb, subspec._layoutbox]
701650
layoutbox.vstack(order, padding=pad * gslb.width,
702651
strength='strong')

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.