Skip to content

Commit

Permalink
MNT: revert _constrained_layout_changes
Browse files Browse the repository at this point in the history
  • Loading branch information
jklymak committed May 21, 2022
1 parent e420451 commit b352c5c
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 59 deletions.
3 changes: 1 addition & 2 deletions doc/api/next_api_changes/behavior/22745-JMK.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ No need to specify renderer for get_tightbbox and get_window_extent

The ``get_tightbbox`` and `~.Artist.get_window_extent` methods
no longer require the *renderer* kwarg, saving users from having to
querry it from ``fig.canvas.get_renderer``. Currently, all Artists are
aware of their parent figure (``artist.figure``), and if the *renderer*
querry it from ``fig.canvas.get_renderer``. If the *renderer*
kwarg is not supplied these methods first check if there is a cached renderer
from a previous draw and use that. If there is no cahched renderer, then
the methods will use ``fig.canvas.get_renderer()`` as a fallback.
112 changes: 58 additions & 54 deletions lib/matplotlib/_constrained_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,40 @@
layout. Axes manually placed via ``figure.add_axes()`` will not.
See Tutorial: :doc:`/tutorials/intermediate/constrainedlayout_guide`
General idea:
-------------
First, a figure has a gridspec that divides the figure into nrows and ncols,
with heights and widths set by ``height_ratios`` and ``width_ratios``,
often just set to 1 for an equal grid.
Subplotspecs that are derived from this gridspec can contain either a
``SubPanel``, a ``GridSpecFromSubplotSpec``, or an ``Axes``. The ``SubPanel``
and ``GridSpecFromSubplotSpec`` are dealt with recursively and each contain an
analogous layout.
Each ``GridSpec`` has a ``_layoutgrid`` attached to it. The ``_layoutgrid``
has the same logical layout as the ``GridSpec``. Each row of the grid spec
has a top and bottom "margin" and each column has a left and right "margin".
The "inner" height of each row is constrained to be the same (or as modified
by ``height_ratio``), and the "inner" width of each column is
constrained to be the same (as modified by ``width_ratio``), where "inner"
is the width or height of each column/row minus the size of the margins.
Then the size of the margins for each row and column are determined as the
max width of the decorators on each axes that has decorators in that margin.
For instance, a normal axes would have a left margin that includes the
left ticklabels, and the ylabel if it exists. The right margin may include a
colorbar, the bottom margin the xaxis decorations, and the top margin the
title.
With these constraints, the solver then finds appropriate bounds for the
columns and rows. It's possible that the margins take up the whole figure,
in which case the algorithm is not applied and a warning is raised.
See the tutorial doc:`/tutorials/intermediate/constrainedlayout_guide`
for more discussion of the algorithm with examples.
"""

import logging
Expand All @@ -22,40 +56,6 @@
import matplotlib._layoutgrid as mlayoutgrid


# General idea:
# -------------
#
# First, a figure has a gridspec that divides the figure into nrows and ncols,
# with heights and widths set by ``height_ratios`` and ``width_ratios``,
# often just set to 1 for an equal grid.
#
# Subplotspecs that are derived from this gridspec can contain either a
# ``SubPanel``, a ``GridSpecFromSubplotSpec``, or an ``Axes``. The
# ``SubPanel`` and ``GridSpecFromSubplotSpec`` are dealt with recursively and
# each contain an analogous layout.
#
# Each ``GridSpec`` has a ``_layoutgrid`` attached to it. The ``_layoutgrid``
# has the same logical layout as the ``GridSpec``. Each row of the grid spec
# has a top and bottom "margin" and each column has a left and right "margin".
# The "inner" height of each row is constrained to be the same (or as modified
# by ``height_ratio``), and the "inner" width of each column is
# constrained to be the same (as modified by ``width_ratio``), where "inner"
# is the width or height of each column/row minus the size of the margins.
#
# Then the size of the margins for each row and column are determined as the
# max width of the decorators on each axes that has decorators in that margin.
# For instance, a normal axes would have a left margin that includes the
# left ticklabels, and the ylabel if it exists. The right margin may include a
# colorbar, the bottom margin the xaxis decorations, and the top margin the
# title.
#
# With these constraints, the solver then finds appropriate bounds for the
# columns and rows. It's possible that the margins take up the whole figure,
# in which case the algorithm is not applied and a warning is raised.
#
# See the tutorial doc:`/tutorials/intermediate/constrainedlayout_guide`
# for more discussion of the algorithm with examples.

_log = logging.getLogger(__name__)


Expand All @@ -71,6 +71,8 @@ def do_constrained_layout(fig, h_pad, w_pad,
fig : Figure
``Figure`` instance to do the layout in.
renderer : Renderer
Renderer to use.
h_pad, w_pad : float
Padding around the axes elements in figure-normalized units.
Expand All @@ -91,6 +93,7 @@ def do_constrained_layout(fig, h_pad, w_pad,
layoutgrid : private debugging structure
"""

renderer = fig._get_renderer()
# make layoutgrid tree...
layoutgrids = make_layoutgrids(fig, None, rect=rect)
if not layoutgrids['hasgrids']:
Expand All @@ -107,9 +110,9 @@ def do_constrained_layout(fig, h_pad, w_pad,

# make margins for all the axes and subfigures in the
# figure. Add margins for colorbars...
make_layout_margins(layoutgrids, fig, h_pad=h_pad,
make_layout_margins(layoutgrids, fig, renderer, h_pad=h_pad,
w_pad=w_pad, hspace=hspace, wspace=wspace)
make_margin_suptitles(layoutgrids, fig, h_pad=h_pad,
make_margin_suptitles(layoutgrids, fig, renderer, h_pad=h_pad,
w_pad=w_pad)

# if a layout is such that a columns (or rows) margin has no
Expand All @@ -121,7 +124,7 @@ def do_constrained_layout(fig, h_pad, w_pad,
layoutgrids[fig].update_variables()

if check_no_collapsed_axes(layoutgrids, fig):
reposition_axes(layoutgrids, fig, h_pad=h_pad,
reposition_axes(layoutgrids, fig, renderer, h_pad=h_pad,
w_pad=w_pad, hspace=hspace, wspace=wspace)
else:
_api.warn_external('constrained_layout not applied because '
Expand Down Expand Up @@ -282,7 +285,7 @@ def get_margin_from_padding(obj, *, w_pad=0, h_pad=0,
return margin


def make_layout_margins(layoutgrids, fig, *, w_pad=0, h_pad=0,
def make_layout_margins(layoutgrids, fig, renderer, *, w_pad=0, h_pad=0,
hspace=0, wspace=0):
"""
For each axes, make a margin between the *pos* layoutbox and the
Expand All @@ -293,7 +296,7 @@ def make_layout_margins(layoutgrids, fig, *, w_pad=0, h_pad=0,
"""
for sfig in fig.subfigs: # recursively make child panel margins
ss = sfig._subplotspec
make_layout_margins(layoutgrids, sfig,
make_layout_margins(layoutgrids, sfig, renderer,
w_pad=w_pad, h_pad=h_pad,
hspace=hspace, wspace=wspace)

Expand All @@ -313,7 +316,7 @@ def make_layout_margins(layoutgrids, fig, *, w_pad=0, h_pad=0,

margin = get_margin_from_padding(ax, w_pad=w_pad, h_pad=h_pad,
hspace=hspace, wspace=wspace)
pos, bbox = get_pos_and_bbox(ax)
pos, bbox = get_pos_and_bbox(ax, renderer)
# the margin is the distance between the bounding box of the axes
# and its position (plus the padding from above)
margin['left'] += pos.x0 - bbox.x0
Expand All @@ -330,7 +333,7 @@ def make_layout_margins(layoutgrids, fig, *, w_pad=0, h_pad=0,
# colorbars can be child of more than one subplot spec:
cbp_rspan, cbp_cspan = get_cb_parent_spans(cbax)
loc = cbax._colorbar_info['location']
cbpos, cbbbox = get_pos_and_bbox(cbax)
cbpos, cbbbox = get_pos_and_bbox(cbax, renderer)
if loc == 'right':
if cbp_cspan.stop == ss.colspan.stop:
# only increase if the colorbar is on the right edge
Expand Down Expand Up @@ -366,7 +369,7 @@ def make_layout_margins(layoutgrids, fig, *, w_pad=0, h_pad=0,
layoutgrids[gs].edit_outer_margin_mins(margin, ss)


def make_margin_suptitles(layoutgrids, fig, *, w_pad=0, h_pad=0):
def make_margin_suptitles(layoutgrids, fig, renderer, *, w_pad=0, h_pad=0):
# Figure out how large the suptitle is and make the
# top level figure margin larger.

Expand All @@ -379,29 +382,29 @@ def make_margin_suptitles(layoutgrids, fig, *, w_pad=0, h_pad=0):
w_pad_local = padbox.width

for sfig in fig.subfigs:
make_margin_suptitles(layoutgrids, sfig,
make_margin_suptitles(layoutgrids, sfig, renderer,
w_pad=w_pad, h_pad=h_pad)

if fig._suptitle is not None and fig._suptitle.get_in_layout():
p = fig._suptitle.get_position()
if getattr(fig._suptitle, '_autopos', False):
fig._suptitle.set_position((p[0], 1 - h_pad_local))
bbox = inv_trans_fig(fig._suptitle.get_tightbbox())
bbox = inv_trans_fig(fig._suptitle.get_tightbbox(renderer))
layoutgrids[fig].edit_margin_min('top', bbox.height + 2 * h_pad)

if fig._supxlabel is not None and fig._supxlabel.get_in_layout():
p = fig._supxlabel.get_position()
if getattr(fig._supxlabel, '_autopos', False):
fig._supxlabel.set_position((p[0], h_pad_local))
bbox = inv_trans_fig(fig._supxlabel.get_tightbbox())
bbox = inv_trans_fig(fig._supxlabel.get_tightbbox(renderer))
layoutgrids[fig].edit_margin_min('bottom',
bbox.height + 2 * h_pad)

if fig._supylabel is not None and fig._supylabel.get_in_layout():
p = fig._supylabel.get_position()
if getattr(fig._supylabel, '_autopos', False):
fig._supylabel.set_position((w_pad_local, p[1]))
bbox = inv_trans_fig(fig._supylabel.get_tightbbox())
bbox = inv_trans_fig(fig._supylabel.get_tightbbox(renderer))
layoutgrids[fig].edit_margin_min('left', bbox.width + 2 * w_pad)


Expand Down Expand Up @@ -522,14 +525,14 @@ def get_cb_parent_spans(cbax):
return rowspan, colspan


def get_pos_and_bbox(ax):
def get_pos_and_bbox(ax, renderer):
"""
Get the position and the bbox for the axes.
Parameters
----------
ax
renderer
Returns
-------
Expand All @@ -542,15 +545,15 @@ def get_pos_and_bbox(ax):
pos = ax.get_position(original=True)
# pos is in panel co-ords, but we need in figure for the layout
pos = pos.transformed(fig.transSubfigure - fig.transFigure)
tightbbox = martist._get_tightbbox_for_layout_only(ax)
tightbbox = martist._get_tightbbox_for_layout_only(ax, renderer)
if tightbbox is None:
bbox = pos
else:
bbox = tightbbox.transformed(fig.transFigure.inverted())
return pos, bbox


def reposition_axes(layoutgrids, fig, *,
def reposition_axes(layoutgrids, fig, renderer, *,
w_pad=0, h_pad=0, hspace=0, wspace=0):
"""
Reposition all the axes based on the new inner bounding box.
Expand All @@ -560,7 +563,7 @@ def reposition_axes(layoutgrids, fig, *,
bbox = layoutgrids[sfig].get_outer_bbox()
sfig._redo_transform_rel_fig(
bbox=bbox.transformed(trans_fig_to_subfig))
reposition_axes(layoutgrids, sfig,
reposition_axes(layoutgrids, sfig, renderer,
w_pad=w_pad, h_pad=h_pad,
wspace=wspace, hspace=hspace)

Expand Down Expand Up @@ -588,11 +591,11 @@ def reposition_axes(layoutgrids, fig, *,
offset = {'left': 0, 'right': 0, 'bottom': 0, 'top': 0}
for nn, cbax in enumerate(ax._colorbars[::-1]):
if ax == cbax._colorbar_info['parents'][0]:
reposition_colorbar(layoutgrids, cbax,
reposition_colorbar(layoutgrids, cbax, renderer,
offset=offset)


def reposition_colorbar(layoutgrids, cbax, *, offset=None):
def reposition_colorbar(layoutgrids, cbax, renderer, *, offset=None):
"""
Place the colorbar in its new place.
Expand All @@ -601,6 +604,7 @@ def reposition_colorbar(layoutgrids, cbax, *, offset=None):
cbax : Axes
Axes for the colorbar
renderer :
w_pad, h_pad : float
width and height padding (in fraction of figure)
hspace, wspace : float
Expand All @@ -627,7 +631,7 @@ def reposition_colorbar(layoutgrids, cbax, *, offset=None):
aspect = cbax._colorbar_info['aspect']
shrink = cbax._colorbar_info['shrink']

cbpos, cbbbox = get_pos_and_bbox(cbax)
cbpos, cbbbox = get_pos_and_bbox(cbax, renderer)

# Colorbar gets put at extreme edge of outer bbox of the subplotspec
# It needs to be moved in by: 1) a pad 2) its "margin" 3) by
Expand Down
2 changes: 1 addition & 1 deletion lib/matplotlib/_tight_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ def get_subplotspec_list(axes_list, grid_spec=None):
return subplotspec_list


def get_tight_layout_figure(fig, axes_list, subplotspec_list, renderer=None,
def get_tight_layout_figure(fig, axes_list, subplotspec_list, renderer,
pad=1.08, h_pad=None, w_pad=None, rect=None):
"""
Return subplot parameters for tight-layouted-figure with specified padding.
Expand Down
6 changes: 4 additions & 2 deletions lib/matplotlib/contour.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,23 +276,25 @@ def get_label_coords(self, distances, XX, YY, ysize, lw):
def _get_nth_label_width(self, nth):
"""Return the width of the *nth* label, in pixels."""
fig = self.axes.figure
renderer = fig._get_renderer()
return (
text.Text(0, 0,
self.get_text(self.labelLevelList[nth], self.labelFmt),
figure=fig,
size=self.labelFontSizeList[nth],
fontproperties=self.labelFontProps)
.get_window_extent().width)
.get_window_extent(renderer).width)

@_api.deprecated("3.5")
def get_label_width(self, lev, fmt, fsize):
"""Return the width of the label in points."""
if not isinstance(lev, str):
lev = self.get_text(lev, fmt)
fig = self.axes.figure
renderer = fig._get_renderer()
width = (text.Text(0, 0, lev, figure=fig,
size=fsize, fontproperties=self.labelFontProps)
.get_window_extent().width)
.get_window_extent(renderer).width)
width *= 72 / fig.dpi
return width

Expand Down

0 comments on commit b352c5c

Please sign in to comment.