Stretch
Stretch

Reputation: 1631

matplotlib savefig bbox_inches = 'tight' does not ignore invisible axes

When you set bbox_inches = 'tight' in Matplotlib's savefig() function, it tries to find the tightest bounding box that encapsulates all the content in your figure window. Unfortunately, the tightest bounding box appears to include invisible axes.

For example, here is a snippet where setting bbox_inches = 'tight' works as desired:

import matplotlib.pylab as plt
fig = plt.figure(figsize = (5,5))
data_ax = fig.add_axes([0.2, 0.2, 0.6, 0.6])
data_ax.plot([1,2], [1,2])
plt.savefig('Test1.pdf', bbox_inches = 'tight', pad_inches = 0)

which produces:

Nice tight bounding box

The bounds of the saved pdf correspond to the bounds of the content. This is great, except that I like to use a set of invisible figure axes to place annotations in. If the invisible axes extend beyond the bounds of the visible content, then the pdf bounds are larger than the visible content. For example:

import matplotlib.pylab as plt
fig = plt.figure(figsize = (5,5))
fig_ax = fig.add_axes([0, 0, 1, 1], frame_on = False)
fig_ax.xaxis.set_visible(False)
fig_ax.yaxis.set_visible(False)
data_ax = fig.add_axes([0.2, 0.2, 0.6, 0.6])
data_ax.plot([1,2], [1,2])
plt.savefig('Test2.pdf', bbox_inches = 'tight', pad_inches = 0)

producing

Loose bounding box

How can I force savefig() to ignore invisible items in the figure window? The only solution I have come up with is to calculate the bounding box myself and explicitly specify the bbox to savefig().

In case it matters, I am running Matplotlib 1.2.1 under Python 2.7.3 on Mac OS X 10.8.5.

Upvotes: 6

Views: 14626

Answers (2)

Stretch
Stretch

Reputation: 1631

tcaswell, thanks for your help. My original question was, "How can I force savefig() to ignore invisible items in the figure window?" When I put fig_ax.set_visible(False) then savefig() ignores the invisible axes. Unfortunately, when I set fig_ax.set_visible(False) then any artist placed in fig_ax is also invisible. I am back to the original plot I posted, where fig_ax does not exist.

As you intimated in your comment, tcaswell, I think the proper solution is avoid creating fig_ax. I am currently working on placing my annotations and data axis labels in the default figure object fig. It's a bit annoying since fig uses normalized figure units instead of mm units, but I can deal with it.

Upvotes: 1

tacaswell
tacaswell

Reputation: 87356

The relevant function (called by canvas.print_figure which is called by figure.savefig to generate the bounding box) in backend_bases.py:

def get_tightbbox(self, renderer):
    """
    Return a (tight) bounding box of the figure in inches.

    It only accounts axes title, axis labels, and axis
    ticklabels. Needs improvement.
    """

    bb = []
    for ax in self.axes:
        if ax.get_visible():
            bb.append(ax.get_tightbbox(renderer))

    _bbox = Bbox.union([b for b in bb if b.width != 0 or b.height != 0])

    bbox_inches = TransformedBbox(_bbox,
                                  Affine2D().scale(1. / self.dpi))

    return bbox_inches

The only consideration that goes into deciding if an axes is 'visible' is if ax.get_visible() returns true, even if you have no visible (either artist.get_visible() == False or simple transparent) artists in the axes.

The bounding box behavior you observe is the correct behavior.

Upvotes: 3

Related Questions