Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 45d849c

Browse files
committed
Add method to add default state
1 parent 67d8f04 commit 45d849c

File tree

2 files changed

+97
-16
lines changed

2 files changed

+97
-16
lines changed

lib/matplotlib/tests/test_widgets.py

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,26 @@ def onselect(epress, erelease):
171171
assert tool.extents == (xdata_new, extents[1], ydata_new, extents[3])
172172

173173

174+
def test_rectangle_add_default_state():
175+
ax = get_ax()
176+
177+
def onselect(epress, erelease):
178+
pass
179+
180+
tool = widgets.RectangleSelector(ax, onselect, interactive=True)
181+
# Create rectangle
182+
_resize_rectangle(tool, 70, 65, 125, 130)
183+
184+
with pytest.raises(ValueError):
185+
tool.add_default_state('unsupported_state')
186+
187+
with pytest.raises(ValueError):
188+
tool.add_default_state('clear')
189+
tool.add_default_state('move')
190+
tool.add_default_state('square')
191+
tool.add_default_state('center')
192+
193+
174194
@pytest.mark.parametrize('use_default_state', [True, False])
175195
def test_rectangle_resize_center(use_default_state):
176196
ax = get_ax()
@@ -184,7 +204,7 @@ def onselect(epress, erelease):
184204
assert tool.extents == (70.0, 125.0, 65.0, 130.0)
185205

186206
if use_default_state:
187-
tool._default_state.add('center')
207+
tool.add_default_state('center')
188208
use_key = None
189209
else:
190210
use_key = 'control'
@@ -257,7 +277,7 @@ def onselect(epress, erelease):
257277
assert tool.extents == (70.0, 120.0, 65.0, 115.0)
258278

259279
if use_default_state:
260-
tool._default_state.add('square')
280+
tool.add_default_state('square')
261281
use_key = None
262282
else:
263283
use_key = 'shift'
@@ -326,8 +346,8 @@ def onselect(epress, erelease):
326346
tool = widgets.RectangleSelector(ax, onselect, interactive=True)
327347
# Create rectangle
328348
_resize_rectangle(tool, 70, 65, 120, 115)
329-
tool._default_state.add('square')
330-
tool._default_state.add('center')
349+
tool.add_default_state('square')
350+
tool.add_default_state('center')
331351
assert tool.extents == (70.0, 120.0, 65.0, 115.0)
332352

333353
# resize NE handle
@@ -727,6 +747,24 @@ def onselect(epress, erelease):
727747
assert artist.get_alpha() == 0.3
728748

729749

750+
def test_span_selector_add_default_state():
751+
ax = get_ax()
752+
753+
def onselect(*args):
754+
pass
755+
756+
tool = widgets.SpanSelector(ax, onselect, 'horizontal', interactive=True)
757+
758+
with pytest.raises(ValueError):
759+
tool.add_default_state('unsupported_state')
760+
with pytest.raises(ValueError):
761+
tool.add_default_state('center')
762+
with pytest.raises(ValueError):
763+
tool.add_default_state('square')
764+
765+
tool.add_default_state('move')
766+
767+
730768
def test_tool_line_handle():
731769
ax = get_ax()
732770

lib/matplotlib/widgets.py

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2044,6 +2044,40 @@ def set_handle_props(self, **handle_props):
20442044
self.update()
20452045
self._handle_props.update(handle_props)
20462046

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)
20472081

20482082
class SpanSelector(_SelectorWidget):
20492083
"""
@@ -2110,6 +2144,12 @@ def on_select(min: float, max: float) -> Any
21102144
Distance in pixels within which the interactive tool handles can be
21112145
activated.
21122146
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+
21132153
drag_from_anywhere : bool, default: False
21142154
If `True`, the widget can be moved by clicking anywhere within
21152155
its bounds.
@@ -2138,9 +2178,15 @@ def on_select(min: float, max: float) -> Any
21382178
def __init__(self, ax, onselect, direction, minspan=0, useblit=False,
21392179
props=None, onmove_callback=None, interactive=False,
21402180
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):
21422183

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)
21442190

21452191
if props is None:
21462192
props = dict(facecolor='red', alpha=0.5)
@@ -2425,7 +2471,7 @@ def _set_active_handle(self, event):
24252471

24262472
# Prioritise center handle over other handles
24272473
# 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:
24292475
self._active_handle = 'C'
24302476
elif e_dist > self.grab_range:
24312477
# Not close to any handles
@@ -2706,8 +2752,7 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent)
27062752
- "move": Move the existing shape, default: no modifier.
27072753
- "clear": Clear the current shape, default: "escape".
27082754
- "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".
27112756
27122757
"square" and "center" can be combined.
27132758
@@ -2869,7 +2914,6 @@ def _press(self, event):
28692914
# button, ...
28702915
if self._interactive and self._selection_artist.get_visible():
28712916
self._set_active_handle(event)
2872-
self._extents_on_press = self.extents
28732917
else:
28742918
self._active_handle = None
28752919

@@ -2886,6 +2930,8 @@ def _press(self, event):
28862930
else:
28872931
self.set_visible(True)
28882932

2933+
self._extents_on_press = self.extents
2934+
28892935
return False
28902936

28912937
def _release(self, event):
@@ -3000,9 +3046,7 @@ def _onmove(self, event):
30003046
y1 = event.ydata
30013047

30023048
# 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':
30063050
x0, x1, y0, y1 = self._extents_on_press
30073051
dx = event.xdata - self._eventpress.xdata
30083052
dy = event.ydata - self._eventpress.ydata
@@ -3137,14 +3181,13 @@ def _set_active_handle(self, event):
31373181
e_idx, e_dist = self._edge_handles.closest(event.x, event.y)
31383182
m_idx, m_dist = self._center_handle.closest(event.x, event.y)
31393183

3140-
if 'move' in self._state:
3184+
if 'move' in self._state | self._default_state:
31413185
self._active_handle = 'C'
31423186
# Set active handle as closest handle, if mouse click is close enough.
31433187
elif m_dist < self.grab_range * 2:
31443188
# Prioritise center handle over other handles
31453189
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:
31483191
# Not close to any handles
31493192
if self.drag_from_anywhere and self._contains(event):
31503193
# Check if we've clicked inside the region

0 commit comments

Comments
 (0)