Skip to content

Turn autoscale into a contextmanager. #5538

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

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
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()
  • Loading branch information
anntzer committed Nov 23, 2015
commit 5586d43a434f6de5dc09d207c7f0b76d05fee681
35 changes: 25 additions & 10 deletions lib/matplotlib/axes/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Copy link
Member

Choose a reason for hiding this comment

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

I would not suggest this in the documentation as this binds the definition of this function to a specific instance.

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(
Copy link
Member

Choose a reason for hiding this comment

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

This does not behave the same if enable is None. Previously, ax.autoscale(None) would autoscale both axis, but leave the _autoscale*on state unchanged, now it scale the axis dependent on the current values of _autoscale*on.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK, so I misunderstood the code (I never used autoscale(None) so I had no idea what it did). I still don't understand the actual behavior: autoscaling (in autoscale_view) is protected by the conditional if scalex and self._autoscaleXon, so I thought that just passing scalex=True would not perform an autoscale as long as self._autoscaleXon is False.
Where did I go wrong?

Copy link
Member

Choose a reason for hiding this comment

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

I was just reading this code locally and sorting through what inputs are going to the autoscale_view, you are correct this does not actually change the behaviour. Sorry for the noise.

tight=tight, scalex=self._autoscaleXon, scaley=self._autoscaleYon)

@contextmanager
def restore_autoscaling():
try:
yield
finally:
self._autoscaleXon, self._autoscaleYon = orig_autoscale
Copy link
Member

Choose a reason for hiding this comment

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

should we mark the axes object as stale?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

(I have no idea of what you're talking about so I'll leave the discussion to the specialists.)

Copy link
Member

Choose a reason for hiding this comment

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

I don't think this needs to set stale because changing this internal state does not invalidate the drawn figure (that is the next call to draw will not be different)....unless I am miss understanding how this works.


return restore_autoscaling()

def autoscale_view(self, tight=None, scalex=True, scaley=True):
"""
Expand Down
33 changes: 19 additions & 14 deletions lib/mpl_toolkits/mplot3d/axes3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) :
"""
Expand Down Expand Up @@ -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)))
Expand Down