@@ -221,6 +221,34 @@ def __init__(self, manager, toolbar_id=DefaultToolbarID):
221221 manager , save_snapshot , toolbar_id = toolbar_id , fix_orientation = True
222222 )
223223
224+ def end_rect (self , filter , p0 , p1 ):
225+ """End rect: emit ``SIG_TOOL_JOB_FINISHED`` *synchronously* so the
226+ ``switch_to_default_tool`` listener restores the canvas cursor while
227+ we are still inside the mouse-release event handler chain — Qt then
228+ gets the chance to refresh the cursor on neighbouring widgets
229+ (axes, toolbar) before any nested event loop is started by the
230+ snapshot dialogs. The action function itself is deferred via a
231+ zero-delay timer so the modal ``ResizeDialog`` (and following
232+ dialogs) is not opened from inside the rubber-band ``mouseRelease``
233+ handler chain — otherwise Qt's implicit grab is left in an unclean
234+ state on Windows and the cross cursor used by the canvas during
235+ the drag remains "stuck" on neighbouring widgets until the mouse
236+ moves over them.
237+ """
238+ plot = filter .plot
239+ if self .fix_orientation :
240+ left , right = min (p0 .x (), p1 .x ()), max (p0 .x (), p1 .x ())
241+ top , bottom = min (p0 .y (), p1 .y ()), max (p0 .y (), p1 .y ())
242+ p0 , p1 = QC .QPointF (left , top ), QC .QPointF (right , bottom )
243+ # Synchronous: cursor is restored on the canvas now, while we are
244+ # still in the mouse-release handler chain.
245+ self .SIG_TOOL_JOB_FINISHED .emit ()
246+ if self .switch_to_default_tool :
247+ shape = self .get_last_final_shape ()
248+ plot .set_active_item (shape )
249+ # Deferred: open the dialogs after Qt has cleanly released the grab.
250+ QC .QTimer .singleShot (0 , lambda : self .action_func (plot , p0 , p1 ))
251+
224252
225253class HelpTool (CommandTool ):
226254 """ """
0 commit comments