From bc3bfd3d5edf06442aa9ccea3f88082b7247536e Mon Sep 17 00:00:00 2001 From: Paul McCarthy <pauld.mccarthy@gmail.com> Date: Thu, 16 Jul 2015 14:50:28 +0100 Subject: [PATCH] SliceCanvas/LightBoxCanvas now have destroy methods which are explciitly called by their owning OrthoPanel/LightBoxPanels --- fsl/fslview/displaycontext/displaycontext.py | 2 + fsl/fslview/frame.py | 3 +- fsl/fslview/gl/lightboxcanvas.py | 16 +++++ fsl/fslview/gl/slicecanvas.py | 73 ++++++++++++++++---- fsl/fslview/gl/wxgllightboxcanvas.py | 57 --------------- fsl/fslview/gl/wxglslicecanvas.py | 51 +------------- fsl/fslview/views/lightboxpanel.py | 2 + fsl/fslview/views/orthopanel.py | 7 +- 8 files changed, 90 insertions(+), 121 deletions(-) diff --git a/fsl/fslview/displaycontext/displaycontext.py b/fsl/fslview/displaycontext/displaycontext.py index 8ab23f27e..71c45aae7 100644 --- a/fsl/fslview/displaycontext/displaycontext.py +++ b/fsl/fslview/displaycontext/displaycontext.py @@ -97,6 +97,8 @@ class DisplayContext(props.SyncableHasProperties): def destroy(self): + self.__overlayList.removeListener('overlays', self.__name) + for overlay, display in self.__displays.items(): display.destroy() diff --git a/fsl/fslview/frame.py b/fsl/fslview/frame.py index 4bd7990b2..440624556 100644 --- a/fsl/fslview/frame.py +++ b/fsl/fslview/frame.py @@ -202,7 +202,8 @@ class FSLViewFrame(wx.Frame): menuBar.Remove(menuIdx) # Calling fslpanel.FSLViewPanel.destroy() - # - I think that the AUINotebook does the + # and DisplayContext.destroy() - the + # AUINotebook should do the # wx.Window.Destroy side of things ... panel.destroy() dctx .destroy() diff --git a/fsl/fslview/gl/lightboxcanvas.py b/fsl/fslview/gl/lightboxcanvas.py index e3b08e174..219058c1c 100644 --- a/fsl/fslview/gl/lightboxcanvas.py +++ b/fsl/fslview/gl/lightboxcanvas.py @@ -212,6 +212,22 @@ class LightBoxCanvas(slicecanvas.SliceCanvas): '{}_zPosChanged'.format(self.name), self._zPosChanged) + def destroy(self): + + self.removeListener('pos', '{}_zPosChanged'.format(self.name)) + self.removeListener('sliceSpacing', self.name) + self.removeListener('ncols', self.name) + self.removeListener('nrows', self.name) + self.removeListener('zrange', self.name) + self.removeListener('showGridLines', self.name) + self.removeListener('highlightSlice', self.name) + self.removeListener('topRow', self.name) + + if self._offscreenRenderTexture is not None: + self._offscreenRenderTexture.destroy() + + slicecanvas.SliceCanvas.destroy(self) + def _topRowChanged(self, *a): """Called when the :attr:`topRow` property changes. Adjusts display diff --git a/fsl/fslview/gl/slicecanvas.py b/fsl/fslview/gl/slicecanvas.py index e50a14797..6da930479 100644 --- a/fsl/fslview/gl/slicecanvas.py +++ b/fsl/fslview/gl/slicecanvas.py @@ -308,6 +308,47 @@ class SliceCanvas(props.HasProperties): self._overlayBoundsChanged) + def destroy(self): + """This method must be called when this ``SliceCanvas`` is no longer + being used. + + It removes listeners from all :class:`.OverlayList`, + :class:`.DisplayContext`, and :class:`.Display` instances, and + destroys OpenGL representations of all overlays. + """ + self.removeListener('zax', self.name) + self.removeListener('pos', self.name) + self.removeListener('displayBounds', self.name) + self.removeListener('showCursor', self.name) + self.removeListener('invertX', self.name) + self.removeListener('invertY', self.name) + self.removeListener('zoom', self.name) + self.removeListener('renderMode', self.name) + + self.overlayList.removeListener('overlays', self.name) + self.displayCtx .removeListener('bounds', self.name) + self.displayCtx .removeListener('overlayOrder', self.name) + + for overlay in self.overlayList: + disp = self.displayCtx.getDisplay(overlay) + globj = self._glObjects[overlay] + + disp.removeListener('overlayType', self.name) + disp.removeListener('enabled', self.name) + disp.unbindProps( 'softwareMode', self) + + globj.destroy() + + rt, rtName = self._prerenderTextures.get(overlay, (None, None)) + ot = self._offscreenTextures.get(overlay, None) + + if rt is not None: glresources.delete(rtName) + if ot is not None: ot .destroy() + + self.overlayList = None + self.displayCxt = None + + def _initGL(self): """Call the _overlayListChanged method - it will generate any necessary GL data for each of the overlays @@ -516,7 +557,7 @@ class SliceCanvas(props.HasProperties): self._refresh() - def __genGLObject(self, overlay, display, updateRenderTextures=True): + def __genGLObject(self, overlay, updateRenderTextures=True): """Creates a :class:`.GLObject` instance for the given ``overlay``, destroying any existing instance. @@ -525,6 +566,8 @@ class SliceCanvas(props.HasProperties): render texture associated with the overlay is destroyed. """ + display = self.displayCtx.getDisplay(overlay) + # Tell the previous GLObject (if # any) to clean up after itself globj = self._glObjects.pop(overlay, None) @@ -559,6 +602,19 @@ class SliceCanvas(props.HasProperties): self._glObjects[overlay] = globj + if not display.isBound('softwareMode', self): + display.bindProps('softwareMode', self) + + display.addListener('overlayType', + self.name, + self.__overlayTypeChanged, + overwrite=True) + + display.addListener('enabled', + self.name, + self._refresh, + overwrite=True) + return globj @@ -590,18 +646,7 @@ class SliceCanvas(props.HasProperties): if overlay in self._glObjects: continue - display = self.displayCtx.getDisplay(overlay) - - self.__genGLObject(overlay, display, updateRenderTextures=False) - - # Bind Display.softwareMode to SliceCanvas.softwareMode - display.bindProps('softwareMode', self) - - display.addListener('overlayType', - self.name, - self.__overlayTypeChanged) - - display.addListener('enabled', self.name, self._refresh) + self.__genGLObject(overlay, updateRenderTextures=False) self._updateRenderTextures() self._resolutionLimitChange() @@ -919,7 +964,7 @@ class SliceCanvas(props.HasProperties): continue if globj is None: - globj = self.__genGLObject(overlay, display) + globj = self.__genGLObject(overlay) # On-screen rendering - the globject is # rendered directly to the screen canvas diff --git a/fsl/fslview/gl/wxgllightboxcanvas.py b/fsl/fslview/gl/wxgllightboxcanvas.py index 60a9074c2..75d1fe158 100644 --- a/fsl/fslview/gl/wxgllightboxcanvas.py +++ b/fsl/fslview/gl/wxgllightboxcanvas.py @@ -8,17 +8,12 @@ :class:`.LightBoxCanvas`, and a :class:`wx.glcanvas.GLCanvas`. """ -import logging import wx import wx.glcanvas as wxgl import lightboxcanvas as lightboxcanvas import fsl.fslview.gl as fslgl -import fsl.fslview.gl.resources as glresources - - -log = logging.getLogger(__name__) class WXGLLightBoxCanvas(lightboxcanvas.LightBoxCanvas, @@ -41,58 +36,6 @@ class WXGLLightBoxCanvas(lightboxcanvas.LightBoxCanvas, displayCtx, zax) fslgl.WXGLCanvasTarget .__init__(self) - - # the overlay list is probably going to outlive - # this SliceCanvas object, so we do the right - # thing and remove our listeners when we die - def onDestroy(ev): - ev.Skip() - - if ev.GetEventObject() is not self: - return - - self.removeListener('zax', self.name) - self.removeListener('pos', self.name) - self.removeListener('pos', - '{}_zPosChanged'.format(self.name)) - self.removeListener('displayBounds', self.name) - self.removeListener('showCursor', self.name) - self.removeListener('invertX', self.name) - self.removeListener('invertY', self.name) - self.removeListener('zoom', self.name) - self.removeListener('sliceSpacing', self.name) - self.removeListener('ncols', self.name) - self.removeListener('nrows', self.name) - self.removeListener('zrange', self.name) - self.removeListener('showGridLines', self.name) - self.removeListener('highlightSlice', self.name) - self.removeListener('topRow', self.name) - self.removeListener('renderMode', self.name) - - self.overlayList.removeListener('overlays', self.name) - self.displayCtx .removeListener('bounds', self.name) - self.displayCtx .removeListener('overlayOrder', self.name) - - for overlay in self.overlayList: - - disp = self.displayCtx.getDisplay(overlay) - globj = self._glObjects[overlay] - - disp.removeListener('overlayType', self.name) - disp.removeListener('enabled', self.name) - disp.removeListener('softwareMode', self.name) - - globj.destroy() - - rt, rtName = self._prerenderTextures.get(overlay, (None, None)) - - if rt is not None: - glresources.delete(rtName) - - if self._offscreenRenderTexture is not None: - self._offscreenRenderTexture.destroy() - - self.Bind(wx.EVT_WINDOW_DESTROY, onDestroy) # When the canvas is resized, we have to update # the display bounds to preserve the aspect ratio diff --git a/fsl/fslview/gl/wxglslicecanvas.py b/fsl/fslview/gl/wxglslicecanvas.py index ad0778e06..502679b7f 100644 --- a/fsl/fslview/gl/wxglslicecanvas.py +++ b/fsl/fslview/gl/wxglslicecanvas.py @@ -13,17 +13,12 @@ data (although most of the functionality is provided by the :class:`.SliceCanvas` class). """ -import logging import wx -import wx.glcanvas as wxgl +import wx.glcanvas as wxgl -import slicecanvas as slicecanvas -import fsl.fslview.gl.resources as glresources -import fsl.fslview.gl as fslgl - - -log = logging.getLogger(__name__) +import slicecanvas as slicecanvas +import fsl.fslview.gl as fslgl class WXGLSliceCanvas(slicecanvas.SliceCanvas, @@ -43,46 +38,6 @@ class WXGLSliceCanvas(slicecanvas.SliceCanvas, wxgl.GLCanvas .__init__(self, parent) slicecanvas.SliceCanvas.__init__(self, overlayList, displayCtx, zax) fslgl.WXGLCanvasTarget .__init__(self) - - # the image list is probably going to outlive - # this SliceCanvas object, so we do the right - # thing and remove our listeners when we die - def onDestroy(ev): - ev.Skip() - - if ev.GetEventObject() is not self: - return - - self.removeListener('zax', self.name) - self.removeListener('pos', self.name) - self.removeListener('displayBounds', self.name) - self.removeListener('showCursor', self.name) - self.removeListener('invertX', self.name) - self.removeListener('invertY', self.name) - self.removeListener('zoom', self.name) - self.removeListener('renderMode', self.name) - - self.overlayList.removeListener('overlays', self.name) - self.displayCtx .removeListener('bounds', self.name) - self.displayCtx .removeListener('overlayOrder', self.name) - - for overlay in self.overlayList: - disp = self.displayCtx.getDisplay(overlay) - globj = self._glObjects[overlay] - - disp.removeListener('overlayType', self.name) - disp.removeListener('enabled', self.name) - disp.removeListener('softwareMode', self.name) - - globj.destroy() - - rt, rtName = self._prerenderTextures.get(overlay, (None, None)) - ot = self._offscreenTextures.get(overlay, None) - - if rt is not None: glresources.delete(rtName) - if ot is not None: ot .destroy() - - self.Bind(wx.EVT_WINDOW_DESTROY, onDestroy) # When the canvas is resized, we have to update # the display bounds to preserve the aspect ratio diff --git a/fsl/fslview/views/lightboxpanel.py b/fsl/fslview/views/lightboxpanel.py index 54178ab81..616e3ee7a 100644 --- a/fsl/fslview/views/lightboxpanel.py +++ b/fsl/fslview/views/lightboxpanel.py @@ -138,6 +138,8 @@ class LightBoxPanel(canvaspanel.CanvasPanel): self._displayCtx .removeListener('selectedOverlay', self._name) self._overlayList.removeListener('overlays', self._name) + self._lbCanvas.destroy() + canvaspanel.CanvasPanel.destroy(self) diff --git a/fsl/fslview/views/orthopanel.py b/fsl/fslview/views/orthopanel.py index d503048ac..567f29e84 100644 --- a/fsl/fslview/views/orthopanel.py +++ b/fsl/fslview/views/orthopanel.py @@ -184,7 +184,6 @@ class OrthoPanel(canvaspanel.CanvasPanel): this OrthoPanel. So when this panel is destroyed, all those registered listeners are removed. """ - canvaspanel.CanvasPanel.destroy(self) self._displayCtx .removeListener('location', self._name) self._displayCtx .removeListener('bounds', self._name) @@ -192,6 +191,10 @@ class OrthoPanel(canvaspanel.CanvasPanel): self._displayCtx .removeListener('overlayOrder', self._name) self._overlayList.removeListener('overlays', self._name) + self._xcanvas.destroy() + self._ycanvas.destroy() + self._zcanvas.destroy() + # The _overlayListChanged method adds # listeners to individual overlays, # so we have to remove them too @@ -199,6 +202,8 @@ class OrthoPanel(canvaspanel.CanvasPanel): opts = self._displayCtx.getOpts(ovl) opts.removeGlobalListener(self._name) + canvaspanel.CanvasPanel.destroy(self) + def __onZoom(self, *a): """Called when the :attr:`.SceneOpts.zoom` property changes. -- GitLab