Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bbox_inches="tight" support for *all* figure artists. #1448

Merged
merged 2 commits into from Mar 31, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Expand Up @@ -22,4 +22,4 @@ install:
script:
- mkdir ../foo
- cd ../foo
- python ../matplotlib/tests.py
- python ../matplotlib/tests.py -sv
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: This means we can see if any of the tests are producing standard out (if you're prepared to look at the travis log), and to determine easily which test is producing it.

7 changes: 7 additions & 0 deletions doc/users/whats_new.rst
Expand Up @@ -40,6 +40,13 @@ They may be symmetric or weighted.

.. plot:: mpl_examples/pylab_examples/stackplot_demo2.py

Improved ``bbox_inches="tight"`` functionality
----------------------------------------------
Passing ``bbox_inches="tight"`` through to :func:`plt.save` now takes into account
*all* artists on a figure - this was previously not the case and led to several
corner cases which did not function as expected.


Remember save directory
-----------------------
Martin Spacek made the save figure dialog remember the last directory saved
Expand Down
9 changes: 9 additions & 0 deletions lib/matplotlib/artist.py
Expand Up @@ -180,6 +180,15 @@ def get_axes(self):
"""
return self.axes

def get_window_extent(self, renderer):
"""
Get the axes bounding box in display space.
Subclasses should override for inclusion in the bounding box
"tight" calculation. Default is to return an empty bounding
box at 0, 0.
"""
return Bbox([[0, 0], [0, 0]])

def add_callback(self, func):
"""
Adds a callback function that will be called whenever one of
Expand Down
9 changes: 2 additions & 7 deletions lib/matplotlib/axes.py
Expand Up @@ -9072,13 +9072,8 @@ def matshow(self, Z, **kwargs):
return im

def get_default_bbox_extra_artists(self):
bbox_extra_artists = [t for t in self.texts if t.get_visible()]
if self.legend_:
bbox_extra_artists.append(self.legend_)
if self.tables:
for t in self.tables:
bbox_extra_artists.append(t)
return bbox_extra_artists
return [artist for artist in self.get_children()
if artist.get_visible()]

def get_tightbbox(self, renderer, call_axes_locator=True):
"""
Expand Down
27 changes: 19 additions & 8 deletions lib/matplotlib/backend_bases.py
Expand Up @@ -2092,15 +2092,26 @@ def print_figure(self, filename, dpi=None, facecolor='w', edgecolor='w',
renderer = self.figure._cachedRenderer
bbox_inches = self.figure.get_tightbbox(renderer)

bbox_extra_artists = kwargs.pop("bbox_extra_artists", None)
if bbox_extra_artists is None:
bbox_extra_artists = self.figure.get_default_bbox_extra_artists()
bbox_artists = kwargs.pop("bbox_extra_artists", None)
if bbox_artists is None:
bbox_artists = self.figure.get_default_bbox_extra_artists()

bbox_filtered = []
for a in bbox_artists:
bbox = a.get_window_extent(renderer)
if a.get_clip_on():
clip_box = a.get_clip_box()
if clip_box is not None:
bbox = Bbox.intersection(bbox, clip_box)
clip_path = a.get_clip_path()
if clip_path is not None and bbox is not None:
clip_path = clip_path.get_fully_transformed_path()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PEP8: I have a feeling this line is >= 79 characters. Can you check it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is indeed == 79 characters - but I believe that is a-ok 😄

bbox = Bbox.intersection(bbox,
clip_path.get_extents())
if bbox is not None and (bbox.width != 0 or
bbox.height != 0):
bbox_filtered.append(bbox)

bb = [a.get_window_extent(renderer)
for a in bbox_extra_artists]

bbox_filtered = [b for b in bb
if b.width != 0 or b.height != 0]
if bbox_filtered:
_bbox = Bbox.union(bbox_filtered)
trans = Affine2D().scale(1.0 / self.figure.dpi)
Expand Down
7 changes: 3 additions & 4 deletions lib/matplotlib/collections.py
Expand Up @@ -193,10 +193,9 @@ def get_datalim(self, transData):
return result

def get_window_extent(self, renderer):
bbox = self.get_datalim(transforms.IdentityTransform())
#TODO:check to ensure that this does not fail for
#cases other than scatter plot legend
return bbox
# TODO:check to ensure that this does not fail for
# cases other than scatter plot legend
return self.get_datalim(transforms.IdentityTransform())

def _prepare_points(self):
"""Point prep for drawing and hit testing"""
Expand Down
9 changes: 6 additions & 3 deletions lib/matplotlib/figure.py
Expand Up @@ -1504,11 +1504,14 @@ def waitforbuttonpress(self, timeout=-1):
return blocking_input(timeout=timeout)

def get_default_bbox_extra_artists(self):
bbox_extra_artists = [t for t in self.texts if t.get_visible()]
bbox_artists = [artist for artist in self.get_children()
if artist.get_visible()]
for ax in self.axes:
if ax.get_visible():
bbox_extra_artists.extend(ax.get_default_bbox_extra_artists())
return bbox_extra_artists
bbox_artists.extend(ax.get_default_bbox_extra_artists())
# we don't want the figure's patch to influence the bbox calculation
bbox_artists.remove(self.patch)
return bbox_artists

def get_tightbbox(self, renderer):
"""
Expand Down
8 changes: 4 additions & 4 deletions lib/matplotlib/lines.py
Expand Up @@ -374,10 +374,10 @@ def set_picker(self, p):
self._picker = p

def get_window_extent(self, renderer):
bbox = Bbox.unit()
bbox.update_from_data_xy(
self.get_transform().transform(self.get_xydata()),
ignore=True)
bbox = Bbox([[0, 0], [0, 0]])
trans_data_to_xy = self.get_transform().transform
bbox.update_from_data_xy(trans_data_to_xy(self.get_xydata()),
ignore=True)
# correct for marker size, if any
if self._marker:
ms = (self._markersize / 72.0 * self.figure.dpi) * 0.5
Expand Down
2 changes: 2 additions & 0 deletions lib/matplotlib/table.py
Expand Up @@ -202,6 +202,8 @@ def __init__(self, ax, loc=None, bbox=None):
self._autoColumns = []
self._autoFontsize = True

self.set_clip_on(False)

self._cachedRenderer = None

def add_cell(self, row, col, *args, **kwargs):
Expand Down
Binary file not shown.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.