Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 0c35d09

Browse filesBrowse files
committed
Allow Selectors to be dragged from anywhere within their patch
1 parent 906cae4 commit 0c35d09
Copy full SHA for 0c35d09

File tree

Expand file treeCollapse file tree

3 files changed

+65
-7
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+65
-7
lines changed
+9Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Dragging selectors
2+
------------------
3+
4+
The `~matplotlib.widgets.RectangleSelector` and
5+
`~matplotlib.widgets.EllipseSelector` have a new keyword argument,
6+
``drag_from_anywhere``, which when set to `True` allows you to click and drag
7+
from anywhere inside the selector to move it. Previously it was only possible
8+
to move it by either activating the move modifier button, or clicking on the
9+
central handle.

‎lib/matplotlib/tests/test_widgets.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_widgets.py
+30Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,36 @@ def test_rectangle_selector():
4444
check_rectangle(rectprops=dict(fill=True))
4545

4646

47+
@pytest.mark.parametrize('drag_from_anywhere, new_center',
48+
[[True, (60, 75)],
49+
[False, (30, 20)]])
50+
def test_rectangle_drag(drag_from_anywhere, new_center):
51+
ax = get_ax()
52+
53+
def onselect(epress, erelease):
54+
pass
55+
56+
tool = widgets.RectangleSelector(ax, onselect, interactive=True,
57+
drag_from_anywhere=drag_from_anywhere)
58+
# Create rectangle
59+
do_event(tool, 'press', xdata=0, ydata=10, button=1)
60+
do_event(tool, 'onmove', xdata=100, ydata=120, button=1)
61+
do_event(tool, 'release', xdata=100, ydata=120, button=1)
62+
assert tool.center == (50, 65)
63+
# Drag inside rectangle, but away from centre handle to make sure rectangle
64+
# is still moved (if drag_from_anywhere == True)
65+
do_event(tool, 'press', xdata=25, ydata=15, button=1)
66+
do_event(tool, 'onmove', xdata=35, ydata=25, button=1)
67+
do_event(tool, 'release', xdata=35, ydata=25, button=1)
68+
assert tool.center == new_center
69+
# Check that in both cases, dragging outside the rectangle draws a new
70+
# rectangle
71+
do_event(tool, 'press', xdata=175, ydata=185, button=1)
72+
do_event(tool, 'onmove', xdata=185, ydata=195, button=1)
73+
do_event(tool, 'release', xdata=185, ydata=195, button=1)
74+
assert tool.center == (180, 190)
75+
76+
4777
def test_ellipse():
4878
"""For ellipse, test out the key modifiers"""
4979
ax = get_ax()

‎lib/matplotlib/widgets.py

Copy file name to clipboardExpand all lines: lib/matplotlib/widgets.py
+26-7Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2189,7 +2189,8 @@ def __init__(self, ax, onselect, drawtype='box',
21892189
minspanx=0, minspany=0, useblit=False,
21902190
lineprops=None, rectprops=None, spancoords='data',
21912191
button=None, maxdist=10, marker_props=None,
2192-
interactive=False, state_modifier_keys=None):
2192+
interactive=False, state_modifier_keys=None,
2193+
drag_from_anywhere=False):
21932194
r"""
21942195
Parameters
21952196
----------
@@ -2261,13 +2262,18 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent)
22612262
default: "ctrl".
22622263
22632264
"square" and "center" can be combined.
2265+
2266+
drag_from_anywhere : bool, optional
2267+
If `True`, the widget can be moved by clicking anywhere within
2268+
its bounds.
22642269
"""
22652270
super().__init__(ax, onselect, useblit=useblit, button=button,
22662271
state_modifier_keys=state_modifier_keys)
22672272

22682273
self.to_draw = None
22692274
self.visible = True
22702275
self.interactive = interactive
2276+
self.drag_from_anywhere = drag_from_anywhere
22712277

22722278
if drawtype == 'none': # draw a line but make it invisible
22732279
drawtype = 'line'
@@ -2407,8 +2413,9 @@ def _onmove(self, event):
24072413
y1 = event.ydata
24082414

24092415
# move existing shape
2410-
elif (('move' in self.state or self.active_handle == 'C')
2411-
and self._extents_on_press is not None):
2416+
elif (('move' in self.state or self.active_handle == 'C' or
2417+
(self.drag_from_anywhere and self._contains(event))) and
2418+
self._extents_on_press is not None):
24122419
x0, x1, y0, y1 = self._extents_on_press
24132420
dx = event.xdata - self.eventpress.xdata
24142421
dy = event.ydata - self.eventpress.ydata
@@ -2539,16 +2546,24 @@ def _set_active_handle(self, event):
25392546
if 'move' in self.state:
25402547
self.active_handle = 'C'
25412548
self._extents_on_press = self.extents
2542-
25432549
# Set active handle as closest handle, if mouse click is close enough.
25442550
elif m_dist < self.maxdist * 2:
2551+
# Prioritise center handle over other handles
25452552
self.active_handle = 'C'
2546-
elif c_dist > self.maxdist and e_dist > self.maxdist:
2547-
self.active_handle = None
2548-
return
2553+
elif (c_dist > self.maxdist and e_dist > self.maxdist):
2554+
# Not close to any handles
2555+
if self.drag_from_anywhere and self._contains(event):
2556+
# Check if we've clicked inside the region
2557+
self.active_handle = 'C'
2558+
self._extents_on_press = self.extents
2559+
else:
2560+
self.active_handle = None
2561+
return
25492562
elif c_dist < e_dist:
2563+
# Closest to a corner handle
25502564
self.active_handle = self._corner_order[c_idx]
25512565
else:
2566+
# Closest to an edge handle
25522567
self.active_handle = self._edge_order[e_idx]
25532568

25542569
# Save coordinates of rectangle at the start of handle movement.
@@ -2560,6 +2575,10 @@ def _set_active_handle(self, event):
25602575
y0, y1 = y1, event.ydata
25612576
self._extents_on_press = x0, x1, y0, y1
25622577

2578+
def _contains(self, event):
2579+
"""Return True if event is within the patch."""
2580+
return self.to_draw.contains(event, radius=0)[0]
2581+
25632582
@property
25642583
def geometry(self):
25652584
"""

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.