I'm using gridspec to make a rather complicated figure layout. It works mostly fine without spacing adjustments, but some labels still overlap in the 4 subplots at top right:
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.gridspec as gs
fig = plt.figure(figsize=(30, 22))
TheGrid = gs.GridSpec(9, 12, figure=fig)
# Set up the grid
# Main plot: top left of figure
MainAx = plt.subplot(TheGrid.new_subplotspec((0, 0), colspan=4, rowspan=4))
MainAx.set_xlabel("MainAx - x", fontsize=24)
MainAx.set_ylabel("MainAx - y", fontsize=24)
# An empty axis to create some space between MainAx and SubAx1 and SubAx2
VerticalSpacer = plt.subplot(TheGrid.new_subplotspec((0, 4), rowspan=4))
VerticalSpacer.axis("off")
# Two small subplots to the right of main plot
SubAx1 = plt.subplot(TheGrid.new_subplotspec((0, 5), colspan=2, rowspan=2))
SubAx2 = plt.subplot(TheGrid.new_subplotspec((2, 5), colspan=2, rowspan=2))
SubAx1.axes.get_xaxis().set_visible(False)
SubAx2.set_xlabel("SubAx2 - x", fontsize=24)
SubAx1.set_ylabel("SubAx1 - y", fontsize=24)
SubAx2.set_ylabel("SubAx2 - y", fontsize=24)
# An empty axis to create some space between SubAx1, SubAx2 and the next axes
VerticalSpacer2 = plt.subplot(TheGrid.new_subplotspec((0, 7), rowspan=4))
VerticalSpacer2.axis("off")
# 4 small subplots at top right of figure
Iax1 = plt.subplot(TheGrid.new_subplotspec((0, 8), colspan=2, rowspan=2))
Iax1.set_xlabel("Iax1 - x", fontsize=24)
Iax1.set_ylabel("Iax1 - y", fontsize=24)
Iax1.set_title("Iax1", fontsize=24)
Iax2 = plt.subplot(TheGrid.new_subplotspec((0, 10), colspan=2, rowspan=2))
Iax3 = plt.subplot(TheGrid.new_subplotspec((2, 8), colspan=2, rowspan=2))
Iax3.set_xlabel("Iax3 - x", fontsize=24)
Iax3.set_ylabel("Iax3 - y", fontsize=24)
Iax3.set_title("Iax3", fontsize=24)
Iax4 = plt.subplot(TheGrid.new_subplotspec((2, 10), colspan=2, rowspan=2))
# Empty axis to create some space below the above axes
HorizontalSpacer = plt.subplot(TheGrid.new_subplotspec((4, 0), colspan=10))
HorizontalSpacer.axis("off")
# An axis to contain numerous small subplots without labels
SnapshotAx = plt.subplot(TheGrid.new_subplotspec((5, 0), colspan=10, rowspan=4))
SnapshotAxSub = gs.GridSpecFromSubplotSpec(4, 10, subplot_spec=SnapshotAx)
# Plot the snapshots
row_count = 0
col_count = 0
for i in range(38):
if (i % 10 == 0):
col_count = 0
if i != 0:
row_count += 1
else:
col_count += 1
temp_ax = plt.subplot(SnapshotAxSub.new_subplotspec((row_count, col_count), colspan=1, rowspan=1))
temp_ax.plot([1,2,3], [1,2,3])
# turn off pesky ticks
temp_ax.axes.get_xaxis().set_visible(False)
temp_ax.axes.get_yaxis().set_visible(False)
plt.show()
(Note that the loop over 38 items represents a part of the code in which, at each creation of this figure, I have to create an unknown number of subplots, but not more than about 40. I chose 38 here as an arbitrary number to demonstrate the extent of that axis.)
Strangely, when layout='constrained'
is added, the code breaks and throws the following error:
fig = plt.figure(figsize=(30, 22), layout="constrained")
...(all other code the same)...
This throws the following error:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
File ~/anaconda3/lib/python3.8/site-packages/IPython/core/formatters.py:339, in BaseFormatter.__call__(self, obj)
337 pass
338 else:
--> 339 return printer(obj)
340 # Finally look for special method names
341 method = get_real_method(obj, self.print_method)
File ~/anaconda3/lib/python3.8/site-packages/IPython/core/pylabtools.py:151, in print_figure(fig, fmt, bbox_inches, base64, **kwargs)
148 from matplotlib.backend_bases import FigureCanvasBase
149 FigureCanvasBase(fig)
--> 151 fig.canvas.print_figure(bytes_io, **kw)
152 data = bytes_io.getvalue()
153 if fmt == 'svg':
File ~/anaconda3/lib/python3.8/site-packages/matplotlib/backend_bases.py:2342, in FigureCanvasBase.print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs)
2336 renderer = _get_renderer(
2337 self.figure,
2338 functools.partial(
2339 print_method, orientation=orientation)
2340 )
2341 with getattr(renderer, "_draw_disabled", nullcontext)():
-> 2342 self.figure.draw(renderer)
2344 if bbox_inches:
2345 if bbox_inches == "tight":
File ~/anaconda3/lib/python3.8/site-packages/matplotlib/artist.py:95, in _finalize_rasterization.<locals>.draw_wrapper(artist, renderer, *args, **kwargs)
93 @wraps(draw)
94 def draw_wrapper(artist, renderer, *args, **kwargs):
---> 95 result = draw(artist, renderer, *args, **kwargs)
96 if renderer._rasterizing:
97 renderer.stop_rasterizing()
File ~/anaconda3/lib/python3.8/site-packages/matplotlib/artist.py:72, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
69 if artist.get_agg_filter() is not None:
70 renderer.start_filter()
---> 72 return draw(artist, renderer)
73 finally:
74 if artist.get_agg_filter() is not None:
File ~/anaconda3/lib/python3.8/site-packages/matplotlib/figure.py:3134, in Figure.draw(self, renderer)
3132 if self.axes and self.get_layout_engine() is not None:
3133 try:
-> 3134 self.get_layout_engine().execute(self)
3135 except ValueError:
3136 pass
File ~/anaconda3/lib/python3.8/site-packages/matplotlib/layout_engine.py:253, in ConstrainedLayoutEngine.execute(self, fig)
250 w_pad = self._params['w_pad'] / width
251 h_pad = self._params['h_pad'] / height
--> 253 return do_constrained_layout(fig, w_pad=w_pad, h_pad=h_pad,
254 wspace=self._params['wspace'],
255 hspace=self._params['hspace'],
256 rect=self._params['rect'],
257 compress=self._compress)
File ~/anaconda3/lib/python3.8/site-packages/matplotlib/_constrained_layout.py:104, in do_constrained_layout(fig, h_pad, w_pad, hspace, wspace, rect, compress)
102 renderer = fig._get_renderer()
103 # make layoutgrid tree...
--> 104 layoutgrids = make_layoutgrids(fig, None, rect=rect)
105 if not layoutgrids['hasgrids']:
106 _api.warn_external('There are no gridspecs with layoutgrids. '
107 'Possibly did not call parent GridSpec with the'
108 ' "figure" keyword')
File ~/anaconda3/lib/python3.8/site-packages/matplotlib/_constrained_layout.py:192, in make_layoutgrids(fig, layoutgrids, rect)
190 gs = ax.get_gridspec()
191 if gs is not None:
--> 192 layoutgrids = make_layoutgrids_gs(layoutgrids, gs)
194 return layoutgrids
File ~/anaconda3/lib/python3.8/site-packages/matplotlib/_constrained_layout.py:233, in make_layoutgrids_gs(layoutgrids, gs)
227 rep = (gs, 'top')
228 if rep not in layoutgrids:
229 layoutgrids[rep] = mlayoutgrid.LayoutGrid(
230 parent=subspeclb,
231 name='top',
232 nrows=1, ncols=1,
--> 233 parent_pos=(subplot_spec.rowspan, subplot_spec.colspan))
234 layoutgrids[gs] = mlayoutgrid.LayoutGrid(
235 parent=layoutgrids[rep],
236 name='gridspec',
237 nrows=gs._nrows, ncols=gs._ncols,
238 width_ratios=gs.get_width_ratios(),
239 height_ratios=gs.get_height_ratios())
240 return layoutgrids
AttributeError: 'Axes' object has no attribute 'rowspan'
All of the errors highlighted are within Python and Matlotlib. These are installed via Anaconda; version of Python is 3.8.13, and the Matplotlib version is 3.7.1. Matplotlib was previously 3.5, so I tried upgrading it after a few SE/google searches, but this has not helped the problem. I haven't found any resources on this particular error being encountered simply by triggering layout="'constrained'
.
Is there something else in my code that could be causing this? Thanks in advance.