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

Skip to content

Commit f4c01d0

Browse files
committed
Add method to add default state
1 parent 49609c3 commit f4c01d0

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
@@ -803,6 +823,24 @@ def onselect(*args):
803823
assert tool.extents == (10, 50)
804824

805825

826+
def test_span_selector_add_default_state():
827+
ax = get_ax()
828+
829+
def onselect(*args):
830+
pass
831+
832+
tool = widgets.SpanSelector(ax, onselect, 'horizontal', interactive=True)
833+
834+
with pytest.raises(ValueError):
835+
tool.add_default_state('unsupported_state')
836+
with pytest.raises(ValueError):
837+
tool.add_default_state('center')
838+
with pytest.raises(ValueError):
839+
tool.add_default_state('square')
840+
841+
tool.add_default_state('move')
842+
843+
806844
def test_tool_line_handle():
807845
ax = get_ax()
808846

lib/matplotlib/widgets.py

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2062,6 +2062,40 @@ def set_handle_props(self, **handle_props):
20622062
self.update()
20632063
self._handle_props.update(handle_props)
20642064

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

20662100
class SpanSelector(_SelectorWidget):
20672101
"""
@@ -2129,6 +2163,12 @@ def on_select(min: float, max: float) -> Any
21292163
Distance in pixels within which the interactive tool handles can be
21302164
activated.
21312165
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+
21322172
drag_from_anywhere : bool, default: False
21332173
If `True`, the widget can be moved by clicking anywhere within
21342174
its bounds.
@@ -2157,9 +2197,15 @@ def on_select(min: float, max: float) -> Any
21572197
def __init__(self, ax, onselect, direction, minspan=0, useblit=False,
21582198
props=None, onmove_callback=None, interactive=False,
21592199
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):
21612202

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

21642210
if props is None:
21652211
props = dict(facecolor='red', alpha=0.5)
@@ -2446,7 +2492,7 @@ def _set_active_handle(self, event):
24462492

24472493
# Prioritise center handle over other handles
24482494
# 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:
24502496
self._active_handle = 'C'
24512497
elif e_dist > self.grab_range:
24522498
# Not close to any handles
@@ -2730,8 +2776,7 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent)
27302776
- "move": Move the existing shape, default: no modifier.
27312777
- "clear": Clear the current shape, default: "escape".
27322778
- "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".
27352780
27362781
"square" and "center" can be combined.
27372782
@@ -2893,7 +2938,6 @@ def _press(self, event):
28932938
# button, ...
28942939
if self._interactive and self._selection_artist.get_visible():
28952940
self._set_active_handle(event)
2896-
self._extents_on_press = self.extents
28972941
else:
28982942
self._active_handle = None
28992943

@@ -2910,6 +2954,8 @@ def _press(self, event):
29102954
else:
29112955
self.set_visible(True)
29122956

2957+
self._extents_on_press = self.extents
2958+
29132959
return False
29142960

29152961
def _release(self, event):
@@ -3027,9 +3073,7 @@ def _onmove(self, event):
30273073
y1 = event.ydata
30283074

30293075
# 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':
30333077
x0, x1, y0, y1 = self._extents_on_press
30343078
dx = event.xdata - self._eventpress.xdata
30353079
dy = event.ydata - self._eventpress.ydata
@@ -3164,14 +3208,13 @@ def _set_active_handle(self, event):
31643208
e_idx, e_dist = self._edge_handles.closest(event.x, event.y)
31653209
m_idx, m_dist = self._center_handle.closest(event.x, event.y)
31663210

3167-
if 'move' in self._state:
3211+
if 'move' in self._state | self._default_state:
31683212
self._active_handle = 'C'
31693213
# Set active handle as closest handle, if mouse click is close enough.
31703214
elif m_dist < self.grab_range * 2:
31713215
# Prioritise center handle over other handles
31723216
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:
31753218
# Not close to any handles
31763219
if self.drag_from_anywhere and self._contains(event):
31773220
# Check if we've clicked inside the region

0 commit comments

Comments
 (0)