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

Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
60 changes: 47 additions & 13 deletions lib/matplotlib/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

from contextlib import ExitStack
import copy
import enum
import itertools
from numbers import Integral, Number

Expand Down Expand Up @@ -150,6 +151,10 @@ def ignore(self, event):
# docstring inherited
return super().ignore(event) or self.canvas is None

def _set_cursor(self, cursor):
"""Update the canvas cursor."""
self.ax.get_figure(root=True).canvas.set_cursor(cursor)


class Button(AxesWidget):
"""
Expand Down Expand Up @@ -2643,7 +2648,7 @@ def _handles_artists(self):
else:
return ()

def _set_cursor(self, enabled):
def _set_span_cursor(self, *, enabled):
"""Update the canvas cursor based on direction of the selector."""
if enabled:
cursor = (backend_tools.Cursors.RESIZE_HORIZONTAL
Expand All @@ -2652,7 +2657,7 @@ def _set_cursor(self, enabled):
else:
cursor = backend_tools.Cursors.POINTER

self.ax.get_figure(root=True).canvas.set_cursor(cursor)
self._set_cursor(cursor)

def connect_default_events(self):
# docstring inherited
Expand All @@ -2662,7 +2667,7 @@ def connect_default_events(self):

def _press(self, event):
"""Button press event handler."""
self._set_cursor(True)
self._set_span_cursor(enabled=True)
if self._interactive and self._selection_artist.get_visible():
self._set_active_handle(event)
else:
Expand Down Expand Up @@ -2712,7 +2717,7 @@ def direction(self, direction):

def _release(self, event):
"""Button release event handler."""
self._set_cursor(False)
self._set_span_cursor(enabled=False)

if not self._interactive:
self._selection_artist.set_visible(False)
Expand Down Expand Up @@ -2754,7 +2759,7 @@ def _hover(self, event):
return

_, e_dist = self._edge_handles.closest(event.x, event.y)
self._set_cursor(e_dist <= self.grab_range)
self._set_span_cursor(enabled=e_dist <= self.grab_range)

def _onmove(self, event):
"""Motion notify event handler."""
Expand Down Expand Up @@ -3145,6 +3150,13 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent)
"""


class _RectangleSelectorAction(enum.Enum):
ROTATE = enum.auto()
MOVE = enum.auto()
RESIZE = enum.auto()
CREATE = enum.auto()


@_docstring.Substitution(_RECTANGLESELECTOR_PARAMETERS_DOCSTRING.replace(
'__ARTIST_NAME__', 'rectangle'))
class RectangleSelector(_SelectorWidget):
Expand Down Expand Up @@ -3278,10 +3290,23 @@ def _press(self, event):
self._rotation_on_press = self._rotation
self._set_aspect_ratio_correction()

match self._get_action():
case _RectangleSelectorAction.ROTATE:
# TODO: set to a rotate cursor if possible?
pass
case _RectangleSelectorAction.MOVE:
self._set_cursor(backend_tools.cursors.MOVE)
case _RectangleSelectorAction.RESIZE:
# TODO: set to a resize cursor if possible?
pass
Comment on lines +3299 to +3301
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need diagonal resize here, or something different? Resize usually needs two directions (or four if you're counting straight and diagonal together.)

case _RectangleSelectorAction.CREATE:
self._set_cursor(backend_tools.cursors.SELECT_REGION)

return False

def _release(self, event):
"""Button release event handler."""
self._set_cursor(backend_tools.Cursors.POINTER)
if not self._interactive:
self._selection_artist.set_visible(False)

Expand Down Expand Up @@ -3325,9 +3350,20 @@ def _release(self, event):
self.update()
self._active_handle = None
self._extents_on_press = None

return False

def _get_action(self):
state = self._state
if 'rotate' in state and self._active_handle in self._corner_order:
return _RectangleSelectorAction.ROTATE
elif self._active_handle == 'C':
return _RectangleSelectorAction.MOVE
elif self._active_handle:
return _RectangleSelectorAction.RESIZE

return _RectangleSelectorAction.CREATE


def _onmove(self, event):
"""
Motion notify event handler.
Expand All @@ -3342,12 +3378,10 @@ def _onmove(self, event):
# The calculations are done for rotation at zero: we apply inverse
# transformation to events except when we rotate and move
state = self._state
rotate = 'rotate' in state and self._active_handle in self._corner_order
move = self._active_handle == 'C'
resize = self._active_handle and not move
action = self._get_action()

xdata, ydata = self._get_data_coords(event)
if resize:
if action == _RectangleSelectorAction.RESIZE:
inv_tr = self._get_rotation_transform().inverted()
xdata, ydata = inv_tr.transform([xdata, ydata])
eventpress.xdata, eventpress.ydata = inv_tr.transform(
Expand All @@ -3367,7 +3401,7 @@ def _onmove(self, event):

x0, x1, y0, y1 = self._extents_on_press
# rotate an existing shape
if rotate:
if action == _RectangleSelectorAction.ROTATE:
# calculate angle abc
a = (eventpress.xdata, eventpress.ydata)
b = self.center
Expand All @@ -3376,7 +3410,7 @@ def _onmove(self, event):
np.arctan2(a[1]-b[1], a[0]-b[0]))
self.rotation = np.rad2deg(self._rotation_on_press + angle)

elif resize:
elif action == _RectangleSelectorAction.RESIZE:
size_on_press = [x1 - x0, y1 - y0]
center = (x0 + size_on_press[0] / 2, y0 + size_on_press[1] / 2)

Expand Down Expand Up @@ -3427,7 +3461,7 @@ def _onmove(self, event):
sign = np.sign(xdata - x0)
x1 = x0 + sign * abs(y1 - y0) * self._aspect_ratio_correction

elif move:
elif action == _RectangleSelectorAction.MOVE:
x0, x1, y0, y1 = self._extents_on_press
dx = xdata - eventpress.xdata
dy = ydata - eventpress.ydata
Expand Down
4 changes: 4 additions & 0 deletions lib/matplotlib/widgets.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ from .figure import Figure
from .lines import Line2D
from .patches import Polygon, Rectangle
from .text import Text
from .backend_tools import Cursors

import PIL.Image

Expand Down Expand Up @@ -38,6 +39,7 @@ class AxesWidget(Widget):
def canvas(self) -> FigureCanvasBase | None: ...
def connect_event(self, event: Event, callback: Callable) -> None: ...
def disconnect_events(self) -> None: ...
def _set_cursor(self, cursor: Cursors) -> None: ...

class Button(AxesWidget):
label: Text
Expand Down Expand Up @@ -335,6 +337,7 @@ class SpanSelector(_SelectorWidget):
_props: dict[str, Any] | None = ...,
_init: bool = ...,
) -> None: ...
def _set_span_cursor(self, *, enabled: bool) -> None: ...
def connect_default_events(self) -> None: ...
@property
def direction(self) -> Literal["horizontal", "vertical"]: ...
Expand Down Expand Up @@ -398,6 +401,7 @@ class RectangleSelector(_SelectorWidget):
minspany: float
spancoords: Literal["data", "pixels"]
grab_range: float
_active_handle: None | Literal["C", "N", "NE", "E", "SE", "S", "SW", "W", "NW"]
def __init__(
self,
ax: Axes,
Expand Down
Loading