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 78160ab

Browse filesBrowse files
committed
Allow Selectors to be dragged from anywhere within their patch
1 parent 01d3149 commit 78160ab
Copy full SHA for 78160ab

File tree

Expand file treeCollapse file tree

3 files changed

+60
-6
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+60
-6
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+
``select_whole_region``, 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('select_whole_region, new_center',
48+
[[True, (100, 100)],
49+
[False, (50, 50)]])
50+
def test_rectangle_drag(select_whole_region, new_center):
51+
ax = get_ax()
52+
53+
def onselect(epress, erelease):
54+
pass
55+
56+
tool = widgets.RectangleSelector(ax, onselect, interactive=True,
57+
select_whole_region=select_whole_region)
58+
# Create rectangle
59+
do_event(tool, 'press', xdata=0, ydata=0, button=1)
60+
do_event(tool, 'onmove', xdata=100, ydata=100, button=1)
61+
do_event(tool, 'release', xdata=100, ydata=100, button=1)
62+
assert tool.center == (50, 50)
63+
# Drag inside rectangle, but away from centre handle to make sure rectangle
64+
# is still moved
65+
do_event(tool, 'press', xdata=25, ydata=25, button=1)
66+
do_event(tool, 'onmove', xdata=75, ydata=75, button=1)
67+
do_event(tool, 'release', xdata=75, ydata=75, 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=175, button=1)
72+
do_event(tool, 'onmove', xdata=185, ydata=185, button=1)
73+
do_event(tool, 'release', xdata=185, ydata=185, button=1)
74+
assert tool.center == (180, 180)
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
+21-6Lines changed: 21 additions & 6 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+
select_whole_region=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+
select_whole_region : 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.select_whole_region = select_whole_region
22712277

22722278
if drawtype == 'none': # draw a line but make it invisible
22732279
drawtype = 'line'
@@ -2407,8 +2413,9 @@ def _onmove(self, event):
24072413
y2 = 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.select_whole_region and self._contains(event))) and
2418+
self._extents_on_press is not None):
24122419
x1, x2, y1, y2 = self._extents_on_press
24132420
dx = event.xdata - self.eventpress.xdata
24142421
dy = event.ydata - self.eventpress.ydata
@@ -2536,19 +2543,23 @@ def _set_active_handle(self, event):
25362543
e_idx, e_dist = self._edge_handles.closest(event.x, event.y)
25372544
m_idx, m_dist = self._center_handle.closest(event.x, event.y)
25382545

2539-
if 'move' in self.state:
2546+
contains = self._contains(event)
2547+
if 'move' in self.state or (self.select_whole_region and contains):
25402548
self.active_handle = 'C'
25412549
self._extents_on_press = self.extents
2542-
25432550
# Set active handle as closest handle, if mouse click is close enough.
25442551
elif m_dist < self.maxdist * 2:
2552+
# Prioritise center handle over other handles
25452553
self.active_handle = 'C'
2546-
elif c_dist > self.maxdist and e_dist > self.maxdist:
2554+
elif (c_dist > self.maxdist and e_dist > self.maxdist) or not contains:
2555+
# Not close to any handles
25472556
self.active_handle = None
25482557
return
25492558
elif c_dist < e_dist:
2559+
# Closest to a corner handle
25502560
self.active_handle = self._corner_order[c_idx]
25512561
else:
2562+
# Closest to an edge handle
25522563
self.active_handle = self._edge_order[e_idx]
25532564

25542565
# Save coordinates of rectangle at the start of handle movement.
@@ -2560,6 +2571,10 @@ def _set_active_handle(self, event):
25602571
y1, y2 = y2, event.ydata
25612572
self._extents_on_press = x1, x2, y1, y2
25622573

2574+
def _contains(self, event):
2575+
"""Return True if event is within the patch."""
2576+
return self.to_draw.contains(event, radius=0)[0]
2577+
25632578
@property
25642579
def geometry(self):
25652580
"""

0 commit comments

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