diff --git a/doc/api/next_api_changes/2017-12-06-KL.rst b/doc/api/next_api_changes/2017-12-06-KL.rst new file mode 100644 index 000000000000..1321454ebbff --- /dev/null +++ b/doc/api/next_api_changes/2017-12-06-KL.rst @@ -0,0 +1,26 @@ +Change return value of Axes.get_shared_[x,y,z]_axes() +----------------------------------------------------- + +The method `matplotlib.Axes.get_shared_x_axes` (and y and z) used to return `~.cbook.Grouper` objects. +Now it returns a `~.weakref.WeakSet` object. + +Workarounds: +* If the intention is to get siblings as previous then the WeakSet contains all the siblings. +An example:: + + sharedx = ax.get_shared_x_axes().get_siblings() + # is now + sharedx = list(ax.get_shared_x_axes()) + +* If the intention was to use `join` then there is a new share axes method. An example:: + + ax1.get_shared_x_axes().join(ax1, ax2) + # is now + ax1.share_x_axes(ax2) + +* If the intention was to check if two elements are in the same group then use the `in` operator. An example:: + + ax1.get_shared_x_axes().joined(ax1, ax2) + # is now + ax2 in ax1.get_shared_x_axes() + diff --git a/doc/api/next_api_changes/remove_old_share_axes_after_creation.rst b/doc/api/next_api_changes/remove_old_share_axes_after_creation.rst new file mode 100644 index 000000000000..067533834e4b --- /dev/null +++ b/doc/api/next_api_changes/remove_old_share_axes_after_creation.rst @@ -0,0 +1,8 @@ +Remove share through `matplotlib.Axes.get_shared_{x,y,z}_axes` +-------------------------------------------------------------- + +Previously when different axes are created with different parent/master axes, +the share would still be symmetric and transitive if an unconventional +method through `matplotlib.Axes.get_shared_x_axes` +is used to share the axes after creation. With the new sharing mechanism +this is no longer possible. diff --git a/doc/users/next_whats_new/2017-12-05_share_unshare_axes_after_creation.rst b/doc/users/next_whats_new/2017-12-05_share_unshare_axes_after_creation.rst new file mode 100644 index 000000000000..74f88b1762b1 --- /dev/null +++ b/doc/users/next_whats_new/2017-12-05_share_unshare_axes_after_creation.rst @@ -0,0 +1,12 @@ +Share and unshare `axes` after creation +--------------------------------------- + +`matplotlib.axes.Axes` have `matplotlib.axes.Axes.unshare_x_axes`, +`matplotlib.axes.Axes.unshare_y_axes`, `matplotlib.axes.Axes.unshare_z_axes` +and `matplotlib.axes.Axes.unshare_axes` methods to unshare axes. +Similiar there are `matplotlib.axes.Axes.share_x_axes`, +`matplotlib.axes.Axes.share_y_axes`, `matplotlib.axes.Axes.share_z_axes` and +`matplotlib.axes.Axes.share_axes` methods to share axes. + +Unshare an axis will decouple the viewlimits for further changes. +Share an axis will couple the viewlimits. \ No newline at end of file diff --git a/examples/mplot3d/share_unshare_3d_axes.py b/examples/mplot3d/share_unshare_3d_axes.py new file mode 100644 index 000000000000..6568ae025292 --- /dev/null +++ b/examples/mplot3d/share_unshare_3d_axes.py @@ -0,0 +1,39 @@ +""" +============================================ +Parametric Curve with Share and Unshare Axes +============================================ + +This example demonstrates plotting a parametric curve in 3D, +and how to share and unshare 3D plot axes. +""" +import matplotlib as mpl +from mpl_toolkits.mplot3d import Axes3D +import numpy as np +import matplotlib.pyplot as plt + +mpl.rcParams['legend.fontsize'] = 10 + +# Prepare arrays x, y, z +theta = np.linspace(-4 * np.pi, 4 * np.pi, 100) +z = np.linspace(-2, 2, 100) +r = z ** 2 + 1 +x = r * np.sin(theta) +y = r * np.cos(theta) + +fig = plt.figure() +ax = fig.add_subplot(311, projection='3d') + +ax.plot(x, y, z, label='parametric curve') +ax.legend() + +ax1 = fig.add_subplot(312) +ax1.plot(range(10)) +ax1.share_axes(ax) + +ax2 = fig.add_subplot(313, projection='3d', sharex=ax) +ax2.plot(x, y, z) + +ax2.unshare_x_axes() +ax2.share_z_axes(ax) + +plt.show() diff --git a/examples/subplots_axes_and_figures/unshare_axis_demo.py b/examples/subplots_axes_and_figures/unshare_axis_demo.py new file mode 100644 index 000000000000..8baa48128fd7 --- /dev/null +++ b/examples/subplots_axes_and_figures/unshare_axis_demo.py @@ -0,0 +1,36 @@ +""" +====================== +Unshare and share axis +====================== + +The example shows how to share and unshare axes after they are created. +""" + +import matplotlib.pyplot as plt +import numpy as np + +t = np.arange(0.01, 5.0, 0.01) +s1 = np.sin(2 * np.pi * t) +s2 = np.exp(-t) +s3 = np.sin(4 * np.pi * t) + +ax1 = plt.subplot(311) +plt.plot(t, s1) + +ax2 = plt.subplot(312) +plt.plot(t, s2) + +ax3 = plt.subplot(313) +plt.plot(t, s3) + +ax1.share_x_axes(ax2) +ax1.share_y_axes(ax2) + +# Share both axes. +ax3.share_axes(ax1) +plt.xlim(0.01, 5.0) + +ax3.unshare_y_axes() +ax2.unshare_x_axes() + +plt.show() diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index c201c09b9189..95cfdd03533b 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -1,4 +1,6 @@ from collections import OrderedDict +from weakref import WeakSet +import copy import itertools import logging import math @@ -400,8 +402,6 @@ class _AxesBase(martist.Artist): """ name = "rectilinear" - _shared_x_axes = cbook.Grouper() - _shared_y_axes = cbook.Grouper() _twinned_axes = cbook.Grouper() def __str__(self): @@ -482,12 +482,18 @@ def __init__(self, fig, rect, self._aspect = 'auto' self._adjustable = 'box' self._anchor = 'C' + #Adding yourself to shared xy axes. Reflexive. + self._shared_x_axes = WeakSet([self]) + self._shared_y_axes = WeakSet([self]) self._sharex = sharex self._sharey = sharey + if sharex is not None: - self._shared_x_axes.join(self, sharex) + self.share_x_axes(sharex) + if sharey is not None: - self._shared_y_axes.join(self, sharey) + self.share_y_axes(sharey) + self.set_label(label) self.set_figure(fig) @@ -571,10 +577,14 @@ def __getstate__(self): state.pop('_layoutbox') state.pop('_poslayoutbox') + state['_shared_x_axes'] = list(self._shared_x_axes) + state['_shared_y_axes'] = list(self._shared_y_axes) return state def __setstate__(self, state): self.__dict__ = state + self._shared_x_axes = WeakSet(state['_shared_x_axes']) + self._shared_y_axes = WeakSet(state['_shared_y_axes']) self._stale = True self._layoutbox = None self._poslayoutbox = None @@ -1102,8 +1112,6 @@ def cla(self): self.xaxis.set_clip_path(self.patch) self.yaxis.set_clip_path(self.patch) - self._shared_x_axes.clean() - self._shared_y_axes.clean() if self._sharex: self.xaxis.set_visible(xaxis_visible) self.patch.set_visible(patch_visible) @@ -1280,8 +1288,7 @@ def set_aspect(self, aspect, adjustable=None, anchor=None, share=False): if not (isinstance(aspect, str) and aspect in ('equal', 'auto')): aspect = float(aspect) # raise ValueError if necessary if share: - axes = set(self._shared_x_axes.get_siblings(self) - + self._shared_y_axes.get_siblings(self)) + axes = set(self._shared_x_axes | self._shared_y_axes) else: axes = [self] for ax in axes: @@ -1335,8 +1342,7 @@ def set_adjustable(self, adjustable, share=False): if adjustable not in ('box', 'datalim', 'box-forced'): raise ValueError("argument must be 'box', or 'datalim'") if share: - axes = set(self._shared_x_axes.get_siblings(self) - + self._shared_y_axes.get_siblings(self)) + axes = set(self._shared_x_axes | self._shared_y_axes) else: axes = [self] for ax in axes: @@ -1403,8 +1409,7 @@ def set_anchor(self, anchor, share=False): raise ValueError('argument must be among %s' % ', '.join(mtransforms.Bbox.coefs)) if share: - axes = set(self._shared_x_axes.get_siblings(self) - + self._shared_y_axes.get_siblings(self)) + axes = set(self._shared_x_axes | self._shared_y_axes) else: axes = [self] for ax in axes: @@ -1556,8 +1561,8 @@ def apply_aspect(self, position=None): xm = 0 ym = 0 - shared_x = self in self._shared_x_axes - shared_y = self in self._shared_y_axes + shared_x = self.is_sharing_x_axes() + shared_y = self.is_sharing_y_axes() # Not sure whether we need this check: if shared_x and shared_y: raise RuntimeError("adjustable='datalim' is not allowed when both" @@ -2389,8 +2394,7 @@ def handle_single_axis(scale, autoscaleon, shared_axes, interval, if not (scale and autoscaleon): return # nothing to do... - shared = shared_axes.get_siblings(self) - dl = [ax.dataLim for ax in shared] + dl = [ax.dataLim for ax in shared_axes] # ignore non-finite data limits if good limits exist finite_dl = [d for d in dl if np.isfinite(d).all()] if len(finite_dl): @@ -3126,7 +3130,7 @@ def set_xlim(self, left=None, right=None, emit=True, auto=False, if emit: self.callbacks.process('xlim_changed', self) # Call all of the other x-axes that are shared with this one - for other in self._shared_x_axes.get_siblings(self): + for other in self._shared_x_axes: if other is not self: other.set_xlim(self.viewLim.intervalx, emit=False, auto=auto) @@ -3165,8 +3169,7 @@ def set_xscale(self, value, **kwargs): matplotlib.scale.LogisticTransform : logit transform """ - g = self.get_shared_x_axes() - for ax in g.get_siblings(self): + for ax in self._shared_x_axes: ax.xaxis._set_scale(value, **kwargs) ax._update_transScale() ax.stale = True @@ -3459,7 +3462,7 @@ def set_ylim(self, bottom=None, top=None, emit=True, auto=False, if emit: self.callbacks.process('ylim_changed', self) # Call all of the other y-axes that are shared with this one - for other in self._shared_y_axes.get_siblings(self): + for other in self._shared_y_axes: if other is not self: other.set_ylim(self.viewLim.intervaly, emit=False, auto=auto) @@ -3498,8 +3501,7 @@ def set_yscale(self, value, **kwargs): matplotlib.scale.LogisticTransform : logit transform """ - g = self.get_shared_y_axes() - for ax in g.get_siblings(self): + for ax in self._shared_y_axes: ax.yaxis._set_scale(value, **kwargs) ax._update_transScale() ax.stale = True @@ -4231,9 +4233,100 @@ def twiny(self): return ax2 def get_shared_x_axes(self): - """Return a reference to the shared axes Grouper object for x axes.""" - return self._shared_x_axes + """Return a copy of the shared axes Weakset object for x axes""" + return WeakSet(self._shared_x_axes) def get_shared_y_axes(self): - """Return a reference to the shared axes Grouper object for y axes.""" - return self._shared_y_axes + """Return a copy of the shared axes Weakset object for y axes""" + return WeakSet(self._shared_y_axes) + + def is_sharing_x_axes(self): + return len(self.get_shared_x_axes()) > 1 + + def is_sharing_y_axes(self): + return len(self.get_shared_y_axes()) > 1 + + def _unshare_axes(self, shared_axes): + for ax in getattr(self, "_shared_{}_axes".format(shared_axes)): + parent = getattr(ax, "_share{}".format(shared_axes)) + + if parent is self: + setattr(ax, "_share{}".format(shared_axes), None) + self._copy_axis_major_minor( + getattr(ax, "{}axis".format(shared_axes))) + + getattr(self, "_shared_{}_axes".format(shared_axes)).remove(self) + setattr(self, "_shared_{}_axes".format(shared_axes), WeakSet([self])) + setattr(self, "_share{}".format(shared_axes), None) + + self._copy_axis_major_minor( + getattr(self, "{}axis".format(shared_axes))) + + @staticmethod + def _copy_axis_major_minor(axis): + major = axis.major + minor = axis.minor + + axis.major = copy.deepcopy(major) + axis.minor = copy.deepcopy(minor) + + axis.major.set_axis(axis) + axis.minor.set_axis(axis) + + def unshare_x_axes(self): + """ Unshare x axis. """ + self._unshare_axes("x") + + def unshare_y_axes(self): + """ Unshare y axis. """ + self._unshare_axes("y") + + def unshare_axes(self): + """ Unshare both x and y axes. """ + self.unshare_x_axes() + self.unshare_y_axes() + + def _share_axes(self, axes, shared_axes): + if not iterable(axes): + axes = [axes] + + shared = getattr(self, "_shared_{}_axes".format(shared_axes)) + for ax in axes: + shared |= getattr(ax, "_shared_{}_axes".format(shared_axes)) + + for ax in shared: + setattr(ax, "_shared_{}_axes".format(shared_axes), shared) + + def share_x_axes(self, axes): + """ + Share x axis. + + Parameters + ---------- + axes: Axes + Axes to share. + """ + self._share_axes(axes, 'x') + + def share_y_axes(self, axes): + """ + Share y axis. + + Parameters + ---------- + axes: Axes + Axes to share. + """ + self._share_axes(axes, 'y') + + def share_axes(self, axes): + """ + Share both x and y axes. + + Parameters + ---------- + axes: Axes + Axes to share. + """ + self.share_x_axes(axes) + self.share_y_axes(axes) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 8ca2209b3a0f..83ad56b50626 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -7,6 +7,7 @@ import warnings import numpy as np +import copy from matplotlib import rcParams import matplotlib.artist as artist @@ -651,6 +652,31 @@ class Ticker(object): locator = None formatter = None + def __deepcopy__(self, memodict={}): + """ + Perform deep copy by disable axis attribute from + locator and formatter. + + """ + axisLocator = self.locator.axis + axisFormatter = self.formatter.axis + self.locator.axis = None + self.formatter.axis = None + + cls = self.__class__ + result = cls.__new__(cls) + result.locator = copy.deepcopy(self.locator) + result.formatter = copy.deepcopy(self.formatter) + + self.locator.axis = axisLocator + self.formatter.axis = axisFormatter + + return result + + def set_axis(self, axis): + self.locator.axis = axis + self.formatter.axis = axis + class _LazyTickList(object): """ diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 54728fe05d6b..ffa98ed4e147 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -2922,9 +2922,9 @@ def release_zoom(self, event): twinx, twiny = False, False if last_a: for la in last_a: - if a.get_shared_x_axes().joined(a, la): + if la in a.get_shared_x_axes(): twinx = True - if a.get_shared_y_axes().joined(a, la): + if la in a.get_shared_y_axes(): twiny = True last_a.append(a) diff --git a/lib/matplotlib/backend_tools.py b/lib/matplotlib/backend_tools.py index 803ce1c02c26..d8b29d435de6 100644 --- a/lib/matplotlib/backend_tools.py +++ b/lib/matplotlib/backend_tools.py @@ -934,9 +934,9 @@ def _release(self, event): twinx, twiny = False, False if last_a: for la in last_a: - if a.get_shared_x_axes().joined(a, la): + if la in a.get_shared_x_axes(): twinx = True - if a.get_shared_y_axes().joined(a, la): + if la in a.get_shared_y_axes(): twiny = True last_a.append(a) diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 81ecfe9781bd..514648d82385 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -1395,21 +1395,17 @@ def _reset_loc_form(axis): axis.set_minor_formatter(axis.get_minor_formatter()) axis.set_minor_locator(axis.get_minor_locator()) - def _break_share_link(ax, grouper): - siblings = grouper.get_siblings(ax) - if len(siblings) > 1: - grouper.remove(ax) - for last_ax in siblings: - if ax is not last_ax: - return last_ax - return None - self.delaxes(ax) - last_ax = _break_share_link(ax, ax._shared_y_axes) + + shared_y_axes = ax._shared_y_axes + shared_x_axes = ax._shared_x_axes + ax.unshare_axes() + + last_ax = next((a for a in shared_y_axes if a is not ax), None) if last_ax is not None: _reset_loc_form(last_ax.yaxis) - last_ax = _break_share_link(ax, ax._shared_x_axes) + last_ax = next((a for a in shared_x_axes if a is not ax), None) if last_ax is not None: _reset_loc_form(last_ax.xaxis) diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index fda0fe0f362b..50dd78f28f86 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -5719,3 +5719,115 @@ def test_tick_padding_tightbbox(): bb2 = ax.get_window_extent(fig.canvas.get_renderer()) assert bb.x0 < bb2.x0 assert bb.y0 < bb2.y0 + + +def test_share_unshare_axes(): + fig = plt.figure() + ax1 = fig.add_subplot(211) + ax2 = fig.add_subplot(212, sharex=ax1, sharey=ax1) + + # Testing unsharing + ax1.unshare_axes() + assert ax2._sharex is None + assert ax2._sharey is None + + sharedx = ax2._shared_x_axes + sharedy = ax2._shared_y_axes + + assert ax1 not in sharedx + assert ax1 not in sharedy + + sharedx = ax1._shared_x_axes + sharedy = ax1._shared_y_axes + + assert ax2 not in sharedx + assert ax2 not in sharedy + + assert not ax1.is_sharing_x_axes() + assert not ax1.is_sharing_y_axes() + + assert not ax2.is_sharing_x_axes() + assert not ax2.is_sharing_y_axes() + + # Testing sharing + ax1.share_x_axes(ax2) + + sharedx = ax1._shared_x_axes + assert ax2 in sharedx + + sharedx = ax2._shared_x_axes + assert ax1 in sharedx + + ax2.share_y_axes(ax1) + + sharedy = ax1._shared_y_axes + assert ax2 in sharedy + + sharedy = ax2._shared_y_axes + assert ax1 in sharedy + + assert ax1.is_sharing_x_axes() + assert ax1.is_sharing_y_axes() + + assert ax2.is_sharing_x_axes() + assert ax2.is_sharing_y_axes() + + +def test_share_unshare_3d_axes(): + fig = plt.figure() + ax1 = fig.add_subplot(211, projection="3d") + ax2 = fig.add_subplot(212, projection="3d", + sharex=ax1, + sharey=ax1, + sharez=ax1) + + # Testing unsharing + ax1.unshare_axes() + assert ax2._sharex is None + assert ax2._sharey is None + assert ax2._sharez is None + + sharedx = ax2._shared_x_axes + sharedy = ax2._shared_y_axes + sharedz = ax2._shared_z_axes + + assert ax1 not in sharedx + assert ax1 not in sharedy + assert ax1 not in sharedz + + sharedx = ax1._shared_x_axes + sharedy = ax1._shared_y_axes + sharedz = ax1._shared_z_axes + + assert ax2 not in sharedx + assert ax2 not in sharedy + assert ax2 not in sharedz + + # Testing sharing + + # x axis + ax1.share_x_axes(ax2) + + sharedx = ax1._shared_x_axes + assert ax2 in sharedx + + sharedx = ax2._shared_x_axes + assert ax1 in sharedx + + # y axis + ax2.share_y_axes(ax1) + + sharedy = ax1._shared_y_axes + assert ax2 in sharedy + + sharedy = ax2._shared_y_axes + assert ax1 in sharedy + + # z axis + ax1.share_z_axes(ax2) + + sharedz = ax2._shared_z_axes + assert ax1 in sharedz + + sharedz = ax1._shared_z_axes + assert ax2 in sharedz diff --git a/lib/matplotlib/tests/test_pickle.py b/lib/matplotlib/tests/test_pickle.py index 719dc7356fc8..66d461e7d6f1 100644 --- a/lib/matplotlib/tests/test_pickle.py +++ b/lib/matplotlib/tests/test_pickle.py @@ -147,6 +147,45 @@ def test_polar(): plt.draw() +def test_sharing_axes(): + fig = plt.figure() + ax1 = fig.add_subplot(211) + ax2 = fig.add_subplot(212, sharex=ax1, sharey=ax1) + + pf = pickle.dumps(fig) + fig1 = pickle.loads(pf) + + ax = fig1.axes + + assert ax[0] in ax[1]._shared_x_axes + assert ax[0] in ax[1]._shared_y_axes + + assert ax[1] in ax[0]._shared_x_axes + assert ax[1] in ax[0]._shared_y_axes + + +def test_sharing_3d_axes(): + fig = plt.figure() + ax1 = fig.add_subplot(211, projection="3d") + ax2 = fig.add_subplot(212, projection="3d", + sharex=ax1, + sharey=ax1, + sharez=ax1) + + pf = pickle.dumps(fig) + fig1 = pickle.loads(pf) + + ax = fig1.axes + + assert ax[0] in ax[1]._shared_x_axes + assert ax[0] in ax[1]._shared_y_axes + assert ax[0] in ax[1]._shared_z_axes + + assert ax[1] in ax[0]._shared_x_axes + assert ax[1] in ax[0]._shared_y_axes + assert ax[1] in ax[0]._shared_z_axes + + class TransformBlob(object): def __init__(self): self.identity = mtransforms.IdentityTransform() diff --git a/lib/matplotlib/tests/test_subplots.py b/lib/matplotlib/tests/test_subplots.py index 9a1a5e7f7a8d..76ef9ecca089 100644 --- a/lib/matplotlib/tests/test_subplots.py +++ b/lib/matplotlib/tests/test_subplots.py @@ -19,8 +19,10 @@ def check_shared(axs, x_shared, y_shared): enumerate(zip("xy", [x_shared, y_shared]))): if i2 <= i1: continue + share = getattr(ax1, "_shared_{}_axes".format(name)) + share = ax2 in share assert \ - (getattr(axs[0], "_shared_{}_axes".format(name)).joined(ax1, ax2) + (share == shared[i1, i2]), \ "axes %i and %i incorrectly %ssharing %s axis" % ( i1, i2, "not " if shared[i1, i2] else "", name) diff --git a/lib/mpl_toolkits/mplot3d/axes3d.py b/lib/mpl_toolkits/mplot3d/axes3d.py index 16bb0ebec65e..937300620d1d 100644 --- a/lib/mpl_toolkits/mplot3d/axes3d.py +++ b/lib/mpl_toolkits/mplot3d/axes3d.py @@ -13,6 +13,8 @@ from collections import defaultdict import math import warnings +import copy +from weakref import WeakSet import numpy as np @@ -44,7 +46,6 @@ class Axes3D(Axes): 3D axes object. """ name = '3d' - _shared_z_axes = cbook.Grouper() def __init__( self, fig, rect=None, *args, @@ -90,10 +91,11 @@ def __init__( self.view_init(self.initial_elev, self.initial_azim) self._ready = 0 + self._shared_z_axes = WeakSet([self]) self._sharez = sharez + if sharez is not None: - self._shared_z_axes.join(self, sharez) - self._adjustable = 'datalim' + self.share_z_axes(sharez) super().__init__(fig, rect, frameon=True, *args, **kwargs) # Disable drawing of axes by base class @@ -125,6 +127,17 @@ def __init__( self.figure.add_axes(self) + def __getstate__(self): + state = super(Axes3D, self).__getstate__() + state['_shared_z_axes'] = list(self._shared_z_axes) + + return state + + def __setstate__(self, state): + self.__dict__ = state + + self._shared_z_axes = WeakSet(state['_shared_z_axes']) + def set_axis_off(self): self._axis3don = False self.stale = True @@ -528,7 +541,6 @@ def autoscale_view(self, tight=None, scalex=True, scaley=True, _tight = self._tight = bool(tight) if scalex and self._autoscaleXon: - self._shared_x_axes.clean() x0, x1 = self.xy_dataLim.intervalx xlocator = self.xaxis.get_major_locator() try: @@ -545,7 +557,6 @@ def autoscale_view(self, tight=None, scalex=True, scaley=True, self.set_xbound(x0, x1) if scaley and self._autoscaleYon: - self._shared_y_axes.clean() y0, y1 = self.xy_dataLim.intervaly ylocator = self.yaxis.get_major_locator() try: @@ -562,7 +573,6 @@ def autoscale_view(self, tight=None, scalex=True, scaley=True, self.set_ybound(y0, y1) if scalez and self._autoscaleZon: - self._shared_z_axes.clean() z0, z1 = self.zz_dataLim.intervalx zlocator = self.zaxis.get_major_locator() try: @@ -640,7 +650,7 @@ def set_xlim3d(self, left=None, right=None, emit=True, auto=False, if emit: self.callbacks.process('xlim_changed', self) # Call all of the other x-axes that are shared with this one - for other in self._shared_x_axes.get_siblings(self): + for other in self._shared_x_axes: if other is not self: other.set_xlim(self.xy_viewLim.intervalx, emit=False, auto=auto) @@ -698,7 +708,7 @@ def set_ylim3d(self, bottom=None, top=None, emit=True, auto=False, if emit: self.callbacks.process('ylim_changed', self) # Call all of the other y-axes that are shared with this one - for other in self._shared_y_axes.get_siblings(self): + for other in self._shared_y_axes: if other is not self: other.set_ylim(self.xy_viewLim.intervaly, emit=False, auto=auto) @@ -756,7 +766,7 @@ def set_zlim3d(self, bottom=None, top=None, emit=True, auto=False, if emit: self.callbacks.process('zlim_changed', self) # Call all of the other y-axes that are shared with this one - for other in self._shared_z_axes.get_siblings(self): + for other in self._shared_z_axes: if other is not self: other.set_zlim(self.zz_viewLim.intervalx, emit=False, auto=auto) @@ -1105,12 +1115,14 @@ def cla(self): super().cla() self.zaxis.cla() - if self._sharez is not None: - self.zaxis.major = self._sharez.zaxis.major - self.zaxis.minor = self._sharez.zaxis.minor - z0, z1 = self._sharez.get_zlim() + sharez = self._sharez + + if sharez is not None: + self.zaxis.major = sharez.zaxis.major + self.zaxis.minor = sharez.zaxis.minor + z0, z1 = sharez.get_zlim() self.set_zlim(z0, z1, emit=False, auto=None) - self.zaxis._set_scale(self._sharez.zaxis.get_scale()) + self.zaxis._set_scale(sharez.zaxis.get_scale()) else: self.zaxis._set_scale('linear') try: @@ -2885,6 +2897,40 @@ def permutation_matrices(n): return polygons + def unshare_z_axes(self): + """ Unshare z axis. """ + self._unshare_axes("z") + + def unshare_axes(self): + """ Unshare x, y and z axes. """ + self.unshare_x_axes() + self.unshare_y_axes() + self.unshare_z_axes() + + def share_z_axes(self, axes): + """ + Share z axis. + + Parameters + ---------- + axes: Axes + Axes to share. + """ + self._share_axes(axes, 'z') + + def share_axes(self, axes): + """ + Share x, y, z axes. + + Parameters + ---------- + axes: Axes + Axes to share. + """ + self.share_x_axes(axes) + self.share_y_axes(axes) + self.share_z_axes(axes) + def get_test_data(delta=0.05): ''' pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy