@@ -2044,6 +2044,40 @@ def set_handle_props(self, **handle_props):
2044
2044
self .update ()
2045
2045
self ._handle_props .update (handle_props )
2046
2046
2047
+ @property
2048
+ def default_state (self ):
2049
+ """
2050
+ Default state of the selector, which affect the widget's behavior. See
2051
+ the `state_modifier_keys` parameters for details.
2052
+ """
2053
+ return tuple (self ._default_state )
2054
+
2055
+ def add_default_state (self , value ):
2056
+ """
2057
+ Add a default state to define the widget's behavior. See the
2058
+ `state_modifier_keys` parameters for details.
2059
+
2060
+ Parameters
2061
+ ----------
2062
+ value : str
2063
+ Must be a supported state of the selector. See the
2064
+ `state_modifier_keys` parameters for details.
2065
+
2066
+ Raises
2067
+ ------
2068
+ ValueError
2069
+ When the value is not supported by the selector.
2070
+
2071
+ """
2072
+ supported_default_state = [
2073
+ key for key , value in self .state_modifier_keys .items ()
2074
+ if key != 'clear' and value != 'not-applicable'
2075
+ ]
2076
+ if value not in supported_default_state :
2077
+ keys = ', ' .join (supported_default_state )
2078
+ raise ValueError ('Setting default state must be one of the '
2079
+ f'following: { keys } .' )
2080
+ self ._default_state .add (value )
2047
2081
2048
2082
class SpanSelector (_SelectorWidget ):
2049
2083
"""
@@ -2110,6 +2144,12 @@ def on_select(min: float, max: float) -> Any
2110
2144
Distance in pixels within which the interactive tool handles can be
2111
2145
activated.
2112
2146
2147
+ state_modifier_keys : dict, optional
2148
+ Keyboard modifiers which affect the widget's behavior. Values
2149
+ amend the defaults.
2150
+
2151
+ - "clear": Clear the current shape, default: "escape".
2152
+
2113
2153
drag_from_anywhere : bool, default: False
2114
2154
If `True`, the widget can be moved by clicking anywhere within
2115
2155
its bounds.
@@ -2138,9 +2178,15 @@ def on_select(min: float, max: float) -> Any
2138
2178
def __init__ (self , ax , onselect , direction , minspan = 0 , useblit = False ,
2139
2179
props = None , onmove_callback = None , interactive = False ,
2140
2180
button = None , handle_props = None , grab_range = 10 ,
2141
- drag_from_anywhere = False , ignore_event_outside = False ):
2181
+ state_modifier_keys = None , drag_from_anywhere = False ,
2182
+ ignore_event_outside = False ):
2142
2183
2143
- super ().__init__ (ax , onselect , useblit = useblit , button = button )
2184
+ if state_modifier_keys is None :
2185
+ state_modifier_keys = dict (clear = 'escape' ,
2186
+ square = 'not-applicable' ,
2187
+ center = 'not-applicable' )
2188
+ super ().__init__ (ax , onselect , useblit = useblit , button = button ,
2189
+ state_modifier_keys = state_modifier_keys )
2144
2190
2145
2191
if props is None :
2146
2192
props = dict (facecolor = 'red' , alpha = 0.5 )
@@ -2425,7 +2471,7 @@ def _set_active_handle(self, event):
2425
2471
2426
2472
# Prioritise center handle over other handles
2427
2473
# Use 'C' to match the notation used in the RectangleSelector
2428
- if 'move' in self ._state :
2474
+ if 'move' in self ._state | self . _default_state :
2429
2475
self ._active_handle = 'C'
2430
2476
elif e_dist > self .grab_range :
2431
2477
# Not close to any handles
@@ -2706,8 +2752,7 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent)
2706
2752
- "move": Move the existing shape, default: no modifier.
2707
2753
- "clear": Clear the current shape, default: "escape".
2708
2754
- "square": Makes the shape square, default: "shift".
2709
- - "center": Make the initial point the center of the shape,
2710
- default: "ctrl".
2755
+ - "center": change the shape around its center, default: "ctrl".
2711
2756
2712
2757
"square" and "center" can be combined.
2713
2758
@@ -2869,7 +2914,6 @@ def _press(self, event):
2869
2914
# button, ...
2870
2915
if self ._interactive and self ._selection_artist .get_visible ():
2871
2916
self ._set_active_handle (event )
2872
- self ._extents_on_press = self .extents
2873
2917
else :
2874
2918
self ._active_handle = None
2875
2919
@@ -2886,6 +2930,8 @@ def _press(self, event):
2886
2930
else :
2887
2931
self .set_visible (True )
2888
2932
2933
+ self ._extents_on_press = self .extents
2934
+
2889
2935
return False
2890
2936
2891
2937
def _release (self , event ):
@@ -3000,9 +3046,7 @@ def _onmove(self, event):
3000
3046
y1 = event .ydata
3001
3047
3002
3048
# move existing shape
3003
- elif (self ._active_handle == 'C' or
3004
- (self .drag_from_anywhere and self ._contains (event )) and
3005
- self ._extents_on_press is not None ):
3049
+ elif self ._active_handle == 'C' :
3006
3050
x0 , x1 , y0 , y1 = self ._extents_on_press
3007
3051
dx = event .xdata - self ._eventpress .xdata
3008
3052
dy = event .ydata - self ._eventpress .ydata
@@ -3137,14 +3181,13 @@ def _set_active_handle(self, event):
3137
3181
e_idx , e_dist = self ._edge_handles .closest (event .x , event .y )
3138
3182
m_idx , m_dist = self ._center_handle .closest (event .x , event .y )
3139
3183
3140
- if 'move' in self ._state :
3184
+ if 'move' in self ._state | self . _default_state :
3141
3185
self ._active_handle = 'C'
3142
3186
# Set active handle as closest handle, if mouse click is close enough.
3143
3187
elif m_dist < self .grab_range * 2 :
3144
3188
# Prioritise center handle over other handles
3145
3189
self ._active_handle = 'C'
3146
- elif (c_dist > self .grab_range and
3147
- e_dist > self .grab_range ):
3190
+ elif c_dist > self .grab_range and e_dist > self .grab_range :
3148
3191
# Not close to any handles
3149
3192
if self .drag_from_anywhere and self ._contains (event ):
3150
3193
# Check if we've clicked inside the region
0 commit comments