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

Skip to content

Commit 6ee977c

Browse files
committed
Improve cursor icons with RectangleSelector
1 parent b78e4a1 commit 6ee977c

File tree

2 files changed

+46
-13
lines changed

2 files changed

+46
-13
lines changed

lib/matplotlib/widgets.py

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,10 @@ def ignore(self, event):
150150
# docstring inherited
151151
return super().ignore(event) or self.canvas is None
152152

153+
def _set_cursor(self, cursor):
154+
"""Update the canvas cursor."""
155+
self.ax.get_figure(root=True).canvas.set_cursor(cursor)
156+
153157

154158
class Button(AxesWidget):
155159
"""
@@ -2643,7 +2647,7 @@ def _handles_artists(self):
26432647
else:
26442648
return ()
26452649

2646-
def _set_cursor(self, enabled):
2650+
def _set_span_cursor(self, enabled):
26472651
"""Update the canvas cursor based on direction of the selector."""
26482652
if enabled:
26492653
cursor = (backend_tools.Cursors.RESIZE_HORIZONTAL
@@ -2652,7 +2656,7 @@ def _set_cursor(self, enabled):
26522656
else:
26532657
cursor = backend_tools.Cursors.POINTER
26542658

2655-
self.ax.get_figure(root=True).canvas.set_cursor(cursor)
2659+
self._set_cursor(cursor)
26562660

26572661
def connect_default_events(self):
26582662
# docstring inherited
@@ -2662,7 +2666,7 @@ def connect_default_events(self):
26622666

26632667
def _press(self, event):
26642668
"""Button press event handler."""
2665-
self._set_cursor(True)
2669+
self._set_span_cursor(True)
26662670
if self._interactive and self._selection_artist.get_visible():
26672671
self._set_active_handle(event)
26682672
else:
@@ -2712,7 +2716,7 @@ def direction(self, direction):
27122716

27132717
def _release(self, event):
27142718
"""Button release event handler."""
2715-
self._set_cursor(False)
2719+
self._set_span_cursor(False)
27162720

27172721
if not self._interactive:
27182722
self._selection_artist.set_visible(False)
@@ -2754,7 +2758,7 @@ def _hover(self, event):
27542758
return
27552759

27562760
_, e_dist = self._edge_handles.closest(event.x, event.y)
2757-
self._set_cursor(e_dist <= self.grab_range)
2761+
self._set_span_cursor(e_dist <= self.grab_range)
27582762

27592763
def _onmove(self, event):
27602764
"""Motion notify event handler."""
@@ -3278,10 +3282,24 @@ def _press(self, event):
32783282
self._rotation_on_press = self._rotation
32793283
self._set_aspect_ratio_correction()
32803284

3285+
match self._get_action():
3286+
case "rotate":
3287+
# TODO: set to a rotate cursor if possible?
3288+
pass
3289+
case "move":
3290+
self._set_cursor(backend_tools.cursors.MOVE)
3291+
case "resize":
3292+
# TODO: set to a resize cursor if possible?
3293+
pass
3294+
case "create":
3295+
self._set_cursor(backend_tools.cursors.SELECT_REGION)
3296+
3297+
32813298
return False
32823299

32833300
def _release(self, event):
32843301
"""Button release event handler."""
3302+
self._set_cursor(backend_tools.Cursors.POINTER)
32853303
if not self._interactive:
32863304
self._selection_artist.set_visible(False)
32873305

@@ -3325,9 +3343,23 @@ def _release(self, event):
33253343
self.update()
33263344
self._active_handle = None
33273345
self._extents_on_press = None
3328-
33293346
return False
33303347

3348+
def _get_action(self):
3349+
"""
3350+
Return one of "rotate", "move", "resize", "create"
3351+
"""
3352+
state = self._state
3353+
if 'rotate' in state and self._active_handle in self._corner_order:
3354+
return 'rotate'
3355+
elif self._active_handle == 'C':
3356+
return 'move'
3357+
elif self._active_handle:
3358+
return 'resize'
3359+
3360+
return 'create'
3361+
3362+
33313363
def _onmove(self, event):
33323364
"""
33333365
Motion notify event handler.
@@ -3342,12 +3374,10 @@ def _onmove(self, event):
33423374
# The calculations are done for rotation at zero: we apply inverse
33433375
# transformation to events except when we rotate and move
33443376
state = self._state
3345-
rotate = 'rotate' in state and self._active_handle in self._corner_order
3346-
move = self._active_handle == 'C'
3347-
resize = self._active_handle and not move
3377+
action = self._get_action()
33483378

33493379
xdata, ydata = self._get_data_coords(event)
3350-
if resize:
3380+
if action == "resize":
33513381
inv_tr = self._get_rotation_transform().inverted()
33523382
xdata, ydata = inv_tr.transform([xdata, ydata])
33533383
eventpress.xdata, eventpress.ydata = inv_tr.transform(
@@ -3367,7 +3397,7 @@ def _onmove(self, event):
33673397

33683398
x0, x1, y0, y1 = self._extents_on_press
33693399
# rotate an existing shape
3370-
if rotate:
3400+
if action == "rotate":
33713401
# calculate angle abc
33723402
a = (eventpress.xdata, eventpress.ydata)
33733403
b = self.center
@@ -3376,7 +3406,7 @@ def _onmove(self, event):
33763406
np.arctan2(a[1]-b[1], a[0]-b[0]))
33773407
self.rotation = np.rad2deg(self._rotation_on_press + angle)
33783408

3379-
elif resize:
3409+
elif action == "resize":
33803410
size_on_press = [x1 - x0, y1 - y0]
33813411
center = (x0 + size_on_press[0] / 2, y0 + size_on_press[1] / 2)
33823412

@@ -3427,7 +3457,7 @@ def _onmove(self, event):
34273457
sign = np.sign(xdata - x0)
34283458
x1 = x0 + sign * abs(y1 - y0) * self._aspect_ratio_correction
34293459

3430-
elif move:
3460+
elif action == "move":
34313461
x0, x1, y0, y1 = self._extents_on_press
34323462
dx = xdata - eventpress.xdata
34333463
dy = ydata - eventpress.ydata

lib/matplotlib/widgets.pyi

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ from .figure import Figure
66
from .lines import Line2D
77
from .patches import Polygon, Rectangle
88
from .text import Text
9+
from .backend_tools import Cursors
910

1011
import PIL.Image
1112

@@ -38,6 +39,7 @@ class AxesWidget(Widget):
3839
def canvas(self) -> FigureCanvasBase | None: ...
3940
def connect_event(self, event: Event, callback: Callable) -> None: ...
4041
def disconnect_events(self) -> None: ...
42+
def _set_cursor(self, cursor: Cursors) -> None: ...
4143

4244
class Button(AxesWidget):
4345
label: Text
@@ -398,6 +400,7 @@ class RectangleSelector(_SelectorWidget):
398400
minspany: float
399401
spancoords: Literal["data", "pixels"]
400402
grab_range: float
403+
_active_handle: None | Literal["C", "N", "NE", "E", "SE", "S", "SW", "W", "NW"]
401404
def __init__(
402405
self,
403406
ax: Axes,

0 commit comments

Comments
 (0)