Skip to content

Conversation

timhoffm
Copy link
Member

@timhoffm timhoffm commented Sep 20, 2025

First step towards improving blitting - towards #30503 and fixing #30575.

This PR proposes a minimal low-level API to store background information on the canvas.
It also adds higher-level methods save_blit_background() and load_blit_background() to AxesWidgets. Child classes can execrise managed background storage though this API.

I specifically would like to have feedback on the solution idea and API design. Note: This is not intended to be a solution for everything right away, but I would like to learn more on blitting through the case of widgets.

Open topics

  • Check whether widgets act reasonably if the background is pulled under them. - As implemented now, they won't crash, but we must ensure that they can recover the background. It may be (to be checked) that they just loose the background.
  • We still need to refactor useblit from statically including canvas blitting capability to dynamic handling.

Edit: Tested the following examples all with blitting enabled, and they work as expected (after closing the figure, plt.figure(fig); plt.show() recrates the figure and the widgets continue to work.

@timhoffm
Copy link
Member Author

timhoffm commented Sep 26, 2025

Recent update/commit: Support blitting with changing canvas in some widgets

Approach depends a bit on the widget, but generally:

  • self._useblit only stores the flag passed to __init__
  • canvas.supports_blit is checked dynamically when needed
  • we sometimes have the property sef.useblit, which is the effective
    value of flag and capability
  • if animated artists are used, that state is set during
    __init__ based on the flag. This relies on the fact that
    the flag cannot be changed later, and that the value of
    "animated" does not play a role for drawing canvases without blit support.

Supported:

  • Button
  • _SelectorWidget
  • SpanSelector
  • ToolLineHandles
  • ToolHandles
  • RectangleSelector
  • LassoSelector
  • PolygonSelector

Not supported yet:

  • CheckButtons
  • RadioButtons
  • Cursor
  • MultiCursor
  • Lasso

This should already fix #30575 as Selector widgets are covered.

Note: It's probably best to review the commits individually.

@timhoffm timhoffm force-pushed the safe-blit branch 2 times, most recently from b418b60 to f7ca7cc Compare September 26, 2025 10:21
@timhoffm timhoffm marked this pull request as ready for review September 26, 2025 10:46
@tacaswell
Copy link
Member

Drat, I did not re-look at this PR before I opened #30605

@timhoffm
Copy link
Member Author

timhoffm commented Oct 3, 2025

Rebased.

@timhoffm
Copy link
Member Author

timhoffm commented Oct 3, 2025

@tacaswell How do we want to continue here? I consider this PR complete for the handled widgets, and suggest that we review and merge it. The remaining widgets should be handled in a follow-up PR to keep this PR more manageable.

Here's a summary of this PR approach:

  • First commit: Create an infrastructure that safely handles background images by storing them on the canvas and defining an API in a way that there is no guarantee a background image is available. Consumers can try to obtain a background but must gracefully handle the case that no background is available.
    This is the basis to be able to reasonably handle blit-backgrounds while swapping canvas. Apply this to widgets that store backgrounds.
  • Second commit: Fix dynamic changes in useblit on the individual Widgets case-by-case. Since logic slightly differs between widgets, we have to think them through individually. The list of currently fixed widgets is in FIX: Make widget blitting compatible with swapped canvas #30591 (comment).

"""Update the canvas cursor."""
self.ax.get_figure(root=True).canvas.set_cursor(cursor)

def _save_blit_background(self, background):
Copy link
Member

Choose a reason for hiding this comment

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

Do we want to pass the bounding box in instead?

Copy link
Member Author

Choose a reason for hiding this comment

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

For symmetry reasons and to keep the changes within the PR minimal, I would leave the complete handling to the individual widgets for now.

They do

self._save_blit_background(self.canvas.copy_from_bbox(self.ax.bbox))

and

background = self._load_blit_background()
if background is not None:
    self.canvas.restore_region(background)

It may be reasonable to refactor in a follow-up and move that logic to the base class.

@timhoffm timhoffm force-pushed the safe-blit branch 2 times, most recently from 30ca550 to 87fa6db Compare October 3, 2025 18:15
@tacaswell tacaswell added this to the v3.11.0 milestone Oct 3, 2025
@tacaswell tacaswell added the Release critical For bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions. label Oct 3, 2025
Approach depends a bit on the widget, but generally:
- `self._useblit` only stores the flag passed to __init__
- canvas.supports_blit is checked dynamically when needed
- we sometimes have the property `sef.useblit`, which is the effective
value of flag and capability
- if animated artists are needed that state is set during
  __init__ based on the flag. This relies on the fact that
  the flag cannot be changed later, and that the value of
  "animated" does not play a role for drawing canvases without blit support.

Supported:
- Button
- _SelectorWidget
- SpanSelector
- ToolLineHandles
- ToolHandles
- RectangleSelector
- LassoSelector
- PolygonSelector

Not supported yet:
- CheckButtons
- RadioButtons
- Cursor
- MultiCursor
- Lasso
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Release critical For bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions. status: needs review topic: canvas and figure manager topic: widgets/UI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants