Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Replace the data_coordinates state by the use_data_coordinates ar…
…gument
  • Loading branch information
ericpre committed Dec 4, 2021
commit 8ce27d0ccdf7cf266197c743fe04790879a69958
11 changes: 5 additions & 6 deletions doc/users/next_whats_new/selector_improvement.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ The rotation is enabled or disable by striking the *r* key
*selector.add_state('rotate')*.
Comment thread
ericpre marked this conversation as resolved.
Outdated

The aspect ratio of the axes can now be taken into account when using the
"square" state. This can be enable or disable by striking the *d* key
(default value of 'data_coordinates' in *state_modifier_keys*)
or by calling *selector.add_state('rotate')*.
"square" state. This is enable by specifying *use_data_coordinates='True'* when
Comment thread
ericpre marked this conversation as resolved.
Outdated
Comment thread
ericpre marked this conversation as resolved.
Outdated
the selector is initialized.

In addition to changing selector state interactively using the modifier keys
defined in *state_modifier_keys*, the selector state can now be changed
Expand All @@ -29,12 +28,12 @@ programmatically using the *add_state* and *remove_state* method.
ax = fig.add_subplot()
ax.plot(values, values)

selector = RectangleSelector(ax, print, interactive=True, drag_from_anywhere=True)
selector = RectangleSelector(ax, print, interactive=True,
drag_from_anywhere=True,
use_data_coordinates=True)
selector.add_state('rotate') # alternatively press 'r' key
# rotate the selector interactively

selector.remove_state('rotate') # alternatively press 'r' key

selector.add_state('square')
Comment thread
ericpre marked this conversation as resolved.
# to keep the aspect ratio in data coordinates
selector.add_state('data_coordinates')
52 changes: 26 additions & 26 deletions lib/matplotlib/tests/test_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,6 @@ def onselect(epress, erelease):
tool.add_state('move')
tool.add_state('square')
tool.add_state('center')
tool.add_state('data_coordinates')


@pytest.mark.parametrize('add_state', [True, False])
Expand Down Expand Up @@ -466,48 +465,49 @@ def onselect(epress, erelease):
do_event(tool, 'release', xdata=130, ydata=140)
assert tool.extents == (100, 130, 100, 140)
assert len(tool._state) == 0
for state in ['rotate', 'data_coordinates', 'square', 'center']:
for state in ['rotate', 'square', 'center']:
tool.add_state(state)
assert len(tool._state) == 1
tool.remove_state(state)
assert len(tool._state) == 0


def test_rectangle_resize_square_center_aspect():
@pytest.mark.parametrize('use_data_coordinates', [False, True])
def test_rectangle_resize_square_center_aspect(use_data_coordinates):
ax = get_ax()
ax.set_aspect(0.8)

def onselect(epress, erelease):
pass

tool = widgets.RectangleSelector(ax, onselect, interactive=True)
tool = widgets.RectangleSelector(ax, onselect, interactive=True,
use_data_coordinates=use_data_coordinates)
# Create rectangle
_resize_rectangle(tool, 70, 65, 120, 115)
assert tool.extents == (70.0, 120.0, 65.0, 115.0)
tool.add_state('square')
tool.add_state('center')
assert tool.extents == (70.0, 120.0, 65.0, 115.0)

# resize E handle
extents = tool.extents
xdata, ydata = extents[1], extents[3]
xdiff = 10
xdata_new, ydata_new = xdata + xdiff, ydata
ychange = xdiff * 1 / tool._aspect_ratio_correction
_resize_rectangle(tool, xdata, ydata, xdata_new, ydata_new)
assert_allclose(tool.extents, [extents[0] - xdiff, xdata_new,
46.25, 133.75])

# use data coordinates
do_event(tool, 'on_key_press', key='d')
# resize E handle
extents = tool.extents
xdata, ydata, width = extents[1], extents[3], extents[1] - extents[0]
xdiff, ycenter = 10, extents[2] + (extents[3] - extents[2]) / 2
xdata_new, ydata_new = xdata + xdiff, ydata
ychange = width / 2 + xdiff
_resize_rectangle(tool, xdata, ydata, xdata_new, ydata_new)
assert_allclose(tool.extents, [extents[0] - xdiff, xdata_new,
ycenter - ychange, ycenter + ychange])
if use_data_coordinates:
# resize E handle
extents = tool.extents
xdata, ydata, width = extents[1], extents[3], extents[1] - extents[0]
xdiff, ycenter = 10, extents[2] + (extents[3] - extents[2]) / 2
xdata_new, ydata_new = xdata + xdiff, ydata
ychange = width / 2 + xdiff
_resize_rectangle(tool, xdata, ydata, xdata_new, ydata_new)
assert_allclose(tool.extents, [extents[0] - xdiff, xdata_new,
ycenter - ychange, ycenter + ychange])
else:
# resize E handle
extents = tool.extents
xdata, ydata = extents[1], extents[3]
xdiff = 10
xdata_new, ydata_new = xdata + xdiff, ydata
ychange = xdiff * 1 / tool._aspect_ratio_correction
_resize_rectangle(tool, xdata, ydata, xdata_new, ydata_new)
assert_allclose(tool.extents, [extents[0] - xdiff, xdata_new,
46.25, 133.75])


def test_ellipse():
Expand Down
42 changes: 20 additions & 22 deletions lib/matplotlib/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -1801,7 +1801,7 @@ def _update(self):
class _SelectorWidget(AxesWidget):

def __init__(self, ax, onselect, useblit=False, button=None,
state_modifier_keys=None):
state_modifier_keys=None, use_data_coordinates=False):
super().__init__(ax)

self.visible = True
Expand All @@ -1811,9 +1811,9 @@ def __init__(self, ax, onselect, useblit=False, button=None,

self._state_modifier_keys = dict(move=' ', clear='escape',
square='shift', center='control',
data_coordinates='d',
rotate='r')
self._state_modifier_keys.update(state_modifier_keys or {})
self._use_data_coordinates = use_data_coordinates

self.background = None

Expand Down Expand Up @@ -1999,17 +1999,15 @@ def on_key_press(self, event):
return
for (state, modifier) in self._state_modifier_keys.items():
if modifier in key.split('+'):
# 'rotate' and 'data_coordinates' are changing _state on
# press and are not removed from _state when releasing
if state in ['rotate', 'data_coordinates']:
# 'rotate' is changing _state on press and is not removed
# from _state when releasing
if state == 'rotate':
if state in self._state:
self._state.discard(state)
else:
self._state.add(state)
else:
self._state.add(state)
if 'data_coordinates' in state:
self._set_aspect_ratio_correction()
self._on_key_press(event)

def _on_key_press(self, event):
Expand All @@ -2019,12 +2017,10 @@ def on_key_release(self, event):
"""Key release event handler and validator."""
if self.active:
key = event.key or ''
key = key.replace('ctrl', 'control')
for (state, modifier) in self._state_modifier_keys.items():
# 'rotate' and 'data_coordinates' are changing _state on
# press and are not removed from _state when releasing
if (modifier in key.split('+') and
state not in ['rotate', 'data_coordinates']):
# 'rotate' is changing _state on press and is not removed
# from _state when releasing
if modifier in key.split('+') and state != 'rotate':
self._state.discard(state)
self._on_key_release(event)

Expand Down Expand Up @@ -2236,7 +2232,6 @@ def __init__(self, ax, onselect, direction, minspan=0, useblit=False,
state_modifier_keys = dict(clear='escape',
square='not-applicable',
center='not-applicable',
data_coordinates='not-applicable',
rotate='not-applicable')
super().__init__(ax, onselect, useblit=useblit, button=button,
state_modifier_keys=state_modifier_keys)
Expand Down Expand Up @@ -2811,13 +2806,11 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent)
- "clear": Clear the current shape, default: "escape".
- "square": Make the shape square, default: "shift".
- "center": change the shape around its center, default: "ctrl".
- "data_coordinates": define if data or figure coordinates should be
used to define the square shape, default: "d"
- "rotate": Rotate the shape around its center, default: "r".

"square" and "center" can be combined. The square shape can be defined
in data or figure coordinates as determined by the ``data_coordinates``
modifier, which can be enable and disable by pressing the 'd' key.
in data or figure coordinates as determined by the
``use_data_coordinates`` argument specified when creating the selector.

drag_from_anywhere : bool, default: False
If `True`, the widget can be moved by clicking anywhere within
Expand All @@ -2827,6 +2820,10 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent)
If `True`, the event triggered outside the span selector will be
ignored.

use_data_coordinates : bool, default: False
If `True`, the "square" shape of the selector is defined in
data coordinates instead of figure coordinates.

"""


Expand Down Expand Up @@ -2871,9 +2868,11 @@ def __init__(self, ax, onselect, drawtype='box',
lineprops=None, props=None, spancoords='data',
button=None, grab_range=10, handle_props=None,
interactive=False, state_modifier_keys=None,
drag_from_anywhere=False, ignore_event_outside=False):
drag_from_anywhere=False, ignore_event_outside=False,
use_data_coordinates=False):
super().__init__(ax, onselect, useblit=useblit, button=button,
state_modifier_keys=state_modifier_keys)
state_modifier_keys=state_modifier_keys,
use_data_coordinates=use_data_coordinates)

self.visible = True
self._interactive = interactive
Expand Down Expand Up @@ -3076,7 +3075,7 @@ def _onmove(self, event):
# refmax is used when moving the corner handle with the square state
# and is the maximum between refx and refy
refmax = None
Comment thread
ericpre marked this conversation as resolved.
if 'data_coordinates' in state:
if self._use_data_coordinates:
refx, refy = dx, dy
else:
# Add 1e-6 to avoid divided by zero error
Expand Down Expand Up @@ -3213,7 +3212,7 @@ def _set_aspect_ratio_correction(self):
return

self._selection_artist._aspect_ratio_correction = aspect_ratio
if 'data_coordinates' in self._state:
if self._use_data_coordinates:
self._aspect_ratio_correction = 1
else:
self._aspect_ratio_correction = aspect_ratio
Expand Down Expand Up @@ -3581,7 +3580,6 @@ def __init__(self, ax, onselect, useblit=False,
move_all='shift', move='not-applicable',
square='not-applicable',
center='not-applicable',
data_coordinates='not-applicable',
rotate='not-applicable')
super().__init__(ax, onselect, useblit=useblit,
state_modifier_keys=state_modifier_keys)
Expand Down