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