matplotlib: Get resulting bounding box of `bbox_inches=tight`
matplotlib
Solution
Thanks to @Schorsch and @tcaswell for pointing out that `ax.get_tightbbox` almost solves this problem. The only tricky part is to convert the renderer dependent bbox into an "inches" bbox. The general solution is:
tight_bbox_raw = ax.get_tightbbox(fig.canvas.get_renderer())
tight_bbox = TransformedBbox(tight_bbox_raw, Affine2D().scale(1./fig.dpi))
I can now reuse the `tight_bbox` for my other plots as well, resulting in nice, identically boxed figures.
I have also written a small helper class -- not elegant, but useful: I call `max_box_tracker.add_figure` for every figure I create, and it will internally update a maximum bounding box. After generating all plots, I can print out the resulting bounding box (directly in the form of code to add to my script).
class MaxBoxTracker:
def __init__(self):
self.box = None
def add_figure(self, fig, ax):
from matplotlib.transforms import TransformedBbox, Affine2D
box_raw = ax.get_tightbbox(fig.canvas.get_renderer())
box = TransformedBbox(box_raw, Affine2D().scale(1./fig.dpi)).get_points()
if self.box is None:
self.box = box
else:
self.box[0][0] = min(self.box[0][0], box[0][0])
self.box[0][1] = min(self.box[0][1], box[0][1])
self.box[1][0] = max(self.box[1][0], box[1][0])
self.box[1][1] = max(self.box[1][1], box[1][1])
def print_max(self):
print 'Code to set maximum bbox:\nfrom matplotlib.transforms import Bbox\ntight_bbox = Bbox([[%f, %f], [%f, %f]])' % (
self.box[0][0], self.box[0][1], self.box[1][0], self.box[1][1]
)
Problem
I often produce individual figures with matplotlib which are supposed to be aligned vertically or horizontally e.g. in a LaTeX document. My goals are: - Avoid excessive margins or clipping in all figures. For stand-alone figures this is easily solved by using `bbox_inches='tight'` in `savefig`. - Use the exact same bounding box for all figures, since I want my axes elements to line up nicely in the resulting document. My current solution is a very inconvenient trial and error approach: I manually try to guess reasonable margins and set them via `plt.subplots_adjust()`. This often takes a lot of time until I have a satisfactory result. I'm wondering if it is possible to combine the power of `bbox_inches='tight'` with `plt.subplots_adjust()`? Is it possible to programmatically get the resulting bounding box of `bbox_inches='tight'`? This would allow my to determining the bounding box for the figure with the largest axis labels/titles, and use this bounding box for all other figures.