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

Skip to content

Commit 8f6b55c

Browse files
committed
Fix center modifier in interactive rectangle selector - in _onmove of existing shape
1 parent f6c97b5 commit 8f6b55c

File tree

2 files changed

+193
-12
lines changed

2 files changed

+193
-12
lines changed

lib/matplotlib/tests/test_widgets.py

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,158 @@ def onselect(epress, erelease):
9090
assert tool.center == (180, 190)
9191

9292

93+
def test_rectangle_resize():
94+
ax = get_ax()
95+
96+
def onselect(epress, erelease):
97+
pass
98+
99+
tool = widgets.RectangleSelector(ax, onselect, interactive=True)
100+
# Create rectangle
101+
do_event(tool, 'press', xdata=0, ydata=10, button=1)
102+
do_event(tool, 'onmove', xdata=100, ydata=120, button=1)
103+
do_event(tool, 'release', xdata=100, ydata=120, button=1)
104+
assert tool.extents == (0.0, 100.0, 10.0, 120.0)
105+
106+
# resize NE handle
107+
extents = tool.extents
108+
xdata, ydata = extents[1], extents[3]
109+
xdata_new, ydata_new = xdata + 10, ydata + 5
110+
do_event(tool, 'press', xdata=xdata, ydata=ydata, button=1)
111+
do_event(tool, 'onmove', xdata=xdata_new, ydata=ydata_new, button=1)
112+
do_event(tool, 'release', xdata=xdata_new, ydata=ydata_new, button=1)
113+
assert tool.extents == (0.0, xdata_new, 10.0, ydata_new)
114+
115+
# resize E handle
116+
extents = tool.extents
117+
xdata, ydata = extents[1], extents[2] + (extents[3] - extents[2]) / 2
118+
xdata_new, ydata_new = xdata + 10, ydata
119+
do_event(tool, 'press', xdata=xdata, ydata=ydata, button=1)
120+
do_event(tool, 'onmove', xdata=xdata_new, ydata=ydata_new, button=1)
121+
do_event(tool, 'release', xdata=xdata_new, ydata=ydata_new, button=1)
122+
assert tool.extents == (0.0, xdata_new, 10.0, 125.0)
123+
124+
# resize W handle
125+
extents = tool.extents
126+
xdata, ydata = extents[0], extents[2] + (extents[3] - extents[2]) / 2
127+
xdata_new, ydata_new = xdata + 15, ydata
128+
do_event(tool, 'press', xdata=xdata, ydata=ydata, button=1)
129+
do_event(tool, 'onmove', xdata=xdata_new, ydata=ydata_new, button=1)
130+
do_event(tool, 'release', xdata=xdata_new, ydata=ydata_new, button=1)
131+
assert tool.extents == (xdata_new, 120.0, 10.0, 125.0)
132+
133+
# resize SW handle
134+
extents = tool.extents
135+
xdata, ydata = extents[0], extents[2]
136+
xdata_new, ydata_new = xdata + 20, ydata + 25
137+
do_event(tool, 'press', xdata=xdata, ydata=ydata, button=1)
138+
do_event(tool, 'onmove', xdata=xdata_new, ydata=ydata_new, button=1)
139+
do_event(tool, 'release', xdata=xdata_new, ydata=ydata_new, button=1)
140+
assert tool.extents == (xdata_new, 120.0, ydata_new, 125.0)
141+
142+
143+
@pytest.mark.parametrize('use_default_state', [True, False])
144+
def test_rectangle_resize_center(use_default_state):
145+
ax = get_ax()
146+
147+
def onselect(epress, erelease):
148+
pass
149+
150+
tool = widgets.RectangleSelector(ax, onselect, interactive=True)
151+
# Create rectangle
152+
do_event(tool, 'press', xdata=70, ydata=65, button=1)
153+
do_event(tool, 'onmove', xdata=125, ydata=130, button=1)
154+
do_event(tool, 'release', xdata=125, ydata=130, button=1)
155+
assert tool.extents == (70.0, 125.0, 65.0, 130.0)
156+
157+
if use_default_state:
158+
tool._default_state.add('center')
159+
160+
# resize NE handle
161+
extents = tool.extents
162+
xdata, ydata = extents[1], extents[3]
163+
xdiff, ydiff = 10, 5
164+
xdata_new, ydata_new = xdata + xdiff, ydata + ydiff
165+
do_event(tool, 'press', xdata=xdata, ydata=ydata, button=1)
166+
if not use_default_state:
167+
do_event(tool, 'on_key_press', key='control')
168+
do_event(tool, 'onmove', xdata=xdata_new, ydata=ydata_new, button=1)
169+
if not use_default_state:
170+
do_event(tool, 'on_key_release', key='control')
171+
do_event(tool, 'release', xdata=xdata_new, ydata=ydata_new, button=1)
172+
assert tool.extents == (70.0 - xdiff, xdata_new, 65.0 - ydiff, ydata_new)
173+
174+
# resize E handle
175+
extents = tool.extents
176+
xdata, ydata = extents[1], extents[2] + (extents[3] - extents[2]) / 2
177+
xdiff = 10
178+
xdata_new, ydata_new = xdata + xdiff, ydata
179+
do_event(tool, 'press', xdata=xdata, ydata=ydata, button=1)
180+
if not use_default_state:
181+
do_event(tool, 'on_key_press', key='control')
182+
do_event(tool, 'onmove', xdata=xdata_new, ydata=ydata_new, button=1)
183+
do_event(tool, 'release', xdata=xdata_new, ydata=ydata_new, button=1)
184+
if not use_default_state:
185+
do_event(tool, 'on_key_release', key='control')
186+
assert tool.extents == (60.0 - xdiff, xdata_new, 60.0, 135.0)
187+
188+
# resize E handle negative diff
189+
extents = tool.extents
190+
xdata, ydata = extents[1], extents[2] + (extents[3] - extents[2]) / 2
191+
xdiff = -20
192+
xdata_new, ydata_new = xdata + xdiff, ydata
193+
do_event(tool, 'press', xdata=xdata, ydata=ydata, button=1)
194+
if not use_default_state:
195+
do_event(tool, 'on_key_press', key='control')
196+
do_event(tool, 'onmove', xdata=xdata_new, ydata=ydata_new, button=1)
197+
do_event(tool, 'release', xdata=xdata_new, ydata=ydata_new, button=1)
198+
if not use_default_state:
199+
do_event(tool, 'on_key_release', key='control')
200+
assert tool.extents == (50.0 - xdiff, xdata_new, 60.0, 135.0)
201+
202+
# resize W handle
203+
extents = tool.extents
204+
xdata, ydata = extents[0], extents[2] + (extents[3] - extents[2]) / 2
205+
xdiff = 15
206+
xdata_new, ydata_new = xdata + xdiff, ydata
207+
do_event(tool, 'press', xdata=xdata, ydata=ydata, button=1)
208+
if not use_default_state:
209+
do_event(tool, 'on_key_press', key='control')
210+
do_event(tool, 'onmove', xdata=xdata_new, ydata=ydata_new, button=1)
211+
do_event(tool, 'release', xdata=xdata_new, ydata=ydata_new, button=1)
212+
if not use_default_state:
213+
do_event(tool, 'on_key_release', key='control')
214+
assert tool.extents == (xdata_new, 125.0 - xdiff, 60.0, 135.0)
215+
216+
# resize W handle
217+
extents = tool.extents
218+
xdata, ydata = extents[0], extents[2] + (extents[3] - extents[2]) / 2
219+
xdiff = -25
220+
xdata_new, ydata_new = xdata + xdiff, ydata
221+
do_event(tool, 'press', xdata=xdata, ydata=ydata, button=1)
222+
if not use_default_state:
223+
do_event(tool, 'on_key_press', key='control')
224+
do_event(tool, 'onmove', xdata=xdata_new, ydata=ydata_new, button=1)
225+
do_event(tool, 'release', xdata=xdata_new, ydata=ydata_new, button=1)
226+
if not use_default_state:
227+
do_event(tool, 'on_key_release', key='control')
228+
assert tool.extents == (xdata_new, 110.0 - xdiff, 60.0, 135.0)
229+
230+
# resize SW handle
231+
extents = tool.extents
232+
xdata, ydata = extents[0], extents[2]
233+
xdiff, ydiff = 20, 25
234+
xdata_new, ydata_new = xdata + xdiff, ydata + ydiff
235+
do_event(tool, 'press', xdata=xdata, ydata=ydata, button=1)
236+
if not use_default_state:
237+
do_event(tool, 'on_key_press', key='control')
238+
do_event(tool, 'onmove', xdata=xdata_new, ydata=ydata_new, button=1)
239+
do_event(tool, 'release', xdata=xdata_new, ydata=ydata_new, button=1)
240+
if not use_default_state:
241+
do_event(tool, 'on_key_release', key='control')
242+
assert tool.extents == (xdata_new, 135.0 - xdiff, ydata_new, 135.0 - ydiff)
243+
244+
93245
def test_ellipse():
94246
"""For ellipse, test out the key modifiers"""
95247
ax = get_ax()

lib/matplotlib/widgets.py

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1816,6 +1816,7 @@ def __init__(self, ax, onselect, useblit=False, button=None,
18161816
self._eventrelease = None
18171817
self._prev_event = None
18181818
self._state = set()
1819+
self._default_state = set()
18191820

18201821
eventpress = _api.deprecate_privatize_attribute("3.5")
18211822
eventrelease = _api.deprecate_privatize_attribute("3.5")
@@ -2897,20 +2898,48 @@ def _release(self, event):
28972898
def _onmove(self, event):
28982899
"""Motion notify event handler."""
28992900

2900-
2901+
state = self._state | self._default_state
29012902
# resize an existing shape
29022903
if self._active_handle and self._active_handle != 'C':
29032904
x0, x1, y0, y1 = self._extents_on_press
2904-
# Switch variables so that only x1 and/or y1 are updated on move.
2905-
if self._active_handle in ['W', 'SW', 'NW']:
2906-
x0, x1 = x1, event.xdata
2907-
if self._active_handle in ['S', 'SW', 'SE']:
2908-
y0, y1 = y1, event.ydata
2905+
if len(state) == 0:
2906+
# Switch variables so that only x1 and/or y1 are updated on move.
2907+
if self._active_handle in ['W', 'SW', 'NW']:
2908+
x0, x1 = x1, event.xdata
2909+
if self._active_handle in ['S', 'SW', 'SE']:
2910+
y0, y1 = y1, event.ydata
2911+
2912+
if self._active_handle in ['E', 'W'] + self._corner_order:
2913+
x1 = event.xdata
2914+
if self._active_handle in ['N', 'S'] + self._corner_order:
2915+
y1 = event.ydata
2916+
2917+
else:
2918+
dx = event.xdata - self._eventpress.xdata
2919+
dy = event.ydata - self._eventpress.ydata
2920+
2921+
sizepress = [x1 - x0, y1 - y0]
2922+
centerpress = [x0 + sizepress[0] / 2, y0 + sizepress[1] / 2]
2923+
center = centerpress
2924+
2925+
# from center
2926+
if 'center' in state:
2927+
if 'W' in self._active_handle:
2928+
dx = -dx
2929+
if 'S' in self._active_handle:
2930+
dy = -dy
2931+
dw = sizepress[0] / 2 + dx
2932+
dh = sizepress[1] / 2 + dy
2933+
2934+
# cancel changes in perpendicular direction
2935+
if self._active_handle in ['E', 'W']:
2936+
dh = sizepress[1] / 2
2937+
if self._active_handle in ['N', 'S']:
2938+
dw = sizepress[0] / 2
2939+
2940+
x0, x1, y0, y1 = (center[0] - dw, center[0] + dw,
2941+
center[1] - dh, center[1] + dh)
29092942

2910-
if self._active_handle in ['E', 'W'] + self._corner_order:
2911-
x1 = event.xdata
2912-
if self._active_handle in ['N', 'S'] + self._corner_order:
2913-
y1 = event.ydata
29142943

29152944
# move existing shape
29162945
elif (self._active_handle == 'C' or
@@ -2936,7 +2965,7 @@ def _onmove(self, event):
29362965
dy = (event.ydata - center[1]) / 2.
29372966

29382967
# square shape
2939-
if 'square' in self._state:
2968+
if 'square' in state:
29402969
dx_pix = abs(event.x - center_pix[0])
29412970
dy_pix = abs(event.y - center_pix[1])
29422971
if not dx_pix:
@@ -2948,7 +2977,7 @@ def _onmove(self, event):
29482977
dy *= maxd / (abs(dy_pix) + 1e-6)
29492978

29502979
# from center
2951-
if 'center' in self._state:
2980+
if 'center' in state:
29522981
dx *= 2
29532982
dy *= 2
29542983

0 commit comments

Comments
 (0)