From 9b53c97bccd748fa891c281bd968a03fa3e24e62 Mon Sep 17 00:00:00 2001 From: Paul McCarthy <pauld.mccarthy@gmail.com> Date: Thu, 16 Jul 2015 14:21:37 +0100 Subject: [PATCH] DisplayContext now has a destroy method, which is called by the FSLViewFrame when the associated ViewPanel is closed. Lots of fixes still to be made. --- fsl/fslview/actions/__init__.py | 16 +++++++++------- fsl/fslview/displaycontext/displaycontext.py | 7 +++++++ fsl/fslview/frame.py | 8 +++++--- fsl/fslview/panel.py | 18 ++++++++---------- fsl/fslview/profiles/__init__.py | 4 ++-- fsl/fslview/profiles/lightboxviewprofile.py | 3 ++- fsl/fslview/views/canvaspanel.py | 6 +++--- fsl/fslview/views/lightboxpanel.py | 3 ++- fsl/fslview/views/viewpanel.py | 9 +++++++-- 9 files changed, 45 insertions(+), 29 deletions(-) diff --git a/fsl/fslview/actions/__init__.py b/fsl/fslview/actions/__init__.py index 7a7b242a0..cedaa72f4 100644 --- a/fsl/fslview/actions/__init__.py +++ b/fsl/fslview/actions/__init__.py @@ -202,15 +202,17 @@ class ActionProvider(props.HasProperties): act = Action(overlayList, displayCtx, action=func) self.__actions[name] = act - log.memory('{}.init ({})'.format(type(self).__name__, id(self))) - - - def __del__(self): - """ + + def destroy(self): + """This method should be called when this ``ActionProvider`` is + about to be destroyed. It ensures that all ``Action`` instances + are cleared. """ - log.memory('{}.del ({})'.format(type(self).__name__, id(self))) + for _, act in self.__actions.items(): + act.unbindAllWidgets() + self.__actions = None - + def addActionToggleListener(self, name, listenerName, func): """Add a listener function which will be called when the named action diff --git a/fsl/fslview/displaycontext/displaycontext.py b/fsl/fslview/displaycontext/displaycontext.py index 03e6aac7a..8ab23f27e 100644 --- a/fsl/fslview/displaycontext/displaycontext.py +++ b/fsl/fslview/displaycontext/displaycontext.py @@ -93,7 +93,14 @@ class DisplayContext(props.SyncableHasProperties): def __del__(self): log.memory('{}.del ({})'.format(type(self).__name__, id(self))) + + def destroy(self): + + for overlay, display in self.__displays.items(): + display.destroy() + + self.__displays = None def getDisplay(self, overlay, overlayType=None): diff --git a/fsl/fslview/frame.py b/fsl/fslview/frame.py index 3530734c1..4bd7990b2 100644 --- a/fsl/fslview/frame.py +++ b/fsl/fslview/frame.py @@ -98,6 +98,7 @@ class FSLViewFrame(wx.Frame): # Keeping track of all # open view panels self.__viewPanels = [] + self.__viewPanelDCs = {} self.__viewPanelTitles = {} self.__viewPanelMenus = {} self.__viewPanelCount = 0 @@ -141,6 +142,7 @@ class FSLViewFrame(wx.Frame): self.__viewPanels.append(panel) self.__viewPanelTitles[panel] = title + self.__viewPanelDCs[ panel] = childDC self.__centrePane.AddPage(panel, title, True) self.__centrePane.Split( @@ -184,6 +186,7 @@ class FSLViewFrame(wx.Frame): self.__viewPanels .remove(panel) self.__viewPanelMenus .pop( panel, None) title = self.__viewPanelTitles.pop( panel) + dctx = self.__viewPanelDCs .pop( panel) log.debug('Destroying view panel {} ({})'.format( title, type(panel).__name__)) @@ -200,10 +203,9 @@ class FSLViewFrame(wx.Frame): # Calling fslpanel.FSLViewPanel.destroy() # - I think that the AUINotebook does the - # wx.Window.Destroy side of things, which - # will eventually result in panel.__del__ - # ... + # wx.Window.Destroy side of things ... panel.destroy() + dctx .destroy() def __onClose(self, ev): diff --git a/fsl/fslview/panel.py b/fsl/fslview/panel.py index 4c57902d5..e88572f85 100644 --- a/fsl/fslview/panel.py +++ b/fsl/fslview/panel.py @@ -113,20 +113,18 @@ class _FSLViewPanel(actions.ActionProvider): Overriding subclass implementations must call this base class method, otherwise memory leaks will probably occur, and warnings will - probably be output to the log (see :meth:`__del__`). + probably be output to the log (see :meth:`__del__`). This + implememtation should be called after the subclass has performed its + own clean-up, as this method expliciltly clears the ``_overlayList`` + and ``_displayCtx`` references. """ - self.__destroyed = True + actions.ActionProvider.destroy(self) + self._displayCtx = None + self._overlayList = None + self.__destroyed = True def __del__(self): - """Sub-classes which implement ``__del__`` must call this - implementation, otherwise memory leaks will occur. - """ - actions.ActionProvider.__del__(self) - - self._overlayList = None - self._displayCtx = None - if not self.__destroyed: log.warning('The {}.destroy() method has not been called ' '- unless the application is shutting down, ' diff --git a/fsl/fslview/profiles/__init__.py b/fsl/fslview/profiles/__init__.py index 1bcc5aecc..f074175b5 100644 --- a/fsl/fslview/profiles/__init__.py +++ b/fsl/fslview/profiles/__init__.py @@ -193,8 +193,6 @@ class Profile(actions.ActionProvider): is called by the :class:`ProfileManager` when this ``Profile`` instance is no longer needed. """ - self.deregister() - self._viewPanel = None self._overlayList = None self._displayCtx = None @@ -591,6 +589,7 @@ class ProfileManager(object): important object references to avoid memory leaks. """ if self._currentProfile is not None: + self._currentProfile.deregister() self._currentProfile.destroy() self._currentProfile = None @@ -622,6 +621,7 @@ class ProfileManager(object): log.debug('Deregistering {} profile from {}'.format( self._currentProfile.__class__.__name__, self._viewCls.__name__)) + self._currentProfile.deregister() self._currentProfile.destroy() self._currentProfile = profileCls(self._viewPanel, diff --git a/fsl/fslview/profiles/lightboxviewprofile.py b/fsl/fslview/profiles/lightboxviewprofile.py index 1d8e3b298..338e75564 100644 --- a/fsl/fslview/profiles/lightboxviewprofile.py +++ b/fsl/fslview/profiles/lightboxviewprofile.py @@ -21,8 +21,9 @@ class LightBoxViewProfile(profiles.Profile): displayCtx, modes=['view', 'zoom']) - self._canvas = canvasPanel.getCanvas() + self._canvas = viewPanel.getCanvas() + def getEventTargets(self): return [self._canvas] diff --git a/fsl/fslview/views/canvaspanel.py b/fsl/fslview/views/canvaspanel.py index ecb724f49..3a559c243 100644 --- a/fsl/fslview/views/canvaspanel.py +++ b/fsl/fslview/views/canvaspanel.py @@ -245,11 +245,11 @@ class CanvasPanel(viewpanel.ViewPanel): cleanly. """ - viewpanel.ViewPanel.destroy(self) - if self.__colourBar is not None: self.__colourBar.destroy() - + + viewpanel.ViewPanel.destroy(self) + def screenshot(self, *a): _takeScreenShot(self._overlayList, self._displayCtx, self) diff --git a/fsl/fslview/views/lightboxpanel.py b/fsl/fslview/views/lightboxpanel.py index 46e10c741..54178ab81 100644 --- a/fsl/fslview/views/lightboxpanel.py +++ b/fsl/fslview/views/lightboxpanel.py @@ -133,12 +133,13 @@ class LightBoxPanel(canvaspanel.CanvasPanel): def destroy(self): """Removes property listeners""" - canvaspanel.CanvasPanel.destroy(self) self._displayCtx .removeListener('location', self._name) self._displayCtx .removeListener('selectedOverlay', self._name) self._overlayList.removeListener('overlays', self._name) + canvaspanel.CanvasPanel.destroy(self) + def _selectedOverlayChanged(self, *a): """Called when the selected overlay changes. diff --git a/fsl/fslview/views/viewpanel.py b/fsl/fslview/views/viewpanel.py index a7da9444e..deb698491 100644 --- a/fsl/fslview/views/viewpanel.py +++ b/fsl/fslview/views/viewpanel.py @@ -138,8 +138,6 @@ class ViewPanel(fslpanel.FSLViewPanel): def destroy(self): """ """ - - fslpanel.FSLViewPanel.destroy(self) # Make sure that any control panels are correctly destroyed for panelType, panel in self.__panels.items(): @@ -159,9 +157,16 @@ class ViewPanel(fslpanel.FSLViewPanel): # Un-initialise the AUI manager self.__auiMgr.UnInit() + # The AUI manager does not clear its + # reference to this panel, so let's + # do it here. + self.__auiMgr._frame = None self.__profileManager = None self.__auiMgr = None + fslpanel.FSLViewPanel.destroy(self) + + def setCentrePanel(self, panel): panel.Reparent(self) -- GitLab