1111
1212from contextlib import ExitStack
1313import copy
14+ import enum
1415import itertools
1516from numbers import Integral , Number
1617
@@ -150,6 +151,10 @@ def ignore(self, event):
150151 # docstring inherited
151152 return super ().ignore (event ) or self .canvas is None
152153
154+ def _set_cursor (self , cursor ):
155+ """Update the canvas cursor."""
156+ self .ax .get_figure (root = True ).canvas .set_cursor (cursor )
157+
153158
154159class Button (AxesWidget ):
155160 """
@@ -2645,7 +2650,7 @@ def _handles_artists(self):
26452650 else :
26462651 return ()
26472652
2648- def _set_cursor (self , enabled ):
2653+ def _set_span_cursor (self , * , enabled ):
26492654 """Update the canvas cursor based on direction of the selector."""
26502655 if enabled :
26512656 cursor = (backend_tools .Cursors .RESIZE_HORIZONTAL
@@ -2654,7 +2659,7 @@ def _set_cursor(self, enabled):
26542659 else :
26552660 cursor = backend_tools .Cursors .POINTER
26562661
2657- self .ax . get_figure ( root = True ). canvas . set_cursor (cursor )
2662+ self ._set_cursor (cursor )
26582663
26592664 def connect_default_events (self ):
26602665 # docstring inherited
@@ -2664,7 +2669,7 @@ def connect_default_events(self):
26642669
26652670 def _press (self , event ):
26662671 """Button press event handler."""
2667- self ._set_cursor ( True )
2672+ self ._set_span_cursor ( enabled = True )
26682673 if self ._interactive and self ._selection_artist .get_visible ():
26692674 self ._set_active_handle (event )
26702675 else :
@@ -2714,7 +2719,7 @@ def direction(self, direction):
27142719
27152720 def _release (self , event ):
27162721 """Button release event handler."""
2717- self ._set_cursor ( False )
2722+ self ._set_span_cursor ( enabled = False )
27182723
27192724 if not self ._interactive :
27202725 self ._selection_artist .set_visible (False )
@@ -2756,7 +2761,7 @@ def _hover(self, event):
27562761 return
27572762
27582763 _ , e_dist = self ._edge_handles .closest (event .x , event .y )
2759- self ._set_cursor ( e_dist <= self .grab_range )
2764+ self ._set_span_cursor ( enabled = e_dist <= self .grab_range )
27602765
27612766 def _onmove (self , event ):
27622767 """Motion notify event handler."""
@@ -3147,6 +3152,13 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent)
31473152 """
31483153
31493154
3155+ class _RectangleSelectorAction (enum .Enum ):
3156+ ROTATE = enum .auto ()
3157+ MOVE = enum .auto ()
3158+ RESIZE = enum .auto ()
3159+ CREATE = enum .auto ()
3160+
3161+
31503162@_docstring .Substitution (_RECTANGLESELECTOR_PARAMETERS_DOCSTRING .replace (
31513163 '__ARTIST_NAME__' , 'rectangle' ))
31523164class RectangleSelector (_SelectorWidget ):
@@ -3280,10 +3292,23 @@ def _press(self, event):
32803292 self ._rotation_on_press = self ._rotation
32813293 self ._set_aspect_ratio_correction ()
32823294
3295+ match self ._get_action ():
3296+ case _RectangleSelectorAction .ROTATE :
3297+ # TODO: set to a rotate cursor if possible?
3298+ pass
3299+ case _RectangleSelectorAction .MOVE :
3300+ self ._set_cursor (backend_tools .cursors .MOVE )
3301+ case _RectangleSelectorAction .RESIZE :
3302+ # TODO: set to a resize cursor if possible?
3303+ pass
3304+ case _RectangleSelectorAction .CREATE :
3305+ self ._set_cursor (backend_tools .cursors .SELECT_REGION )
3306+
32833307 return False
32843308
32853309 def _release (self , event ):
32863310 """Button release event handler."""
3311+ self ._set_cursor (backend_tools .Cursors .POINTER )
32873312 if not self ._interactive :
32883313 self ._selection_artist .set_visible (False )
32893314
@@ -3327,9 +3352,20 @@ def _release(self, event):
33273352 self .update ()
33283353 self ._active_handle = None
33293354 self ._extents_on_press = None
3330-
33313355 return False
33323356
3357+ def _get_action (self ):
3358+ state = self ._state
3359+ if 'rotate' in state and self ._active_handle in self ._corner_order :
3360+ return _RectangleSelectorAction .ROTATE
3361+ elif self ._active_handle == 'C' :
3362+ return _RectangleSelectorAction .MOVE
3363+ elif self ._active_handle :
3364+ return _RectangleSelectorAction .RESIZE
3365+
3366+ return _RectangleSelectorAction .CREATE
3367+
3368+
33333369 def _onmove (self , event ):
33343370 """
33353371 Motion notify event handler.
@@ -3344,12 +3380,10 @@ def _onmove(self, event):
33443380 # The calculations are done for rotation at zero: we apply inverse
33453381 # transformation to events except when we rotate and move
33463382 state = self ._state
3347- rotate = 'rotate' in state and self ._active_handle in self ._corner_order
3348- move = self ._active_handle == 'C'
3349- resize = self ._active_handle and not move
3383+ action = self ._get_action ()
33503384
33513385 xdata , ydata = self ._get_data_coords (event )
3352- if resize :
3386+ if action == _RectangleSelectorAction . RESIZE :
33533387 inv_tr = self ._get_rotation_transform ().inverted ()
33543388 xdata , ydata = inv_tr .transform ([xdata , ydata ])
33553389 eventpress .xdata , eventpress .ydata = inv_tr .transform (
@@ -3369,7 +3403,7 @@ def _onmove(self, event):
33693403
33703404 x0 , x1 , y0 , y1 = self ._extents_on_press
33713405 # rotate an existing shape
3372- if rotate :
3406+ if action == _RectangleSelectorAction . ROTATE :
33733407 # calculate angle abc
33743408 a = (eventpress .xdata , eventpress .ydata )
33753409 b = self .center
@@ -3378,7 +3412,7 @@ def _onmove(self, event):
33783412 np .arctan2 (a [1 ]- b [1 ], a [0 ]- b [0 ]))
33793413 self .rotation = np .rad2deg (self ._rotation_on_press + angle )
33803414
3381- elif resize :
3415+ elif action == _RectangleSelectorAction . RESIZE :
33823416 size_on_press = [x1 - x0 , y1 - y0 ]
33833417 center = (x0 + size_on_press [0 ] / 2 , y0 + size_on_press [1 ] / 2 )
33843418
@@ -3429,7 +3463,7 @@ def _onmove(self, event):
34293463 sign = np .sign (xdata - x0 )
34303464 x1 = x0 + sign * abs (y1 - y0 ) * self ._aspect_ratio_correction
34313465
3432- elif move :
3466+ elif action == _RectangleSelectorAction . MOVE :
34333467 x0 , x1 , y0 , y1 = self ._extents_on_press
34343468 dx = xdata - eventpress .xdata
34353469 dy = ydata - eventpress .ydata
0 commit comments