From 0bb718217a16d319b6639b4dde9830abeb108bc8 Mon Sep 17 00:00:00 2001 From: syrte Date: Wed, 3 Aug 2016 18:06:50 +0800 Subject: [PATCH 1/4] Fix inconsistency between `set_xlim` and `set_view_interval` Fix bug: `Axes.xaxis.set_view_interval` , `Axes.xaxis.set_view_interval` do not update twin axis as `set_xlim` do. Add function: `Axes._set_viewLim_intervalx`, `Axes._set_viewLim_intervaly` --- lib/matplotlib/axes/_base.py | 29 +++++++++++++++++++++++++---- lib/matplotlib/axis.py | 23 ++++++++++++----------- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 81cd11d4284d..9b455aa5e6f1 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -2853,13 +2853,25 @@ def set_xlim(self, left=None, right=None, emit=True, auto=False, **kw): ('Attempting to set identical left==right results\n' 'in singular transformations; automatically expanding.\n' 'left=%s, right=%s') % (left, right)) + left, right = mtransforms.nonsingular(left, right, increasing=False) left, right = self.xaxis.limit_range_for_scale(left, right) - self.viewLim.intervalx = (left, right) if auto is not None: self._autoscaleXon = bool(auto) + self._set_viewLim_intervalx(left, right, emit=emit) + return left, right + + def _set_viewLim_intervalx(self, left, right, emit=True): + """ + Set the data limits for the xaxis + This should be the only method who can change viewLim.intervalx directly. + + *emit*: [ *True* | *False* ] + Notify observers of limit change + """ + self.viewLim.intervalx = (left, right) if emit: self.callbacks.process('xlim_changed', self) # Call all of the other x-axes that are shared with this one @@ -2871,7 +2883,6 @@ def set_xlim(self, left=None, right=None, emit=True, auto=False, **kw): other.figure.canvas is not None): other.figure.canvas.draw_idle() self.stale = True - return left, right def get_xscale(self): return self.xaxis.get_scale() @@ -3115,10 +3126,21 @@ def set_ylim(self, bottom=None, top=None, emit=True, auto=False, **kw): bottom, top = mtransforms.nonsingular(bottom, top, increasing=False) bottom, top = self.yaxis.limit_range_for_scale(bottom, top) - self.viewLim.intervaly = (bottom, top) if auto is not None: self._autoscaleYon = bool(auto) + self._set_viewLim_intervaly(bottom, top, emit=emit) + return bottom, top + + def _set_viewLim_intervaly(self, bottom, top, emit=True): + """ + Set the data limits for the yaxis + This should be the only method who can change viewLim.intervaly directly. + + *emit*: [ *True* | *False* ] + Notify observers of limit change + """ + self.viewLim.intervaly = (bottom, top) if emit: self.callbacks.process('ylim_changed', self) # Call all of the other y-axes that are shared with this one @@ -3130,7 +3152,6 @@ def set_ylim(self, bottom=None, top=None, emit=True, auto=False, **kw): other.figure.canvas is not None): other.figure.canvas.draw_idle() self.stale = True - return bottom, top def get_yscale(self): return self.yaxis.get_scale() diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 276c09605a70..c13ec0eb3683 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -1960,15 +1960,16 @@ def set_view_interval(self, vmin, vmax, ignore=False): """ if ignore: - self.axes.viewLim.intervalx = vmin, vmax + left, right = vmin, vmax else: Vmin, Vmax = self.get_view_interval() if Vmin < Vmax: - self.axes.viewLim.intervalx = (min(vmin, vmax, Vmin), - max(vmin, vmax, Vmax)) + left, right = (min(vmin, vmax, Vmin), + max(vmin, vmax, Vmax)) else: - self.axes.viewLim.intervalx = (max(vmin, vmax, Vmin), - min(vmin, vmax, Vmax)) + left, right = (max(vmin, vmax, Vmin), + min(vmin, vmax, Vmax)) + self.axes._set_viewLim_intervalx(left, right) def get_minpos(self): return self.axes.dataLim.minposx @@ -2299,16 +2300,16 @@ def set_view_interval(self, vmin, vmax, ignore=False): """ if ignore: - self.axes.viewLim.intervaly = vmin, vmax + bottom, top = vmin, vmax else: Vmin, Vmax = self.get_view_interval() if Vmin < Vmax: - self.axes.viewLim.intervaly = (min(vmin, vmax, Vmin), - max(vmin, vmax, Vmax)) + bottom, top = (min(vmin, vmax, Vmin), + max(vmin, vmax, Vmax)) else: - self.axes.viewLim.intervaly = (max(vmin, vmax, Vmin), - min(vmin, vmax, Vmax)) - self.stale = True + bottom, top = (max(vmin, vmax, Vmin), + min(vmin, vmax, Vmax)) + self.axes._set_viewLim_intervaly(bottom, top) def get_minpos(self): return self.axes.dataLim.minposy From d1459adee6a6f7192e3e6a071c850a527fad3b71 Mon Sep 17 00:00:00 2001 From: syrte Date: Wed, 3 Aug 2016 20:07:02 +0800 Subject: [PATCH 2/4] fix: add `auto` to `_set_viewLim_intervalx` Fix error in previous commit. --- lib/matplotlib/axes/_base.py | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 9b455aa5e6f1..f14b79028cb0 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -2857,21 +2857,26 @@ def set_xlim(self, left=None, right=None, emit=True, auto=False, **kw): left, right = mtransforms.nonsingular(left, right, increasing=False) left, right = self.xaxis.limit_range_for_scale(left, right) - if auto is not None: - self._autoscaleXon = bool(auto) - - self._set_viewLim_intervalx(left, right, emit=emit) + self._set_viewLim_intervalx(left, right, emit=emit, auto=auto) return left, right - def _set_viewLim_intervalx(self, left, right, emit=True): + def _set_viewLim_intervalx(self, left, right, emit=True, auto=None): """ Set the data limits for the xaxis This should be the only method who can change viewLim.intervalx directly. - *emit*: [ *True* | *False* ] + Keyword arguments: + *emit*: [ *True* | *False* ] Notify observers of limit change + + *auto*: [ *True* | *False* | *None* ] + Turn *x* autoscaling on (*True*), off (*False*; default), + or leave unchanged (*None*) """ self.viewLim.intervalx = (left, right) + if auto is not None: + self._autoscaleXon = bool(auto) + if emit: self.callbacks.process('xlim_changed', self) # Call all of the other x-axes that are shared with this one @@ -3126,21 +3131,26 @@ def set_ylim(self, bottom=None, top=None, emit=True, auto=False, **kw): bottom, top = mtransforms.nonsingular(bottom, top, increasing=False) bottom, top = self.yaxis.limit_range_for_scale(bottom, top) - if auto is not None: - self._autoscaleYon = bool(auto) - - self._set_viewLim_intervaly(bottom, top, emit=emit) + self._set_viewLim_intervaly(bottom, top, emit=emit, auto=auto) return bottom, top - def _set_viewLim_intervaly(self, bottom, top, emit=True): + def _set_viewLim_intervaly(self, bottom, top, emit=True, auto=None): """ Set the data limits for the yaxis This should be the only method who can change viewLim.intervaly directly. - *emit*: [ *True* | *False* ] + Keyword arguments: + *emit*: [ *True* | *False* ] Notify observers of limit change + + *auto*: [ *True* | *False* | *None* ] + Turn *x* autoscaling on (*True*), off (*False*; default), + or leave unchanged (*None*) """ self.viewLim.intervaly = (bottom, top) + if auto is not None: + self._autoscaleYon = bool(auto) + if emit: self.callbacks.process('ylim_changed', self) # Call all of the other y-axes that are shared with this one From 6719c5d7170e95793963e25af3b8479a1a680b10 Mon Sep 17 00:00:00 2001 From: syrte Date: Wed, 3 Aug 2016 21:03:50 +0800 Subject: [PATCH 3/4] Fix line too long --- lib/matplotlib/axes/_base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index f14b79028cb0..fc35bdc5fa19 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -2863,7 +2863,7 @@ def set_xlim(self, left=None, right=None, emit=True, auto=False, **kw): def _set_viewLim_intervalx(self, left, right, emit=True, auto=None): """ Set the data limits for the xaxis - This should be the only method who can change viewLim.intervalx directly. + This should be the only method to change viewLim.intervalx directly. Keyword arguments: *emit*: [ *True* | *False* ] @@ -3137,7 +3137,7 @@ def set_ylim(self, bottom=None, top=None, emit=True, auto=False, **kw): def _set_viewLim_intervaly(self, bottom, top, emit=True, auto=None): """ Set the data limits for the yaxis - This should be the only method who can change viewLim.intervaly directly. + This should be the only method to change viewLim.intervaly directly. Keyword arguments: *emit*: [ *True* | *False* ] From 8fdbc8f337ce6d7849ec53d2c14843e6d0c92446 Mon Sep 17 00:00:00 2001 From: syrte Date: Wed, 3 Aug 2016 21:18:00 +0800 Subject: [PATCH 4/4] Add test --- lib/matplotlib/tests/test_axes.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 650b6078631a..fc5596496d25 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -122,6 +122,19 @@ def test_twin_axis_locaters_formatters(): ax3 = ax1.twinx() +@cleanup +def test_twinx_view_interval(): + fig, ax = plt.subplots() + ax.set_xlim(0, 1) + ax.set_ylim(0, 1) + ax2 = ax.twinx() + ax3 = ax.twiny() + ax2.set_xticks([1, 2]) + ax3.set_yticks([1, 2]) + assert ax.get_xlim() == ax2.get_xlim() + assert ax.get_ylim() == ax3.get_ylim() + + @cleanup def test_twinx_cla(): fig, ax = plt.subplots()