Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Bug]: fig.tight_layout when quiver collection is clipped produces AttributeError #24104

Closed
@ondrolexa

Description

@ondrolexa

Bug summary

When I set a clipping path to quiver object with set_clip_path(), Figure.tight_layout() produces AttributeError: 'NoneType' object has no attribute 'xmin'. Without tight_layout() everything works.

Code for reproduction

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle

x, y = np.mgrid[-1:1:0.1, -1:1:0.1]
z = np.sin(x) + np.cos(y)
xg, yg = np.gradient(z)

f, ax = plt.subplots()
h = ax.quiver(x, y, xg, yg, label='Test')
c = Circle((0, 0), radius=1, edgecolor="black", fill=False)
ax.add_patch(c)
h.set_clip_path(c)
ax.set_aspect(1)
f.tight_layout()

Actual outcome

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In [42], line 15
     13 h.set_clip_path(c)
     14 ax.set_aspect(1)
---> 15 f.tight_layout()

File ~/miniforge3/envs/apsg/lib/python3.10/site-packages/matplotlib/figure.py:3443, in Figure.tight_layout(self, pad, h_pad, w_pad, rect)
   3441 try:
   3442     self.set_layout_engine(engine)
-> 3443     engine.execute(self)
   3444 finally:
   3445     self.set_layout_engine(None)

File ~/miniforge3/envs/apsg/lib/python3.10/site-packages/matplotlib/layout_engine.py:180, in TightLayoutEngine.execute(self, fig)
    178 renderer = fig._get_renderer()
    179 with getattr(renderer, "_draw_disabled", nullcontext)():
--> 180     kwargs = get_tight_layout_figure(
    181         fig, fig.axes, subplotspec_list, renderer,
    182         pad=info['pad'], h_pad=info['h_pad'], w_pad=info['w_pad'],
    183         rect=info['rect'])
    184 if kwargs:
    185     fig.subplots_adjust(**kwargs)

File ~/miniforge3/envs/apsg/lib/python3.10/site-packages/matplotlib/_tight_layout.py:305, in get_tight_layout_figure(fig, axes_list, subplotspec_list, renderer, pad, h_pad, w_pad, rect)
    300         return {}
    301     span_pairs.append((
    302         slice(ss.rowspan.start * div_row, ss.rowspan.stop * div_row),
    303         slice(ss.colspan.start * div_col, ss.colspan.stop * div_col)))
--> 305 kwargs = _auto_adjust_subplotpars(fig, renderer,
    306                                   shape=(max_nrows, max_ncols),
    307                                   span_pairs=span_pairs,
    308                                   subplot_list=subplot_list,
    309                                   ax_bbox_list=ax_bbox_list,
    310                                   pad=pad, h_pad=h_pad, w_pad=w_pad)
    312 # kwargs can be none if tight_layout fails...
    313 if rect is not None and kwargs is not None:
    314     # if rect is given, the whole subplots area (including
    315     # labels) will fit into the rect instead of the
   (...)
    319     # auto_adjust_subplotpars twice, where the second run
    320     # with adjusted rect parameters.

File ~/miniforge3/envs/apsg/lib/python3.10/site-packages/matplotlib/_tight_layout.py:82, in _auto_adjust_subplotpars(fig, renderer, shape, span_pairs, subplot_list, ax_bbox_list, pad, h_pad, w_pad, rect)
     80 for ax in subplots:
     81     if ax.get_visible():
---> 82         bb += [martist._get_tightbbox_for_layout_only(ax, renderer)]
     84 tight_bbox_raw = Bbox.union(bb)
     85 tight_bbox = fig.transFigure.inverted().transform_bbox(tight_bbox_raw)

File ~/miniforge3/envs/apsg/lib/python3.10/site-packages/matplotlib/artist.py:1378, in _get_tightbbox_for_layout_only(obj, *args, **kwargs)
   1372 """
   1373 Matplotlib's `.Axes.get_tightbbox` and `.Axis.get_tightbbox` support a
   1374 *for_layout_only* kwarg; this helper tries to uses the kwarg but skips it
   1375 when encountering third-party subclasses that do not support it.
   1376 """
   1377 try:
-> 1378     return obj.get_tightbbox(*args, **{**kwargs, "for_layout_only": True})
   1379 except TypeError:
   1380     return obj.get_tightbbox(*args, **kwargs)

File ~/miniforge3/envs/apsg/lib/python3.10/site-packages/matplotlib/axes/_base.py:4450, in _AxesBase.get_tightbbox(self, renderer, call_axes_locator, bbox_extra_artists, for_layout_only)
   4447     bbox_artists = self.get_default_bbox_extra_artists()
   4449 for a in bbox_artists:
-> 4450     bbox = a.get_tightbbox(renderer)
   4451     if (bbox is not None
   4452             and 0 < bbox.width < np.inf
   4453             and 0 < bbox.height < np.inf):
   4454         bb.append(bbox)

File ~/miniforge3/envs/apsg/lib/python3.10/site-packages/matplotlib/artist.py:345, in Artist.get_tightbbox(self, renderer)
    343     if clip_path is not None:
    344         clip_path = clip_path.get_fully_transformed_path()
--> 345         bbox = Bbox.intersection(bbox, clip_path.get_extents())
    346 return bbox

File ~/miniforge3/envs/apsg/lib/python3.10/site-packages/matplotlib/transforms.py:666, in BboxBase.intersection(bbox1, bbox2)
    660 @staticmethod
    661 def intersection(bbox1, bbox2):
    662     """
    663     Return the intersection of *bbox1* and *bbox2* if they intersect, or
    664     None if they don't.
    665     """
--> 666     x0 = np.maximum(bbox1.xmin, bbox2.xmin)
    667     x1 = np.minimum(bbox1.xmax, bbox2.xmax)
    668     y0 = np.maximum(bbox1.ymin, bbox2.ymin)

AttributeError: 'NoneType' object has no attribute 'xmin'

Expected outcome

No error

Additional information

No response

Operating system

Ubuntu

Matplotlib Version

3.6.0

Matplotlib Backend

module://matplotlib_inline.backend_inline

Python version

3.10.6

Jupyter version

3.4.8

Installation

conda

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions