Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Simplify axes and axis clearing, sharing, and aspect ratio control #8752

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 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
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
154 changes: 77 additions & 77 deletions lib/matplotlib/axes/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ def __init__(self, fig, rect,
================ =========================================
Keyword Description
================ =========================================
*adjustable* [ 'box' | 'datalim' | 'box-forced']
*adjustable* [ 'box' | 'datalim' ]
*alpha* float: the alpha transparency (can be None)
*anchor* [ 'C', 'SW', 'S', 'SE', 'E', 'NE', 'N',
'NW', 'W' ]
Expand Down Expand Up @@ -488,25 +488,15 @@ def __init__(self, fig, rect,
self._originalPosition = self._position.frozen()
# self.set_axes(self)
self.axes = self
self.set_aspect('auto')
self._aspect = 'auto'
self._adjustable = 'box'
self.set_anchor('C')
self._anchor = 'C'
self._sharex = sharex
self._sharey = sharey
if sharex is not None:
self._shared_x_axes.join(self, sharex)
if sharex._adjustable == 'box':
sharex._adjustable = 'datalim'
# warnings.warn(
# 'shared axes: "adjustable" is being changed to "datalim"')
self._adjustable = 'datalim'
if sharey is not None:
self._shared_y_axes.join(self, sharey)
if sharey._adjustable == 'box':
sharey._adjustable = 'datalim'
# warnings.warn(
# 'shared axes: "adjustable" is being changed to "datalim"')
self._adjustable = 'datalim'
self.set_label(label)
self.set_figure(fig)

Expand Down Expand Up @@ -537,7 +527,11 @@ def __init__(self, fig, rect,
self._hold = True

self._connected = {} # a dict from events to (id, func)
self.cla()
try:
self.cla(clear_axis=False) # new xaxis, yaxis are already cleared
except TypeError:
self.cla() # For Axes subclasses lacking clear_axis argument.

# funcs used to format x and y - fall back on major formatters
self.fmt_xdata = None
self.fmt_ydata = None
Expand Down Expand Up @@ -607,11 +601,11 @@ def get_window_extent(self, *args, **kwargs):
def _init_axis(self):
"move this out of __init__ because non-separable axes don't use it"
self.xaxis = maxis.XAxis(self)
self.spines['bottom'].register_axis(self.xaxis)
self.spines['top'].register_axis(self.xaxis)
self.spines['bottom'].register_axis(self.xaxis, clear_axis=False)
self.spines['top'].register_axis(self.xaxis, clear_axis=False)
self.yaxis = maxis.YAxis(self)
self.spines['left'].register_axis(self.yaxis)
self.spines['right'].register_axis(self.yaxis)
self.spines['left'].register_axis(self.yaxis, clear_axis=False)
self.spines['right'].register_axis(self.yaxis, clear_axis=False)
self._update_transScale()

def set_figure(self, fig):
Expand All @@ -634,8 +628,7 @@ def set_figure(self, fig):

def _set_lim_and_transforms(self):
"""
set the *dataLim* and *viewLim*
:class:`~matplotlib.transforms.Bbox` attributes and the
set the *_xaxis_transform*, *_yaxis_transform*,
*transScale*, *transData*, *transLimits* and *transAxes*
transformations.

Expand Down Expand Up @@ -952,7 +945,7 @@ def _gen_axes_spines(self, locations=None, offset=0.0, units='inches'):
('bottom', mspines.Spine.linear_spine(self, 'bottom')),
('top', mspines.Spine.linear_spine(self, 'top'))])

def cla(self):
def cla(self, clear_axis=True):
"""Clear the current axes."""
# Note: this is called by Axes.__init__()

Expand All @@ -965,51 +958,46 @@ def cla(self):
xaxis_visible = self.xaxis.get_visible()
yaxis_visible = self.yaxis.get_visible()

self.xaxis.cla()
self.yaxis.cla()
# The axis cla() sets the scale and default locators and
# formatters. It needs to know if the axis is shared
# so that it can preserve the shared scale.
shared_x = self._sharex.xaxis if self._sharex else None
shared_y = self._sharey.yaxis if self._sharey else None

if clear_axis:
self.xaxis.cla(shared_x)
self.yaxis.cla(shared_y)

for name, spine in six.iteritems(self.spines):
spine.cla()
spine.cla(clear_axis=False) # Clears only the position.

self.ignore_existing_data_limits = True
self.callbacks = cbook.CallbackRegistry()

if self._sharex is not None:
# major and minor are class instances with
if shared_x is not None:
# major and minor are axis.Ticker class instances with
# locator and formatter attributes
self.xaxis.major = self._sharex.xaxis.major
self.xaxis.minor = self._sharex.xaxis.minor
self.xaxis.major = shared_x.major
self.xaxis.minor = shared_x.minor
x0, x1 = self._sharex.get_xlim()
self.set_xlim(x0, x1, emit=False, auto=None)
self.xaxis._scale = mscale.scale_factory(
self._sharex.xaxis.get_scale(), self.xaxis)
else:
self.xaxis._set_scale('linear')
try:
self.set_xlim(0, 1)
except TypeError:
pass

if self._sharey is not None:
self.yaxis.major = self._sharey.yaxis.major
self.yaxis.minor = self._sharey.yaxis.minor
if shared_y is not None:
self.yaxis.major = shared_y.major
self.yaxis.minor = shared_y.minor
y0, y1 = self._sharey.get_ylim()
self.set_ylim(y0, y1, emit=False, auto=None)
self.yaxis._scale = mscale.scale_factory(
self._sharey.yaxis.get_scale(), self.yaxis)
else:
self.yaxis._set_scale('linear')
try:
self.set_ylim(0, 1)
except TypeError:
pass

# update the minor locator for x and y axis based on rcParams
if (rcParams['xtick.minor.visible']):
self.xaxis.set_minor_locator(mticker.AutoMinorLocator())

if (rcParams['ytick.minor.visible']):
self.yaxis.set_minor_locator(mticker.AutoMinorLocator())

self._autoscaleXon = True
self._autoscaleYon = True
self._xmargin = rcParams['axes.xmargin']
Expand Down Expand Up @@ -1093,6 +1081,13 @@ def cla(self):
self.yaxis.set_visible(yaxis_visible)
self.patch.set_visible(patch_visible)

# It is not clear to me (EF) why this reset is needed here, but
Copy link
Member

Choose a reason for hiding this comment

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

Axes, Axis, Spine, and Tick are all inexplicably intertwined. I've been trying to disconnect them (e.g., #8678), but there's always one last bug every time...

# it does seem to be needed somewhere in this vicinity. Otherwise,
# setting tick rotation via set_params doesn't work until the
# first draw has occurred.
self.xaxis.reset_ticks()
self.yaxis.reset_ticks()

self.stale = True

@cbook.deprecated("2.1", alternative="Axes.patch")
Expand Down Expand Up @@ -1224,7 +1219,7 @@ def hold(self, b=None):
def get_aspect(self):
return self._aspect

def set_aspect(self, aspect, adjustable=None, anchor=None):
def set_aspect(self, aspect, adjustable=None, anchor=None, share=False):
"""
*aspect*

Expand All @@ -1245,12 +1240,9 @@ def set_aspect(self, aspect, adjustable=None, anchor=None):
============ =====================================
'box' change physical size of axes
'datalim' change xlim or ylim
'box-forced' same as 'box', but axes can be shared
============ =====================================

'box' does not allow axes sharing, as this can cause
unintended side effect. For cases when sharing axes is
fine, use 'box-forced'.
When both axes are shared, only 'box' is allowable.

*anchor*

Expand All @@ -1270,24 +1262,36 @@ def set_aspect(self, aspect, adjustable=None, anchor=None):
else:
self._aspect = float(aspect) # raise ValueError if necessary

if adjustable is not None:
self.set_adjustable(adjustable)
if share and self in self._shared_x_axes:
for ax in self._shared_x_axes.get_siblings(self):
ax._aspect = aspect
if share and self in self._shared_y_axes:
for ax in self._shared_y_axes.get_siblings(self):
ax._aspect = aspect

if adjustable is None:
adjustable = self._adjustable
self.set_adjustable(adjustable, share=share) # Handle sharing.

if anchor is not None:
self.set_anchor(anchor)
self.stale = True

def get_adjustable(self):
return self._adjustable

def set_adjustable(self, adjustable):
def set_adjustable(self, adjustable, share=False):
"""
ACCEPTS: [ 'box' | 'datalim' | 'box-forced']
ACCEPTS: [ 'box' | 'datalim']
"""
# FIXME: add box-forced deprecation
if adjustable in ('box', 'datalim', 'box-forced'):
if self in self._shared_x_axes or self in self._shared_y_axes:
if adjustable == 'box':
raise ValueError(
'adjustable must be "datalim" for shared axes')
if share and self in self._shared_x_axes:
for ax in self._shared_x_axes.get_siblings(self):
ax._adjustable = adjustable
if share and self in self._shared_y_axes:
for ax in self._shared_y_axes.get_siblings(self):
ax._adjustable = adjustable
self._adjustable = adjustable
else:
raise ValueError('argument must be "box", or "datalim"')
Expand Down Expand Up @@ -1387,14 +1391,6 @@ def apply_aspect(self, position=None):
else:
A = aspect

# Ensure at drawing time that any Axes involved in axis-sharing
# does not have its position changed.
if self in self._shared_x_axes or self in self._shared_y_axes:
if self._adjustable == 'box':
self._adjustable = 'datalim'
warnings.warn(
'shared axes: "adjustable" is being changed to "datalim"')

figW, figH = self.get_figure().get_size_inches()
fig_aspect = figH / figW
if self._adjustable in ['box', 'box-forced']:
Expand Down Expand Up @@ -1452,23 +1448,24 @@ def apply_aspect(self, position=None):
xm = 0
ym = 0

changex = (self in self._shared_y_axes and
self not in self._shared_x_axes)
changey = (self in self._shared_x_axes and
self not in self._shared_y_axes)
if changex and changey:
warnings.warn("adjustable='datalim' cannot work with shared "
"x and y axes")
return
if changex:
shared_x = self in self._shared_x_axes
shared_y = self in self._shared_y_axes
# Not sure whether we need this check:
if shared_x and shared_y:
raise RuntimeError("adjustable='datalim' is not allowed when both"
" axes are shared.")

# If y is shared, then we are only allowed to change x, etc.
if shared_y:
adjust_y = False
else:
if xmarg > xm and ymarg > ym:
adjy = ((Ymarg > 0 and y_expander < 0) or
(Xmarg < 0 and y_expander > 0))
else:
adjy = y_expander > 0
adjust_y = changey or adjy # (Ymarg > xmarg)
adjust_y = shared_x or adjy # (Ymarg > xmarg)

if adjust_y:
yc = 0.5 * (ymin + ymax)
y0 = yc - Ysize / 2.0
Expand Down Expand Up @@ -3915,6 +3912,9 @@ def _make_twin_axes(self, *kl, **kwargs):
make a twinx axes of self. This is used for twinx and twiny.
"""
ax2 = self.figure.add_axes(self.get_position(True), *kl, **kwargs)
# do not touch every thing shared, just this and it's twin.
self.set_adjustable('datalim')
ax2.set_adjustable('datalim')
return ax2

def twinx(self):
Expand Down Expand Up @@ -3977,9 +3977,9 @@ def twiny(self):
return ax2

def get_shared_x_axes(self):
'Return a copy of the shared axes Grouper object for x axes'
'Return a reference to the shared axes Grouper object for x axes'
return self._shared_x_axes

def get_shared_y_axes(self):
'Return a copy of the shared axes Grouper object for y axes'
'Return a reference to the shared axes Grouper object for y axes'
return self._shared_y_axes
Loading