diff --git a/fsl/fslview/actions/__init__.py b/fsl/fslview/actions/__init__.py index 7a7b242a0972076556f7bef2e0fa5b68a04ad365..cedaa72f4f29ce4252281c18ab80b84113f947f2 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 03e6aac7a004cf5651fb53993ebf8dd6cb86235d..8ab23f27edef7f881a7eca9d1957f33669bfe4df 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 3530734c1496ae6a4f9386f368830c02559102aa..4bd7990b2c6229e230a8a003bd062c1f6bbc7788 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 4c57902d56177df98748163a294128178352a023..e88572f8517f71077535888b2ea8a9acf9528de4 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 1bcc5aeccc9c1b1f63b3a190a7800ea6404f4207..f074175b530fb16d7c3d0700d71ecfa4f414fbf9 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 1d8e3b298487d6d1f9291e458aa99e51c2663a5d..338e75564a5eb4fe98ffaf9d6886436863dffcc8 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 ecb724f49e155bc1dab6abb6329914bd77ffabfe..3a559c243e4588b8779fbb3902a7170a3ca8fd4a 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 46e10c741700aeaf1545ba033c5f709379778465..54178ab81f6d301bfcadeafdab130517d9aa4349 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 a7da9444e3af075aade6e30c703d1461c3acd187..deb698491445688fb53c9275e8e4a85120d517d9 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)