25
25
The base class for the Toolbar class of each interactive backend.
26
26
"""
27
27
28
+ from collections import namedtuple
28
29
from contextlib import contextmanager , suppress
29
30
from enum import Enum , IntEnum
30
31
import functools
@@ -2871,7 +2872,6 @@ def __init__(self, canvas):
2871
2872
self .canvas = canvas
2872
2873
canvas .toolbar = self
2873
2874
self ._nav_stack = cbook .Stack ()
2874
- self ._xypress = None # location and axis info at the time of the press
2875
2875
# This cursor will be set after the initial draw.
2876
2876
self ._lastCursor = cursors .POINTER
2877
2877
@@ -2889,10 +2889,9 @@ def __init__(self, canvas):
2889
2889
'button_release_event' , self ._zoom_pan_handler )
2890
2890
self ._id_drag = self .canvas .mpl_connect (
2891
2891
'motion_notify_event' , self .mouse_move )
2892
+ self ._pan_info = None
2892
2893
self ._zoom_info = None
2893
2894
2894
- self ._button_pressed = None # determined by button pressed at start
2895
-
2896
2895
self .mode = _Mode .NONE # a mode string for the status bar
2897
2896
self .set_history_buttons ()
2898
2897
@@ -3074,26 +3073,25 @@ def pan(self, *args):
3074
3073
a .set_navigate_mode (self .mode ._navigate_mode )
3075
3074
self .set_message (self .mode )
3076
3075
3076
+ _PanInfo = namedtuple ("_PanInfo" , "button axes cid" )
3077
+
3077
3078
def press_pan (self , event ):
3078
3079
"""Callback for mouse button press in pan/zoom mode."""
3079
- if event .button in [1 , 3 ]:
3080
- self ._button_pressed = event .button
3081
- else :
3082
- self ._button_pressed = None
3080
+ if (event .button not in [MouseButton .LEFT , MouseButton .RIGHT ]
3081
+ or event .x is None or event .y is None ):
3082
+ return
3083
+ axes = [a for a in self .canvas .figure .get_axes ()
3084
+ if a .in_axes (event ) and a .get_navigate () and a .can_pan ()]
3085
+ if not axes :
3083
3086
return
3084
3087
if self ._nav_stack () is None :
3085
- # set the home button to this view
3086
- self .push_current ()
3087
- x , y = event .x , event .y
3088
- self ._xypress = []
3089
- for i , a in enumerate (self .canvas .figure .get_axes ()):
3090
- if (x is not None and y is not None and a .in_axes (event ) and
3091
- a .get_navigate () and a .can_pan ()):
3092
- a .start_pan (x , y , event .button )
3093
- self ._xypress .append ((a , i ))
3094
- self .canvas .mpl_disconnect (self ._id_drag )
3095
- self ._id_drag = self .canvas .mpl_connect (
3096
- 'motion_notify_event' , self .drag_pan )
3088
+ self .push_current () # set the home button to this view
3089
+ for ax in axes :
3090
+ ax .start_pan (event .x , event .y , event .button )
3091
+ self .canvas .mpl_disconnect (self ._id_drag )
3092
+ id_drag = self .canvas .mpl_connect ("motion_notify_event" , self .drag_pan )
3093
+ self ._pan_info = self ._PanInfo (
3094
+ button = event .button , axes = axes , cid = id_drag )
3097
3095
press = cbook ._deprecate_method_override (
3098
3096
__class__ .press , self , since = "3.3" , message = "Calling an "
3099
3097
"overridden press() at pan start is deprecated since %(since)s "
@@ -3103,34 +3101,30 @@ def press_pan(self, event):
3103
3101
3104
3102
def drag_pan (self , event ):
3105
3103
"""Callback for dragging in pan/zoom mode."""
3106
- for a , ind in self ._xypress :
3107
- #safer to use the recorded button at the press than current button:
3108
- #multiple button can get pressed during motion.. .
3109
- a .drag_pan (self ._button_pressed , event .key , event .x , event .y )
3104
+ for ax in self ._pan_info . axes :
3105
+ # Using the recorded button at the press is safer than the current
3106
+ # button, as multiple buttons can get pressed during motion.
3107
+ ax .drag_pan (self ._pan_info . button , event .key , event .x , event .y )
3110
3108
self .canvas .draw_idle ()
3111
3109
3112
3110
def release_pan (self , event ):
3113
3111
"""Callback for mouse button release in pan/zoom mode."""
3114
-
3115
- if self ._button_pressed is None :
3112
+ if self ._pan_info is None :
3116
3113
return
3117
- self .canvas .mpl_disconnect (self ._id_drag )
3114
+ self .canvas .mpl_disconnect (self ._pan_info . cid )
3118
3115
self ._id_drag = self .canvas .mpl_connect (
3119
3116
'motion_notify_event' , self .mouse_move )
3120
- for a , ind in self ._xypress :
3121
- a .end_pan ()
3122
- if not self ._xypress :
3123
- return
3124
- self ._xypress = []
3125
- self ._button_pressed = None
3126
- self .push_current ()
3117
+ for ax in self ._pan_info .axes :
3118
+ ax .end_pan ()
3127
3119
release = cbook ._deprecate_method_override (
3128
3120
__class__ .press , self , since = "3.3" , message = "Calling an "
3129
3121
"overridden release() at pan stop is deprecated since %(since)s "
3130
3122
"and will be removed %(removal)s; override release_pan() instead." )
3131
3123
if release is not None :
3132
3124
release (event )
3133
3125
self ._draw ()
3126
+ self ._pan_info = None
3127
+ self .push_current ()
3134
3128
3135
3129
def zoom (self , * args ):
3136
3130
"""Toggle zoom to rect mode."""
@@ -3144,11 +3138,12 @@ def zoom(self, *args):
3144
3138
a .set_navigate_mode (self .mode ._navigate_mode )
3145
3139
self .set_message (self .mode )
3146
3140
3141
+ _ZoomInfo = namedtuple ("_ZoomInfo" , "direction start_xy axes cid" )
3142
+
3147
3143
def press_zoom (self , event ):
3148
3144
"""Callback for mouse button press in zoom to rect mode."""
3149
- if event .button not in [1 , 3 ]:
3150
- return
3151
- if event .x is None or event .y is None :
3145
+ if (event .button not in [MouseButton .LEFT , MouseButton .RIGHT ]
3146
+ or event .x is None or event .y is None ):
3152
3147
return
3153
3148
axes = [a for a in self .canvas .figure .get_axes ()
3154
3149
if a .in_axes (event ) and a .get_navigate () and a .can_zoom ()]
@@ -3158,12 +3153,9 @@ def press_zoom(self, event):
3158
3153
self .push_current () # set the home button to this view
3159
3154
id_zoom = self .canvas .mpl_connect (
3160
3155
"motion_notify_event" , self .drag_zoom )
3161
- self ._zoom_info = {
3162
- "direction" : "in" if event .button == 1 else "out" ,
3163
- "start_xy" : (event .x , event .y ),
3164
- "axes" : axes ,
3165
- "cid" : id_zoom ,
3166
- }
3156
+ self ._zoom_info = self ._ZoomInfo (
3157
+ direction = "in" if event .button == 1 else "out" ,
3158
+ start_xy = (event .x , event .y ), axes = axes , cid = id_zoom )
3167
3159
press = cbook ._deprecate_method_override (
3168
3160
__class__ .press , self , since = "3.3" , message = "Calling an "
3169
3161
"overridden press() at zoom start is deprecated since %(since)s "
@@ -3173,8 +3165,8 @@ def press_zoom(self, event):
3173
3165
3174
3166
def drag_zoom (self , event ):
3175
3167
"""Callback for dragging in zoom mode."""
3176
- start_xy = self ._zoom_info [ " start_xy" ]
3177
- ax = self ._zoom_info [ " axes" ] [0 ]
3168
+ start_xy = self ._zoom_info . start_xy
3169
+ ax = self ._zoom_info . axes [0 ]
3178
3170
(x1 , y1 ), (x2 , y2 ) = np .clip (
3179
3171
[start_xy , [event .x , event .y ]], ax .bbox .min , ax .bbox .max )
3180
3172
if event .key == "x" :
@@ -3190,44 +3182,40 @@ def release_zoom(self, event):
3190
3182
3191
3183
# We don't check the event button here, so that zooms can be cancelled
3192
3184
# by (pressing and) releasing another mouse button.
3193
- self .canvas .mpl_disconnect (self ._zoom_info [ " cid" ] )
3185
+ self .canvas .mpl_disconnect (self ._zoom_info . cid )
3194
3186
self .remove_rubberband ()
3195
3187
3196
- start_x , start_y = self ._zoom_info ["start_xy" ]
3197
-
3198
- for i , ax in enumerate (self ._zoom_info ["axes" ]):
3199
- x , y = event .x , event .y
3200
- # ignore singular clicks - 5 pixels is a threshold
3201
- # allows the user to "cancel" a zoom action
3202
- # by zooming by less than 5 pixels
3203
- if ((abs (x - start_x ) < 5 and event .key != "y" ) or
3204
- (abs (y - start_y ) < 5 and event .key != "x" )):
3205
- self ._xypress = None
3206
- release = cbook ._deprecate_method_override (
3207
- __class__ .press , self , since = "3.3" , message = "Calling an "
3208
- "overridden release() at zoom stop is deprecated since "
3209
- "%(since)s and will be removed %(removal)s; override "
3210
- "release_zoom() instead." )
3211
- if release is not None :
3212
- release (event )
3213
- self ._draw ()
3214
- return
3188
+ start_x , start_y = self ._zoom_info .start_xy
3189
+ # Ignore single clicks: 5 pixels is a threshold that allows the user to
3190
+ # "cancel" a zoom action by zooming by less than 5 pixels.
3191
+ if ((abs (event .x - start_x ) < 5 and event .key != "y" )
3192
+ or (abs (event .y - start_y ) < 5 and event .key != "x" )):
3193
+ self ._draw ()
3194
+ self ._zoom_info = None
3195
+ release = cbook ._deprecate_method_override (
3196
+ __class__ .press , self , since = "3.3" , message = "Calling an "
3197
+ "overridden release() at zoom stop is deprecated since "
3198
+ "%(since)s and will be removed %(removal)s; override "
3199
+ "release_zoom() instead." )
3200
+ if release is not None :
3201
+ release (event )
3202
+ return
3215
3203
3204
+ for i , ax in enumerate (self ._zoom_info .axes ):
3216
3205
# Detect whether this axes is twinned with an earlier axes in the
3217
3206
# list of zoomed axes, to avoid double zooming.
3218
3207
twinx = any (ax .get_shared_x_axes ().joined (ax , prev )
3219
- for prev in self ._zoom_info [ " axes" ] [:i ])
3208
+ for prev in self ._zoom_info . axes [:i ])
3220
3209
twiny = any (ax .get_shared_y_axes ().joined (ax , prev )
3221
- for prev in self ._zoom_info ["axes" ][:i ])
3222
-
3210
+ for prev in self ._zoom_info .axes [:i ])
3223
3211
ax ._set_view_from_bbox (
3224
- (start_x , start_y , x , y ), self . _zoom_info [ "direction" ] ,
3225
- event .key , twinx , twiny )
3212
+ (start_x , start_y , event . x , event . y ) ,
3213
+ self . _zoom_info . direction , event .key , twinx , twiny )
3226
3214
3227
3215
self ._draw ()
3228
3216
self ._zoom_info = None
3229
-
3230
3217
self .push_current ()
3218
+
3231
3219
release = cbook ._deprecate_method_override (
3232
3220
__class__ .release , self , since = "3.3" , message = "Calling an "
3233
3221
"overridden release() at zoom stop is deprecated since %(since)s "
0 commit comments