Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 8ffab35

Browse filesBrowse files
committed
proper garbage collection of WorldObject, implemented and works for ImageGraphic
1 parent 6d4141b commit 8ffab35
Copy full SHA for 8ffab35

File tree

Expand file treeCollapse file tree

3 files changed

+58
-12
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+58
-12
lines changed

‎fastplotlib/graphics/_base.py

Copy file name to clipboardExpand all lines: fastplotlib/graphics/_base.py
+17-8Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from typing import *
2+
import weakref
23
from warnings import warn
34

45
import numpy as np
@@ -14,6 +15,11 @@
1415
from dataclasses import dataclass
1516

1617

18+
# dict that holds all world objects for a given python kernel/session
19+
# Graphic objects only use proxies to WorldObjects
20+
WORLD_OBJECTS: Dict[str, WorldObject] = dict() #: {hex id str: WorldObject}
21+
22+
1723
PYGFX_EVENTS = [
1824
"key_down",
1925
"key_up",
@@ -58,10 +64,15 @@ def __init__(
5864
self.registered_callbacks = dict()
5965
self.present = PresentFeature(parent=self)
6066

67+
self._world_objects = WORLD_OBJECTS
68+
6169
@property
6270
def world_object(self) -> WorldObject:
63-
"""Associated pygfx WorldObject."""
64-
return self._world_object
71+
"""Associated pygfx WorldObject. Always returns a proxy, real object cannot be accessed directly."""
72+
return weakref.proxy(self._world_objects[hex(id(self))])
73+
74+
def _set_world_object(self, wo: WorldObject):
75+
self._world_objects[hex(id(self))] = wo
6576

6677
@property
6778
def position(self) -> Vector3:
@@ -75,7 +86,7 @@ def visible(self) -> bool:
7586
return self.world_object.visible
7687

7788
@visible.setter
78-
def visible(self, v) -> bool:
89+
def visible(self, v: bool):
7990
"""Access or change the visibility."""
8091
self.world_object.visible = v
8192

@@ -100,6 +111,9 @@ def __repr__(self):
100111
else:
101112
return rval
102113

114+
def __del__(self):
115+
del self._world_objects[hex(id(self))]
116+
103117

104118
class Interaction(ABC):
105119
"""Mixin class that makes graphics interactive"""
@@ -271,11 +285,6 @@ def __init__(self, name: str = None):
271285
super(GraphicCollection, self).__init__(name)
272286
self._graphics: List[Graphic] = list()
273287

274-
@property
275-
def world_object(self) -> Group:
276-
"""Returns the underling pygfx WorldObject."""
277-
return self._world_object
278-
279288
@property
280289
def graphics(self) -> Tuple[Graphic]:
281290
"""returns the Graphics within this collection"""

‎fastplotlib/graphics/image.py

Copy file name to clipboardExpand all lines: fastplotlib/graphics/image.py
+3-1Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,13 @@ def __init__(
100100
self.cmap = ImageCmapFeature(self, cmap)
101101
material = pygfx.ImageBasicMaterial(clim=(vmin, vmax), map=self.cmap())
102102

103-
self._world_object: pygfx.Image = pygfx.Image(
103+
world_object = pygfx.Image(
104104
geometry,
105105
material
106106
)
107107

108+
self._set_world_object(world_object)
109+
108110
self.data = ImageDataFeature(self, data)
109111
# TODO: we need to organize and do this better
110112
if isolated_buffer:

‎fastplotlib/layouts/_base.py

Copy file name to clipboardExpand all lines: fastplotlib/layouts/_base.py
+38-3Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Viewport, WgpuRenderer
44
from wgpu.gui.auto import WgpuCanvas
55
from warnings import warn
6-
from ..graphics._base import Graphic
6+
from ..graphics._base import Graphic, WORLD_OBJECTS
77
from ..graphics.line_slider import LineSlider
88
from typing import *
99

@@ -287,17 +287,52 @@ def auto_scale(self, maintain_aspect: bool = False, zoom: float = 0.8):
287287

288288
def remove_graphic(self, graphic: Graphic):
289289
"""
290-
Remove a graphic from the scene. Note: This does not garbage collect the graphic,
291-
you can add it back to the scene after removing it.
290+
Remove a ``Graphic`` from the scene. Note: This does not garbage collect the graphic,
291+
you can add it back to the scene after removing it. Use ``delete_graphic()`` to
292+
delete and garbage collect a ``Graphic``.
292293
293294
Parameters
294295
----------
295296
graphic: Graphic or GraphicCollection
296297
The graphic to remove from the scene
297298
298299
"""
300+
299301
self.scene.remove(graphic.world_object)
300302

303+
def delete_graphic(self, graphic: Graphic):
304+
"""
305+
Delete the graphic, garbage collects and frees GPU VRAM.
306+
307+
Parameters
308+
----------
309+
graphic: Graphic or GraphicCollection
310+
The graphic to delete
311+
312+
"""
313+
314+
if graphic not in self._graphics:
315+
raise KeyError
316+
317+
if graphic.world_object in self.scene.children:
318+
self.scene.remove(graphic.world_object)
319+
320+
self._graphics.remove(graphic)
321+
322+
# delete associated world object to free GPU VRAM
323+
loc = hex(id(graphic))
324+
del WORLD_OBJECTS[loc]
325+
326+
del graphic
327+
328+
def clear(self):
329+
"""
330+
Clear the Plot or Subplot. Also performs garbage collection, i.e. runs ``delete_graphic`` on all graphics.
331+
"""
332+
333+
for g in self._graphics:
334+
self.delete_graphic(g)
335+
301336
def __getitem__(self, name: str):
302337
for graphic in self._graphics:
303338
if graphic.name == name:

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.