From 7145eb3100117bd599e407c90254566c81d2d866 Mon Sep 17 00:00:00 2001 From: Julien Lhermitte Date: Fri, 16 Oct 2015 00:24:44 -0400 Subject: [PATCH 1/6] moved scroll wheel zooming to the axes, added history to scroll wheel zooming. Fixed zooming to stay fixed on cursor. removed extraneous print(view) statement. --- lib/matplotlib/axes/_base.py | 45 ++++++++++++++++++++++++++++++--- lib/matplotlib/backend_tools.py | 41 ++++++++++++++++-------------- 2 files changed, 63 insertions(+), 23 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index b3b98c04607f..8e1819496b70 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -3322,8 +3322,12 @@ def _set_view_from_bbox(self, bbox, direction='in', Parameters ---------- - bbox : tuple - The selected bounding box limits, in *display* coordinates. + bbox : 4-tuple or 2 tuple + * If result is a 4 tuple, it is the selected bounding box limits, + in *display* coordinates. + * If result is a 3 tuple, it is an (xp, yp, scl) triple, where + (xp,yp) are the center of zoom and scl the scale factor to zoom + by direction : str The direction to apply the bounding box. @@ -3342,15 +3346,48 @@ def _set_view_from_bbox(self, bbox, direction='in', twiny : bool Whether this axis is twinned in the *y*-direction. """ + Xmin, Xmax = self.get_xlim() + Ymin, Ymax = self.get_ylim() + + if(len(bbox) == 3): + # Zooming code + xp, yp, scl = bbox + print("in set view, for zooming with scale {}".format(scl)) + + # Should not happen + if(scl == 0): + scl = 1. + # direction = 'in' + if(scl > 1): + direction = 'in' + else: + direction = 'out' + scl = 1/scl + + # get the limits of the axes + tranD2C = self.transData.transform + xmin, ymin = tranD2C((Xmin, Ymin)) + xmax, ymax = tranD2C((Xmax, Ymax)) + + # set the range + xwidth = xmax - xmin + ywidth = ymax - ymin + xcen = (xmax + xmin)*.5 + ycen = (ymax + ymin)*.5 + xzc = (xp*(scl - 1) + xcen)/scl + yzc = (yp*(scl - 1) + ycen)/scl + + bbox = [xzc - xwidth/2./scl, yzc - ywidth/2./scl, xzc + + xwidth/2./scl, yzc + ywidth/2./scl] + + # Just grab bounding box lastx, lasty, x, y = bbox # zoom to rect inverse = self.transData.inverted() lastx, lasty = inverse.transform_point((lastx, lasty)) x, y = inverse.transform_point((x, y)) - Xmin, Xmax = self.get_xlim() - Ymin, Ymax = self.get_ylim() if twinx: x0, x1 = Xmin, Xmax diff --git a/lib/matplotlib/backend_tools.py b/lib/matplotlib/backend_tools.py index e8359eb6da8f..2fe5001f776e 100644 --- a/lib/matplotlib/backend_tools.py +++ b/lib/matplotlib/backend_tools.py @@ -16,8 +16,8 @@ from matplotlib._pylab_helpers import Gcf import matplotlib.cbook as cbook from weakref import WeakKeyDictionary -import numpy as np from matplotlib.externals import six +import time import warnings @@ -74,8 +74,8 @@ class ToolBase(object): def __init__(self, toolmanager, name): warnings.warn('Treat the new Tool classes introduced in v1.5 as ' + - 'experimental for now, the API will likely change in ' + - 'version 2.1, and some tools might change name') + 'experimental for now, the API will likely change in ' + + 'version 2.1, and some tools might change name') self._name = name self._figure = None self.toolmanager = toolmanager @@ -599,6 +599,8 @@ def __init__(self, *args): self._idRelease = None self._idScroll = None self.base_scale = 2. + self.scrollthresh = .5 # .5 second scroll threshold + self.lastscroll = time.time()-self.scrollthresh def enable(self, event): """Connect press/release events and lock the canvas""" @@ -626,30 +628,31 @@ def scroll_zoom(self, event): # https://gist.github.com/tacaswell/3144287 if event.inaxes is None: return - ax = event.inaxes - cur_xlim = ax.get_xlim() - cur_ylim = ax.get_ylim() - # set the range - cur_xrange = (cur_xlim[1] - cur_xlim[0])*.5 - cur_yrange = (cur_ylim[1] - cur_ylim[0])*.5 - xdata = event.xdata # get event x location - ydata = event.ydata # get event y location + if event.button == 'up': # deal with zoom in - scale_factor = 1 / self.base_scale + scl = self.base_scale elif event.button == 'down': # deal with zoom out - scale_factor = self.base_scale + scl = 1/self.base_scale else: # deal with something that should never happen - scale_factor = 1 - # set new limits - ax.set_xlim([xdata - cur_xrange*scale_factor, - xdata + cur_xrange*scale_factor]) - ax.set_ylim([ydata - cur_yrange*scale_factor, - ydata + cur_yrange*scale_factor]) + scl = 1 + + ax = event.inaxes + ax._set_view_from_bbox([event.x, event.y, scl]) + + # first check if last scroll was done within + # the timing threshold. If yes, delete previous view + if(time.time()-self.lastscroll < self.scrollthresh): + # remove the last view and push the current + self.toolmanager.get_tool(_views_positions).back() + self.figure.canvas.draw_idle() # force re-draw + self.lastscroll = time.time() + self.toolmanager.get_tool(_views_positions).push_current() + class ToolZoom(ZoomPanBase): """Zoom to rectangle""" From 9ed8c7d70c7e4dc015a2d79decfc87e0de007ef7 Mon Sep 17 00:00:00 2001 From: Julien Lhermitte Date: Sat, 17 Oct 2015 13:58:46 -0400 Subject: [PATCH 2/6] document typo fix and added warning for set_view_from_bbox --- lib/matplotlib/axes/_base.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 8e1819496b70..cfed243777a8 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -3322,7 +3322,7 @@ def _set_view_from_bbox(self, bbox, direction='in', Parameters ---------- - bbox : 4-tuple or 2 tuple + bbox : 4-tuple or 3 tuple * If result is a 4 tuple, it is the selected bounding box limits, in *display* coordinates. * If result is a 3 tuple, it is an (xp, yp, scl) triple, where @@ -3352,7 +3352,6 @@ def _set_view_from_bbox(self, bbox, direction='in', if(len(bbox) == 3): # Zooming code xp, yp, scl = bbox - print("in set view, for zooming with scale {}".format(scl)) # Should not happen if(scl == 0): @@ -3380,6 +3379,11 @@ def _set_view_from_bbox(self, bbox, direction='in', bbox = [xzc - xwidth/2./scl, yzc - ywidth/2./scl, xzc + xwidth/2./scl, yzc + ywidth/2./scl] + elif(len(bbox) != 4): + # should be len 3 or 4 but nothing else + print("Warning in _set_view_from_bbox: bounding box is not a + tuple of length 3 or\ 4. Ignoring the view change...\n"); + return # Just grab bounding box lastx, lasty, x, y = bbox From 485ab2e5313aaef3730101c2036faad0ab1f85c2 Mon Sep 17 00:00:00 2001 From: Julien Lhermitte Date: Sat, 17 Oct 2015 14:14:50 -0400 Subject: [PATCH 3/6] fixed a syntax error. also some PEP8 fixes (spaces, indents) --- lib/matplotlib/axes/_base.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index cfed243777a8..1c59ed4d188e 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -487,14 +487,14 @@ def __init__(self, fig, rect, self._shared_x_axes.join(self, sharex) if sharex._adjustable == 'box': sharex._adjustable = 'datalim' - #warnings.warn( + # 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( + # warnings.warn( # 'shared axes: "adjustable" is being changed to "datalim"') self._adjustable = 'datalim' self.set_label(label) @@ -1003,11 +1003,11 @@ def cla(self): self.grid(False) # Disable grid on init to use rcParameter self.grid(self._gridOn, which=rcParams['axes.grid.which'], - axis=rcParams['axes.grid.axis']) + axis=rcParams['axes.grid.axis']) props = font_manager.FontProperties( - size=rcParams['axes.titlesize'], - weight=rcParams['axes.titleweight'] - ) + size=rcParams['axes.titlesize'], + weight=rcParams['axes.titleweight'] + ) self.titleOffsetTrans = mtransforms.ScaledTranslation( 0.0, 5.0 / 72.0, self.figure.dpi_scale_trans) @@ -1122,7 +1122,7 @@ def set_color_cycle(self, clist): .. deprecated:: 1.5 """ cbook.warn_deprecated( - '1.5', name='set_color_cycle', alternative='set_prop_cycle') + '1.5', name='set_color_cycle', alternative='set_prop_cycle') self.set_prop_cycle('color', clist) def ishold(self): @@ -3381,8 +3381,8 @@ def _set_view_from_bbox(self, bbox, direction='in', xwidth/2./scl, yzc + ywidth/2./scl] elif(len(bbox) != 4): # should be len 3 or 4 but nothing else - print("Warning in _set_view_from_bbox: bounding box is not a - tuple of length 3 or\ 4. Ignoring the view change...\n"); + print("Warning in _set_view_from_bbox: bounding box is not a\ + tuple of length 3 or\ 4. Ignoring the view change...\n") return # Just grab bounding box From abf11f78fdbb0886008dfcdd46c52ab9af0c1eb3 Mon Sep 17 00:00:00 2001 From: Julien Lhermitte Date: Sun, 18 Oct 2015 22:55:03 -0400 Subject: [PATCH 4/6] fix documentation typo, change result to bbox --- 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 1c59ed4d188e..e7d053890b2a 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -3323,9 +3323,9 @@ def _set_view_from_bbox(self, bbox, direction='in', ---------- bbox : 4-tuple or 3 tuple - * If result is a 4 tuple, it is the selected bounding box limits, + * If bbox is a 4 tuple, it is the selected bounding box limits, in *display* coordinates. - * If result is a 3 tuple, it is an (xp, yp, scl) triple, where + * If bbox is a 3 tuple, it is an (xp, yp, scl) triple, where (xp,yp) are the center of zoom and scl the scale factor to zoom by From e866b7490d4d367295bed1f24e08a013a81f8919 Mon Sep 17 00:00:00 2001 From: Julien Lhermitte Date: Sun, 18 Oct 2015 23:07:03 -0400 Subject: [PATCH 5/6] grammatical fixes, cleanup code style --- lib/matplotlib/axes/_base.py | 20 ++++++++++---------- lib/matplotlib/backend_tools.py | 7 +++---- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index e7d053890b2a..810ebdf96a72 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -3326,8 +3326,8 @@ def _set_view_from_bbox(self, bbox, direction='in', * If bbox is a 4 tuple, it is the selected bounding box limits, in *display* coordinates. * If bbox is a 3 tuple, it is an (xp, yp, scl) triple, where - (xp,yp) are the center of zoom and scl the scale factor to zoom - by + (xp,yp) is the center of zooming and scl the scale factor to + zoom by. direction : str The direction to apply the bounding box. @@ -3349,16 +3349,16 @@ def _set_view_from_bbox(self, bbox, direction='in', Xmin, Xmax = self.get_xlim() Ymin, Ymax = self.get_ylim() - if(len(bbox) == 3): + if len(bbox) == 3: # Zooming code xp, yp, scl = bbox # Should not happen - if(scl == 0): + if scl == 0: scl = 1. # direction = 'in' - if(scl > 1): + if scl > 1: direction = 'in' else: direction = 'out' @@ -3377,12 +3377,12 @@ def _set_view_from_bbox(self, bbox, direction='in', xzc = (xp*(scl - 1) + xcen)/scl yzc = (yp*(scl - 1) + ycen)/scl - bbox = [xzc - xwidth/2./scl, yzc - ywidth/2./scl, xzc + - xwidth/2./scl, yzc + ywidth/2./scl] - elif(len(bbox) != 4): + bbox = [xzc - xwidth/2./scl, yzc - ywidth/2./scl, + xzc + xwidth/2./scl, yzc + ywidth/2./scl] + elif len(bbox) != 4: # should be len 3 or 4 but nothing else - print("Warning in _set_view_from_bbox: bounding box is not a\ - tuple of length 3 or\ 4. Ignoring the view change...\n") + warnings.warn('Warning in _set_view_from_bbox: bounding box is not a\ + tuple of length 3 or\ 4. Ignoring the view change...') return # Just grab bounding box diff --git a/lib/matplotlib/backend_tools.py b/lib/matplotlib/backend_tools.py index 2fe5001f776e..f54ba65be4c9 100644 --- a/lib/matplotlib/backend_tools.py +++ b/lib/matplotlib/backend_tools.py @@ -642,10 +642,9 @@ def scroll_zoom(self, event): ax = event.inaxes ax._set_view_from_bbox([event.x, event.y, scl]) - # first check if last scroll was done within - # the timing threshold. If yes, delete previous view - if(time.time()-self.lastscroll < self.scrollthresh): - # remove the last view and push the current + # If last scroll was done within the timing threshold, delete the + # previous view + if (time.time()-self.lastscroll) < self.scrollthresh: self.toolmanager.get_tool(_views_positions).back() self.figure.canvas.draw_idle() # force re-draw From 3e4f6963eed6dfc9c249b0152454c914b671b849 Mon Sep 17 00:00:00 2001 From: Julien Lhermitte Date: Mon, 19 Oct 2015 09:03:25 -0400 Subject: [PATCH 6/6] removed a whitespace, pep8 fix --- 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 810ebdf96a72..d392d86e47cd 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -3377,12 +3377,12 @@ def _set_view_from_bbox(self, bbox, direction='in', xzc = (xp*(scl - 1) + xcen)/scl yzc = (yp*(scl - 1) + ycen)/scl - bbox = [xzc - xwidth/2./scl, yzc - ywidth/2./scl, + bbox = [xzc - xwidth/2./scl, yzc - ywidth/2./scl, xzc + xwidth/2./scl, yzc + ywidth/2./scl] elif len(bbox) != 4: # should be len 3 or 4 but nothing else warnings.warn('Warning in _set_view_from_bbox: bounding box is not a\ - tuple of length 3 or\ 4. Ignoring the view change...') + tuple of length 3 or 4. Ignoring the view change...') return # Just grab bounding box