Skip to content

Commit 8605579

Browse files
committed
Merge pull request #1448 from pelson/bbox_tight_all_artists
```bbox_inches="tight"``` support for *all* figure artists.
2 parents cf6b26f + 6569ca7 commit 8605579

18 files changed

+1638
-27
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,4 @@ install:
2222
script:
2323
- mkdir ../foo
2424
- cd ../foo
25-
- python ../matplotlib/tests.py
25+
- python ../matplotlib/tests.py -sv

doc/users/whats_new.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ They may be symmetric or weighted.
4040

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

43+
Improved ``bbox_inches="tight"`` functionality
44+
----------------------------------------------
45+
Passing ``bbox_inches="tight"`` through to :func:`plt.save` now takes into account
46+
*all* artists on a figure - this was previously not the case and led to several
47+
corner cases which did not function as expected.
48+
49+
4350
Remember save directory
4451
-----------------------
4552
Martin Spacek made the save figure dialog remember the last directory saved

lib/matplotlib/artist.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,15 @@ def get_axes(self):
180180
"""
181181
return self.axes
182182

183+
def get_window_extent(self, renderer):
184+
"""
185+
Get the axes bounding box in display space.
186+
Subclasses should override for inclusion in the bounding box
187+
"tight" calculation. Default is to return an empty bounding
188+
box at 0, 0.
189+
"""
190+
return Bbox([[0, 0], [0, 0]])
191+
183192
def add_callback(self, func):
184193
"""
185194
Adds a callback function that will be called whenever one of

lib/matplotlib/axes.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9076,13 +9076,8 @@ def matshow(self, Z, **kwargs):
90769076
return im
90779077

90789078
def get_default_bbox_extra_artists(self):
9079-
bbox_extra_artists = [t for t in self.texts if t.get_visible()]
9080-
if self.legend_:
9081-
bbox_extra_artists.append(self.legend_)
9082-
if self.tables:
9083-
for t in self.tables:
9084-
bbox_extra_artists.append(t)
9085-
return bbox_extra_artists
9079+
return [artist for artist in self.get_children()
9080+
if artist.get_visible()]
90869081

90879082
def get_tightbbox(self, renderer, call_axes_locator=True):
90889083
"""

lib/matplotlib/backend_bases.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2094,15 +2094,26 @@ def print_figure(self, filename, dpi=None, facecolor='w', edgecolor='w',
20942094
renderer = self.figure._cachedRenderer
20952095
bbox_inches = self.figure.get_tightbbox(renderer)
20962096

2097-
bbox_extra_artists = kwargs.pop("bbox_extra_artists", None)
2098-
if bbox_extra_artists is None:
2099-
bbox_extra_artists = self.figure.get_default_bbox_extra_artists()
2097+
bbox_artists = kwargs.pop("bbox_extra_artists", None)
2098+
if bbox_artists is None:
2099+
bbox_artists = self.figure.get_default_bbox_extra_artists()
2100+
2101+
bbox_filtered = []
2102+
for a in bbox_artists:
2103+
bbox = a.get_window_extent(renderer)
2104+
if a.get_clip_on():
2105+
clip_box = a.get_clip_box()
2106+
if clip_box is not None:
2107+
bbox = Bbox.intersection(bbox, clip_box)
2108+
clip_path = a.get_clip_path()
2109+
if clip_path is not None and bbox is not None:
2110+
clip_path = clip_path.get_fully_transformed_path()
2111+
bbox = Bbox.intersection(bbox,
2112+
clip_path.get_extents())
2113+
if bbox is not None and (bbox.width != 0 or
2114+
bbox.height != 0):
2115+
bbox_filtered.append(bbox)
21002116

2101-
bb = [a.get_window_extent(renderer)
2102-
for a in bbox_extra_artists]
2103-
2104-
bbox_filtered = [b for b in bb
2105-
if b.width != 0 or b.height != 0]
21062117
if bbox_filtered:
21072118
_bbox = Bbox.union(bbox_filtered)
21082119
trans = Affine2D().scale(1.0 / self.figure.dpi)

lib/matplotlib/collections.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,10 +193,9 @@ def get_datalim(self, transData):
193193
return result
194194

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

201200
def _prepare_points(self):
202201
"""Point prep for drawing and hit testing"""

lib/matplotlib/figure.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1504,11 +1504,14 @@ def waitforbuttonpress(self, timeout=-1):
15041504
return blocking_input(timeout=timeout)
15051505

15061506
def get_default_bbox_extra_artists(self):
1507-
bbox_extra_artists = [t for t in self.texts if t.get_visible()]
1507+
bbox_artists = [artist for artist in self.get_children()
1508+
if artist.get_visible()]
15081509
for ax in self.axes:
15091510
if ax.get_visible():
1510-
bbox_extra_artists.extend(ax.get_default_bbox_extra_artists())
1511-
return bbox_extra_artists
1511+
bbox_artists.extend(ax.get_default_bbox_extra_artists())
1512+
# we don't want the figure's patch to influence the bbox calculation
1513+
bbox_artists.remove(self.patch)
1514+
return bbox_artists
15121515

15131516
def get_tightbbox(self, renderer):
15141517
"""

lib/matplotlib/lines.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -374,10 +374,10 @@ def set_picker(self, p):
374374
self._picker = p
375375

376376
def get_window_extent(self, renderer):
377-
bbox = Bbox.unit()
378-
bbox.update_from_data_xy(
379-
self.get_transform().transform(self.get_xydata()),
380-
ignore=True)
377+
bbox = Bbox([[0, 0], [0, 0]])
378+
trans_data_to_xy = self.get_transform().transform
379+
bbox.update_from_data_xy(trans_data_to_xy(self.get_xydata()),
380+
ignore=True)
381381
# correct for marker size, if any
382382
if self._marker:
383383
ms = (self._markersize / 72.0 * self.figure.dpi) * 0.5

lib/matplotlib/table.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,8 @@ def __init__(self, ax, loc=None, bbox=None):
202202
self._autoColumns = []
203203
self._autoFontsize = True
204204

205+
self.set_clip_on(False)
206+
205207
self._cachedRenderer = None
206208

207209
def add_cell(self, row, col, *args, **kwargs):

0 commit comments

Comments
 (0)