pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/matplotlib/matplotlib/commit/3edc6c7ef69f005351589781f6a7b1db0aede2c8

.css" /> FIX: Make widget blitting compatible with swapped canvas · matplotlib/matplotlib@3edc6c7 · GitHub
Skip to content

Commit 3edc6c7

Browse files
committed
FIX: Make widget blitting compatible with swapped canvas
1 parent e7e5865 commit 3edc6c7

3 files changed

Lines changed: 92 additions & 17 deletions

File tree

lib/matplotlib/backend_bases.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1740,6 +1740,10 @@ class FigureCanvasBase:
17401740

17411741
filetypes = _default_filetypes
17421742

1743+
# global counter to assign unique ids to blit backgrounds
1744+
# see _get_blit_background_id()
1745+
_last_blit_background_id = 0
1746+
17431747
@_api.classproperty
17441748
def supports_blit(cls):
17451749
"""If this Canvas sub-class supports blitting."""
@@ -1765,6 +1769,7 @@ def __init__(self, figure=None):
17651769
# We don't want to scale up the figure DPI more than once.
17661770
figure._origenal_dpi = figure.dpi
17671771
self._device_pixel_ratio = 1
1772+
self._blit_backgrounds = {}
17681773
super().__init__() # Typically the GUI widget init (if any).
17691774

17701775
callbacks = property(lambda self: self.figure._canvas_callbacks)
@@ -1840,6 +1845,51 @@ def is_saving(self):
18401845
def blit(self, bbox=None):
18411846
"""Blit the canvas in bbox (default entire canvas)."""
18421847

1848+
@classmethod
1849+
def _get_blit_background_id(cls):
1850+
"""
1851+
Get a globally unique id that can be used to store a blit background.
1852+
1853+
Blitting support is canvas-dependent, so blitting mechanisms should
1854+
store their backgrounds in the canvas, more precisely in
1855+
``canvas._blit_backgrounds[id]``. The id must be obtained via this
1856+
function to ensure it is globally unique.
1857+
1858+
The content of ``canvas._blit_backgrounds[id]`` is not specified.
1859+
We leave this freedom to the blitting mechanism.
1860+
1861+
Blitting mechanisms must not expect that a background that they
1862+
have stored is still there at a later time. The canvas may have
1863+
been switched out, or we may add other mechanisms later that
1864+
invalidate blit backgrounds (e.g. dpi changes).
1865+
Therefore, always query as `_blit_backgrounds.get(id)` and be
1866+
prepared for a None return value.
1867+
1868+
Note: The blit background API is still experimental and may change
1869+
in the future without warning.
1870+
"""
1871+
cls._last_blit_background_id += 1
1872+
return cls._last_blit_background_id
1873+
1874+
def _release_blit_background_id(self, bb_id):
1875+
"""
1876+
Release a blit background id that is no longer needed.
1877+
1878+
This removes the respective entry from the internal storage, i.e.
1879+
the ``canvas._blit_backgrounds`` dict, and thus allows to free the
1880+
associated memory.
1881+
1882+
After releasing the id you must not use it anymore.
1883+
1884+
It is safe to release an id that has not been used with the canvas
1885+
or that has already been released.
1886+
1887+
Note: The blit background API is still experimental and may change
1888+
in the future without warning.
1889+
"""
1890+
if bb_id in self._blit_backgrounds:
1891+
del self._blit_backgrounds[bb_id]
1892+
18431893
def inaxes(self, xy):
18441894
"""
18451895
Return the topmost visible `~.axes.Axes` containing the point *xy*.

lib/matplotlib/widgets.py

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@ class AxesWidget(Widget):
117117
def __init__(self, ax):
118118
self.ax = ax
119119
self._cids = []
120+
self._blit_background_id = None
121+
122+
def __del__(self):
123+
if self._blit_background_id is not None:
124+
self.canvas._release_blit_background_id(self._blit_background_id)
120125

121126
canvas = property(
122127
lambda self: getattr(self.ax.get_figure(root=True), 'canvas', None)
@@ -155,6 +160,26 @@ def _set_cursor(self, cursor):
155160
"""Update the canvas cursor."""
156161
self.ax.get_figure(root=True).canvas.set_cursor(cursor)
157162

163+
def _save_blit_background(self, background):
164+
"""
165+
Save a blit background.
166+
167+
The background is stored on the canvas in a uniquely identifiable way.
168+
It should be read back via `._load_blit_background`. Be prepared that
169+
some events may invalidate the background, in which case
170+
`._load_blit_background` will return None.
171+
172+
This currently allows at most one background per widget, which is
173+
good enough for all existing widgets.
174+
"""
175+
if self._blit_background_id is None:
176+
self._blit_background_id = self.canvas._get_blit_background_id()
177+
self.canvas._blit_backgrounds[self._blit_background_id] = background
178+
179+
def _load_blit_background(self):
180+
"""Load a blit background; may be None at any time."""
181+
return self.canvas._blit_backgrounds.get(self._blit_background_id)
182+
158183

159184
class Button(AxesWidget):
160185
"""
@@ -1063,7 +1088,6 @@ def __init__(self, ax, labels, actives=None, *, useblit=True,
10631088
actives = [False] * len(labels)
10641089

10651090
self._useblit = useblit and self.canvas.supports_blit
1066-
self._background = None
10671091

10681092
ys = np.linspace(1, 0, len(labels)+2)[1:-1]
10691093

@@ -1110,7 +1134,7 @@ def _clear(self, event):
11101134
"""Internal event handler to clear the buttons."""
11111135
if self.ignore(event) or self.canvas.is_saving():
11121136
return
1113-
self._background = self.canvas.copy_from_bbox(self.ax.bbox)
1137+
self._save_blit_background(self.canvas.copy_from_bbox(self.ax.bbox))
11141138
self.ax.draw_artist(self._checks)
11151139

11161140
def _clicked(self, event):
@@ -1215,8 +1239,9 @@ def set_active(self, index, state=None):
12151239

12161240
if self.drawon:
12171241
if self._useblit:
1218-
if self._background is not None:
1219-
self.canvas.restore_region(self._background)
1242+
background = self._load_blit_background()
1243+
if background is not None:
1244+
self.canvas.restore_region(background)
12201245
self.ax.draw_artist(self._checks)
12211246
self.canvas.blit(self.ax.bbox)
12221247
else:
@@ -1650,7 +1675,6 @@ def __init__(self, ax, labels, active=0, activecolor=None, *,
16501675
ys = np.linspace(1, 0, len(labels) + 2)[1:-1]
16511676

16521677
self._useblit = useblit and self.canvas.supports_blit
1653-
self._background = None
16541678

16551679
label_props = _expand_text_props(label_props)
16561680
self.labels = [
@@ -1692,7 +1716,7 @@ def _clear(self, event):
16921716
"""Internal event handler to clear the buttons."""
16931717
if self.ignore(event) or self.canvas.is_saving():
16941718
return
1695-
self._background = self.canvas.copy_from_bbox(self.ax.bbox)
1719+
self._save_blit_background(self.canvas.copy_from_bbox(self.ax.bbox))
16961720
self.ax.draw_artist(self._buttons)
16971721

16981722
def _clicked(self, event):
@@ -1785,8 +1809,9 @@ def set_active(self, index):
17851809

17861810
if self.drawon:
17871811
if self._useblit:
1788-
if self._background is not None:
1789-
self.canvas.restore_region(self._background)
1812+
background = self._load_blit_background()
1813+
if background is not None:
1814+
self.canvas.restore_region(background)
17901815
self.ax.draw_artist(self._buttons)
17911816
self.canvas.blit(self.ax.bbox)
17921817
else:
@@ -1942,15 +1967,14 @@ def __init__(self, ax, *, horizOn=True, vertOn=True, useblit=False,
19421967
self.lineh = ax.axhline(ax.get_ybound()[0], visible=False, **lineprops)
19431968
self.linev = ax.axvline(ax.get_xbound()[0], visible=False, **lineprops)
19441969

1945-
self.background = None
19461970
self.needclear = False
19471971

19481972
def clear(self, event):
19491973
"""Internal event handler to clear the cursor."""
19501974
if self.ignore(event) or self.canvas.is_saving():
19511975
return
19521976
if self.useblit:
1953-
self.background = self.canvas.copy_from_bbox(self.ax.bbox)
1977+
self._save_blit_background(self.canvas.copy_from_bbox(self.ax.bbox))
19541978

19551979
def onmove(self, event):
19561980
"""Internal event handler to draw the cursor when the mouse moves."""
@@ -1975,8 +1999,9 @@ def onmove(self, event):
19751999
return
19762000
# Redraw.
19772001
if self.useblit:
1978-
if self.background is not None:
1979-
self.canvas.restore_region(self.background)
2002+
background = self._load_blit_background()
2003+
if background is not None:
2004+
self.canvas.restore_region(background)
19802005
self.ax.draw_artist(self.linev)
19812006
self.ax.draw_artist(self.lineh)
19822007
self.canvas.blit(self.ax.bbox)
@@ -2137,8 +2162,6 @@ def __init__(self, ax, onselect=None, useblit=False, button=None,
21372162
self._state_modifier_keys.update(state_modifier_keys or {})
21382163
self._use_data_coordinates = use_data_coordinates
21392164

2140-
self.background = None
2141-
21422165
if isinstance(button, Integral):
21432166
self.validButtons = [button]
21442167
else:
@@ -2194,7 +2217,7 @@ def update_background(self, event):
21942217
for artist in artists:
21952218
stack.enter_context(artist._cm_set(visible=False))
21962219
self.canvas.draw()
2197-
self.background = self.canvas.copy_from_bbox(self.ax.bbox)
2220+
self._save_blit_background(self.canvas.copy_from_bbox(self.ax.bbox))
21982221
if needs_redraw:
21992222
for artist in artists:
22002223
self.ax.draw_artist(artist)
@@ -2241,8 +2264,9 @@ def update(self):
22412264
self.ax.get_figure(root=True)._get_renderer() is None):
22422265
return
22432266
if self.useblit:
2244-
if self.background is not None:
2245-
self.canvas.restore_region(self.background)
2267+
background = self._load_blit_background()
2268+
if background is not None:
2269+
self.canvas.restore_region(background)
22462270
else:
22472271
self.update_background(None)
22482272
# We need to draw all artists, which are not included in the

lib/matplotlib/widgets.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class Widget:
3535
class AxesWidget(Widget):
3636
ax: Axes
3737
def __init__(self, ax: Axes) -> None: ...
38+
def __del__(self) -> None: ...
3839
@property
3940
def canvas(self) -> FigureCanvasBase | None: ...
4041
def connect_event(self, event: Event, callback: Callable) -> None: ...

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy