From 5586d43a434f6de5dc09d207c7f0b76d05fee681 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Fri, 20 Nov 2015 21:03:04 -0800 Subject: [PATCH 1/4] Turn autoscale into a contextmanager. See #5510. Also clarify a bit the implementation of autoscale, and fix an issue about the lack of effect of set_autoscalez_on for 3D axes, which was previously visible by running:: from pylab import * from mpl_toolkits import mplot3d gcf().add_subplot(111, projection="3d") plot([0, 1], [0, 1], [0, 1]) gca().set_autoscalex_on(False) gca().set_autoscalez_on(False) # has no effect plot([2, 3], [2, 3], [2, 3]) show() --- lib/matplotlib/axes/_base.py | 35 +++++++++++++++++++++--------- lib/mpl_toolkits/mplot3d/axes3d.py | 33 ++++++++++++++++------------ 2 files changed, 44 insertions(+), 24 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index b0637aef6500..efadd2b13a1c 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -4,6 +4,7 @@ from matplotlib.externals import six from matplotlib.externals.six.moves import xrange +from contextlib import contextmanager import itertools import warnings import math @@ -2103,22 +2104,36 @@ def autoscale(self, enable=True, axis='both', tight=None): The *tight* setting is retained for future autoscaling until it is explicitly changed. + This returns a context-manager/decorator that allows temporarily + setting the autoscale status, i.e.:: - Returns None. + with axes.autoscale(enable): ... + + or:: + + @axes.autoscale(enable) + def func(): ... + + will keep the autoscale status to `enable` for the duration of the + block or function and then restore it to its original value. """ - if enable is None: - scalex = True - scaley = True - else: - scalex = False - scaley = False + orig_autoscale = self._autoscaleXon, self._autoscaleYon + if enable is not None: if axis in ['x', 'both']: self._autoscaleXon = bool(enable) - scalex = self._autoscaleXon if axis in ['y', 'both']: self._autoscaleYon = bool(enable) - scaley = self._autoscaleYon - self.autoscale_view(tight=tight, scalex=scalex, scaley=scaley) + self.autoscale_view( + tight=tight, scalex=self._autoscaleXon, scaley=self._autoscaleYon) + + @contextmanager + def restore_autoscaling(): + try: + yield + finally: + self._autoscaleXon, self._autoscaleYon = orig_autoscale + + return restore_autoscaling() def autoscale_view(self, tight=None, scalex=True, scaley=True): """ diff --git a/lib/mpl_toolkits/mplot3d/axes3d.py b/lib/mpl_toolkits/mplot3d/axes3d.py index 59ce4e6626f3..78e8cfcd4d45 100755 --- a/lib/mpl_toolkits/mplot3d/axes3d.py +++ b/lib/mpl_toolkits/mplot3d/axes3d.py @@ -11,6 +11,7 @@ """ from __future__ import (absolute_import, division, print_function, unicode_literals) +from contextlib import contextmanager import math from matplotlib.externals import six @@ -342,7 +343,7 @@ def set_autoscalez_on(self, b) : .. versionadded :: 1.1.0 This function was added, but not tested. Please report any bugs. """ - self._autoscalez_on = b + self._autoscaleZon = b def set_zmargin(self, m) : """ @@ -438,25 +439,29 @@ def autoscale(self, enable=True, axis='both', tight=None) : .. versionadded :: 1.1.0 This function was added, but not tested. Please report any bugs. """ - if enable is None: - scalex = True - scaley = True - scalez = True - else: - scalex = False - scaley = False - scalez = False + orig_autoscale = ( + self._autoscaleXon, self._autoscaleYon, self._autoscaleZon) + if enable is not None: if axis in ['x', 'both']: self._autoscaleXon = bool(enable) - scalex = self._autoscaleXon if axis in ['y', 'both']: self._autoscaleYon = bool(enable) - scaley = self._autoscaleYon if axis in ['z', 'both']: self._autoscaleZon = bool(enable) - scalez = self._autoscaleZon - self.autoscale_view(tight=tight, scalex=scalex, scaley=scaley, - scalez=scalez) + self.autoscale_view(tight=tight, + scalex=self._autoscaleXon, + scaley=self._autoscaleYon, + scalez=self._autoscaleZon) + + @contextmanager + def restore_autoscaling(): + try: + yield + finally: + self._autoscaleXon, self._autoscaleYon, self._autoscaleZon = ( + orig_autoscale) + + return restore_autoscaling() def auto_scale_xyz(self, X, Y, Z=None, had_data=None): x, y, z = list(map(np.asarray, (X, Y, Z))) From 4f30ccec5e3314da2ad7860c3e7503d72e13a4f5 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sun, 22 Nov 2015 18:02:44 -0800 Subject: [PATCH 2/4] Add basic test for switching autoscale status. --- lib/matplotlib/tests/test_axes.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 5a3e6ee70e13..35cc3836c35e 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -4118,6 +4118,23 @@ def test_axisbg_warning(): ("The axisbg attribute was deprecated in version 2.0."))) +@cleanup +def test_autoscale(): + plt.plot([0, 1], [0, 1]) + assert_equal(plt.xlim(), (0, 1)) + plt.autoscale(False) + plt.plot([2, 3], [2, 3]) + assert_equal(plt.xlim(), (0, 1)) + plt.autoscale(True) + plt.plot([1, 2], [1, 2]) + assert_equal(plt.xlim(), (0, 3)) + with plt.autoscale(False): + plt.plot([3, 4], [3, 4]) + assert_equal(plt.xlim(), (0, 3)) + plt.plot([4, 5], [4, 5]) + assert_equal(plt.xlim(), (0, 5)) + + if __name__ == '__main__': import nose import sys From 90397f9c5903210e24531756b92c3ebd6cd54c19 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Mon, 23 Nov 2015 07:37:58 -0800 Subject: [PATCH 3/4] Documentation for the autoscale contextmanager. --- doc/api/api_changes.rst | 13 +++++++++++++ doc/users/whats_new.rst | 12 ++++++++++++ lib/matplotlib/axes/_base.py | 7 +------ 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/doc/api/api_changes.rst b/doc/api/api_changes.rst index 119816a18c74..85d823b18d08 100644 --- a/doc/api/api_changes.rst +++ b/doc/api/api_changes.rst @@ -11,6 +11,19 @@ sources of the changes you are experiencing. For new features that were added to matplotlib, please see :ref:`whats-new`. +Changes in 2.1.0 +================ + +Code changes +------------ + +autoscale is now a context manager +`````````````````````````````````` + +Instead of returning None, ``plt.autoscale`` and ``Axes.autoscale`` +now return a context manager, which allows one to restore the previous +autoscaling status upon exiting the block. + Changes in 2.0.0 ================ diff --git a/doc/users/whats_new.rst b/doc/users/whats_new.rst index c1657092b6ce..7107cb88c920 100644 --- a/doc/users/whats_new.rst +++ b/doc/users/whats_new.rst @@ -23,6 +23,18 @@ revision, see the :ref:`github-stats`. .. contents:: Table of Contents :depth: 3 +.. _whats-new-2-1: + +new in matplotlib-2.1 +===================== + +autoscale is now a context manager +---------------------------------- + +Instead of returning None, ``plt.autoscale`` and ``Axes.autoscale`` +now return a context manager, which allows one to restore the previous +autoscaling status upon exiting the block. + .. _whats-new-1-5: new in matplotlib-1.5 diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index efadd2b13a1c..094b86a461c3 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -2109,13 +2109,8 @@ def autoscale(self, enable=True, axis='both', tight=None): with axes.autoscale(enable): ... - or:: - - @axes.autoscale(enable) - def func(): ... - will keep the autoscale status to `enable` for the duration of the - block or function and then restore it to its original value. + block only. """ orig_autoscale = self._autoscaleXon, self._autoscaleYon if enable is not None: From 498166d309d9a46a1edd0d46d524753e702639fd Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Mon, 23 Nov 2015 23:26:49 -0800 Subject: [PATCH 4/4] WIP: Fix autoscale(None)? --- lib/matplotlib/tests/test_axes.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 35cc3836c35e..8d391ba08ca0 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -4125,8 +4125,7 @@ def test_autoscale(): plt.autoscale(False) plt.plot([2, 3], [2, 3]) assert_equal(plt.xlim(), (0, 1)) - plt.autoscale(True) - plt.plot([1, 2], [1, 2]) + plt.autoscale(None) assert_equal(plt.xlim(), (0, 3)) with plt.autoscale(False): plt.plot([3, 4], [3, 4])