@@ -2062,6 +2062,40 @@ def set_handle_props(self, **handle_props):
2062
2062
self .update ()
2063
2063
self ._handle_props .update (handle_props )
2064
2064
2065
+ @property
2066
+ def default_state (self ):
2067
+ """
2068
+ Default state of the selector, which affect the widget's behavior. See
2069
+ the `state_modifier_keys` parameters for details.
2070
+ """
2071
+ return tuple (self ._default_state )
2072
+
2073
+ def add_default_state (self , value ):
2074
+ """
2075
+ Add a default state to define the widget's behavior. See the
2076
+ `state_modifier_keys` parameters for details.
2077
+
2078
+ Parameters
2079
+ ----------
2080
+ value : str
2081
+ Must be a supported state of the selector. See the
2082
+ `state_modifier_keys` parameters for details.
2083
+
2084
+ Raises
2085
+ ------
2086
+ ValueError
2087
+ When the value is not supported by the selector.
2088
+
2089
+ """
2090
+ supported_default_state = [
2091
+ key for key , value in self .state_modifier_keys .items ()
2092
+ if key != 'clear' and value != 'not-applicable'
2093
+ ]
2094
+ if value not in supported_default_state :
2095
+ keys = ', ' .join (supported_default_state )
2096
+ raise ValueError ('Setting default state must be one of the '
2097
+ f'following: { keys } .' )
2098
+ self ._default_state .add (value )
2065
2099
2066
2100
class SpanSelector (_SelectorWidget ):
2067
2101
"""
@@ -2129,6 +2163,12 @@ def on_select(min: float, max: float) -> Any
2129
2163
Distance in pixels within which the interactive tool handles can be
2130
2164
activated.
2131
2165
2166
+ state_modifier_keys : dict, optional
2167
+ Keyboard modifiers which affect the widget's behavior. Values
2168
+ amend the defaults.
2169
+
2170
+ - "clear": Clear the current shape, default: "escape".
2171
+
2132
2172
drag_from_anywhere : bool, default: False
2133
2173
If `True`, the widget can be moved by clicking anywhere within
2134
2174
its bounds.
@@ -2157,9 +2197,15 @@ def on_select(min: float, max: float) -> Any
2157
2197
def __init__ (self , ax , onselect , direction , minspan = 0 , useblit = False ,
2158
2198
props = None , onmove_callback = None , interactive = False ,
2159
2199
button = None , handle_props = None , grab_range = 10 ,
2160
- drag_from_anywhere = False , ignore_event_outside = False ):
2200
+ state_modifier_keys = None , drag_from_anywhere = False ,
2201
+ ignore_event_outside = False ):
2161
2202
2162
- super ().__init__ (ax , onselect , useblit = useblit , button = button )
2203
+ if state_modifier_keys is None :
2204
+ state_modifier_keys = dict (clear = 'escape' ,
2205
+ square = 'not-applicable' ,
2206
+ center = 'not-applicable' )
2207
+ super ().__init__ (ax , onselect , useblit = useblit , button = button ,
2208
+ state_modifier_keys = state_modifier_keys )
2163
2209
2164
2210
if props is None :
2165
2211
props = dict (facecolor = 'red' , alpha = 0.5 )
@@ -2446,7 +2492,7 @@ def _set_active_handle(self, event):
2446
2492
2447
2493
# Prioritise center handle over other handles
2448
2494
# Use 'C' to match the notation used in the RectangleSelector
2449
- if 'move' in self ._state :
2495
+ if 'move' in self ._state | self . _default_state :
2450
2496
self ._active_handle = 'C'
2451
2497
elif e_dist > self .grab_range :
2452
2498
# Not close to any handles
@@ -2730,8 +2776,7 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent)
2730
2776
- "move": Move the existing shape, default: no modifier.
2731
2777
- "clear": Clear the current shape, default: "escape".
2732
2778
- "square": Make the shape square, default: "shift".
2733
- - "center": Make the initial point the center of the shape,
2734
- default: "ctrl".
2779
+ - "center": change the shape around its center, default: "ctrl".
2735
2780
2736
2781
"square" and "center" can be combined.
2737
2782
@@ -2893,7 +2938,6 @@ def _press(self, event):
2893
2938
# button, ...
2894
2939
if self ._interactive and self ._selection_artist .get_visible ():
2895
2940
self ._set_active_handle (event )
2896
- self ._extents_on_press = self .extents
2897
2941
else :
2898
2942
self ._active_handle = None
2899
2943
@@ -2910,6 +2954,8 @@ def _press(self, event):
2910
2954
else :
2911
2955
self .set_visible (True )
2912
2956
2957
+ self ._extents_on_press = self .extents
2958
+
2913
2959
return False
2914
2960
2915
2961
def _release (self , event ):
@@ -3027,9 +3073,7 @@ def _onmove(self, event):
3027
3073
y1 = event .ydata
3028
3074
3029
3075
# move existing shape
3030
- elif (self ._active_handle == 'C' or
3031
- (self .drag_from_anywhere and self ._contains (event )) and
3032
- self ._extents_on_press is not None ):
3076
+ elif self ._active_handle == 'C' :
3033
3077
x0 , x1 , y0 , y1 = self ._extents_on_press
3034
3078
dx = event .xdata - self ._eventpress .xdata
3035
3079
dy = event .ydata - self ._eventpress .ydata
@@ -3164,14 +3208,13 @@ def _set_active_handle(self, event):
3164
3208
e_idx , e_dist = self ._edge_handles .closest (event .x , event .y )
3165
3209
m_idx , m_dist = self ._center_handle .closest (event .x , event .y )
3166
3210
3167
- if 'move' in self ._state :
3211
+ if 'move' in self ._state | self . _default_state :
3168
3212
self ._active_handle = 'C'
3169
3213
# Set active handle as closest handle, if mouse click is close enough.
3170
3214
elif m_dist < self .grab_range * 2 :
3171
3215
# Prioritise center handle over other handles
3172
3216
self ._active_handle = 'C'
3173
- elif (c_dist > self .grab_range and
3174
- e_dist > self .grab_range ):
3217
+ elif c_dist > self .grab_range and e_dist > self .grab_range :
3175
3218
# Not close to any handles
3176
3219
if self .drag_from_anywhere and self ._contains (event ):
3177
3220
# Check if we've clicked inside the region
0 commit comments