Description
Problem
Currently, if an xtick is at the very edge of an x-axis, and thus the xticklabel "overhangs" further, then it will be taken into account for constrained_layout (CL) purposes. Unfortunately, this means that the axes size, after CL runs, will depend on whether there is such an "edge" xtick. In some cases, it may be preferable to ignore the x-extent of that ticklabel, even if it ends up "going over" the edge of the figure, to maintain the same axes size. (An example use case is when exporting multiple figures, all with similar axes only differing by whether there's an overhanging xticklabel, to SVG, as with this format one can just readjust the viewport of the SVG to unclip the ticklabel.)
Concretely, consider the following example:
from pylab import *
ax = plt.figure(layout="constrained", figsize=(3, 3)).add_subplot(xlim=(-.1, 1.1))
ax = plt.figure(layout="constrained", figsize=(3, 3)).add_subplot(xlim=(0, 1))
show()
Right now the axes have slightly different physical sizes, because the second one is reduced to give room to the edge tick at x=1.
I would like to be able to do something like (*)
from pylab import *
ax = plt.figure(layout="constrained", figsize=(3, 3)).add_subplot(xlim=(-.1, 1.1))
ax = plt.figure(layout="constrained", figsize=(3, 3)).add_subplot(xlim=(0, 1))
ax.figure.draw_without_rendering()
ax.xaxis.majorTicks[-1].label1.set_in_layout(False)
show()
to force the last xtick to be ignored for CL purposes.
Making this work is actually relatively easy; I believe the patch is simply
diff --git i/lib/matplotlib/axis.py w/lib/matplotlib/axis.py
index fafdf92017..a9b31ebb4e 100644
--- i/lib/matplotlib/axis.py
+++ w/lib/matplotlib/axis.py
@@ -1335,9 +1335,11 @@ class Axis(martist.Artist):
if renderer is None:
renderer = self.get_figure(root=True)._get_renderer()
return ([tick.label1.get_window_extent(renderer)
- for tick in ticks if tick.label1.get_visible()],
+ for tick in ticks
+ if tick.label1.get_visible() and tick.label1.get_in_layout()],
[tick.label2.get_window_extent(renderer)
- for tick in ticks if tick.label2.get_visible()])
+ for tick in ticks
+ if tick.label2.get_visible() and tick.label2.get_in_layout()])
def get_tightbbox(self, renderer=None, *, for_layout_only=False):
"""
but a few questions remain
- The end-user experience is not great if one really needs to do (*); can we design a better API?
- Ideally, I only want to ignore the x-extent of the xticklabels, not their y-extent (although in practice I'm saved by the fact that other xticks contribute the necessary info for the y-extent). Should we introduce set_in_layout_x() and set_in_layout_y()?
Of course, the same applies for the y-extent of yticklabels.
Proposed solution
See above.