From f8459a513c3f67447ceb1a07c29760d504517ff2 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Wed, 18 Mar 2020 23:00:00 -0400 Subject: [PATCH 1/2] Use cbook._setattr_cm more. --- lib/matplotlib/legend_handler.py | 13 +++++-------- lib/matplotlib/quiver.py | 21 +++++++++------------ lib/matplotlib/widgets.py | 6 ++---- 3 files changed, 16 insertions(+), 24 deletions(-) diff --git a/lib/matplotlib/legend_handler.py b/lib/matplotlib/legend_handler.py index 58e8a5ee8ef2..76789d68ff2b 100644 --- a/lib/matplotlib/legend_handler.py +++ b/lib/matplotlib/legend_handler.py @@ -28,6 +28,7 @@ def legend_artist(self, legend, orig_handle, fontsize, handlebox) import numpy as np +from matplotlib import cbook from matplotlib.lines import Line2D from matplotlib.patches import Rectangle import matplotlib.collections as mcoll @@ -607,19 +608,15 @@ def create_artists(self, legend, orig_handle, if using_linecoll: # change the function used by update_prop() from the default # to one that handles LineCollection - orig_update_func = self._update_prop_func - self._update_prop_func = self._copy_collection_props - - for line in leg_stemlines: - self.update_prop(line, stemlines, legend) + with cbook._setattr_cm( + self, _update_prop_func=self._copy_collection_props): + for line in leg_stemlines: + self.update_prop(line, stemlines, legend) else: for lm, m in zip(leg_stemlines, stemlines): self.update_prop(lm, m, legend) - if using_linecoll: - self._update_prop_func = orig_update_func - leg_baseline = Line2D([np.min(xdata), np.max(xdata)], [bottom, bottom]) self.update_prop(leg_baseline, baseline, legend) diff --git a/lib/matplotlib/quiver.py b/lib/matplotlib/quiver.py index 91127b296896..abed5202cbb8 100644 --- a/lib/matplotlib/quiver.py +++ b/lib/matplotlib/quiver.py @@ -306,18 +306,15 @@ def _init(self): if not self.Q._initialized: self.Q._init() self._set_transform() - _pivot = self.Q.pivot - self.Q.pivot = self.pivot[self.labelpos] - # Hack: save and restore the Umask - _mask = self.Q.Umask - self.Q.Umask = ma.nomask - u = self.U * np.cos(np.radians(self.angle)) - v = self.U * np.sin(np.radians(self.angle)) - angle = self.Q.angles if isinstance(self.Q.angles, str) else 'uv' - self.verts = self.Q._make_verts( - np.array([u]), np.array([v]), angle) - self.Q.Umask = _mask - self.Q.pivot = _pivot + with cbook._setattr_cm(self.Q, pivot=self.pivot[self.labelpos], + # Hack: save and restore the Umask + Umask=ma.nomask): + u = self.U * np.cos(np.radians(self.angle)) + v = self.U * np.sin(np.radians(self.angle)) + angle = (self.Q.angles if isinstance(self.Q.angles, str) + else 'uv') + self.verts = self.Q._make_verts( + np.array([u]), np.array([v]), angle) kw = self.Q.polykw kw.update(self.kw) self.vector = mcollections.PolyCollection( diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 9877955ccd3e..2615bd2e25a1 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -1151,10 +1151,8 @@ def __init__(self, targetfig, toolfig): # During reset there can be a temporary invalid state depending on the # order of the reset so we turn off validation for the resetting - validate = toolfig.subplotpars.validate - toolfig.subplotpars.validate = False - self.buttonreset.on_clicked(self._on_reset) - toolfig.subplotpars.validate = validate + with cbook._setattr_cm(toolfig.subplotpars, validate=False): + self.buttonreset.on_clicked(self._on_reset) def _on_slider_changed(self, _): self.targetfig.subplots_adjust( From af745264376a10782bd0d8b96d255f958c2950f3 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Wed, 18 Mar 2020 23:36:46 -0400 Subject: [PATCH 2/2] Fix Text('').get_window_extent(dpi=...). This special case does not correctly reset the figure dpi, so the figure randomly gets bigger when it's called. This does not affect Text with actual content. --- lib/matplotlib/tests/test_text.py | 16 ++++++++++++++++ lib/matplotlib/text.py | 23 +++++++++++------------ 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/lib/matplotlib/tests/test_text.py b/lib/matplotlib/tests/test_text.py index a8ed808d4f31..7dc7c9ff90dc 100644 --- a/lib/matplotlib/tests/test_text.py +++ b/lib/matplotlib/tests/test_text.py @@ -321,6 +321,22 @@ def test_set_position(): assert a + shift_val == b +@pytest.mark.parametrize('text', ['', 'O'], ids=['empty', 'non-empty']) +def test_non_default_dpi(text): + fig, ax = plt.subplots() + + t1 = ax.text(0.5, 0.5, text, ha='left', va='bottom') + fig.canvas.draw() + dpi = fig.dpi + + bbox1 = t1.get_window_extent() + bbox2 = t1.get_window_extent(dpi=dpi * 10) + np.testing.assert_allclose(bbox2.get_points(), bbox1.get_points() * 10, + rtol=5e-2) + # Text.get_window_extent should not permanently change dpi. + assert fig.dpi == dpi + + def test_get_rotation_string(): assert mpl.text.get_rotation('horizontal') == 0. assert mpl.text.get_rotation('vertical') == 90. diff --git a/lib/matplotlib/text.py b/lib/matplotlib/text.py index 429d31fb901a..77ab7145a7c2 100644 --- a/lib/matplotlib/text.py +++ b/lib/matplotlib/text.py @@ -891,12 +891,12 @@ def get_window_extent(self, renderer=None, dpi=None): #return _unit_box if not self.get_visible(): return Bbox.unit() - if dpi is not None: - dpi_orig = self.figure.dpi - self.figure.dpi = dpi + if dpi is None: + dpi = self.figure.dpi if self.get_text() == '': - tx, ty = self._get_xy_display() - return Bbox.from_bounds(tx, ty, 0, 0) + with cbook._setattr_cm(self.figure, dpi=dpi): + tx, ty = self._get_xy_display() + return Bbox.from_bounds(tx, ty, 0, 0) if renderer is not None: self._renderer = renderer @@ -905,13 +905,12 @@ def get_window_extent(self, renderer=None, dpi=None): if self._renderer is None: raise RuntimeError('Cannot get window extent w/o renderer') - bbox, info, descent = self._get_layout(self._renderer) - x, y = self.get_unitless_position() - x, y = self.get_transform().transform((x, y)) - bbox = bbox.translated(x, y) - if dpi is not None: - self.figure.dpi = dpi_orig - return bbox + with cbook._setattr_cm(self.figure, dpi=dpi): + bbox, info, descent = self._get_layout(self._renderer) + x, y = self.get_unitless_position() + x, y = self.get_transform().transform((x, y)) + bbox = bbox.translated(x, y) + return bbox def set_backgroundcolor(self, color): """