diff --git a/fsl/data/strings.py b/fsl/data/strings.py index 854695c531864795cdf524de5464cf644184783a..d6e99e50a97006a685b94b2539adb143b8e662d5 100644 --- a/fsl/data/strings.py +++ b/fsl/data/strings.py @@ -42,6 +42,8 @@ messages = TypeDict({ 'fsleyes.loading' : 'Loading {}', 'FSLEyesSplash.default' : 'Loading ...', + 'FSLEyesFrame.restoringLayout' : 'Restoring layout from last session ...', + 'image.saveImage.error' : 'An error occurred saving the file. ' 'Details: {}', diff --git a/fsl/fsleyes/frame.py b/fsl/fsleyes/frame.py index 32e8b96839b38de3d4d721c9d257f56a4b619aba..7346c57869b72f30b4e08c3db775bd3fb50692ed 100644 --- a/fsl/fsleyes/frame.py +++ b/fsl/fsleyes/frame.py @@ -56,11 +56,10 @@ class FSLEyesFrame(wx.Frame): When a ``FSLEyesFrame`` is closed, it saves some display settings so that - they can be restored the next time a ``FSLEyesFrame`` is opened. The + they can be restored the next time a ``FSLEyesFrame`` is opened. The settings are saved using the :class:`~fsl.utils.settings` module. - - Currently, only the frame position and size are saved - in the future, I - plan to save and restore the ``ViewPanel`` layout as well. + Currently, the frame position, size, and layout (see the + :mod:`.perspectives` module) are saved. **Programming interface** @@ -85,7 +84,7 @@ class FSLEyesFrame(wx.Frame): parent, overlayList, displayCtx, - restore=True): + restore=False): """Create a ``FSLEyesFrame``. .. note:: The ``restore`` functionality is not currently implemented. @@ -140,6 +139,11 @@ class FSLEyesFrame(wx.Frame): self.__makeMenuBar() self.__restoreState(restore) + # If we have not restored the previous + # layout, we are not going to save the + # layout on exit. + self.__saveLayout = restore + self.__auiManager.Bind(aui.EVT_AUI_PANE_CLOSE, self.__onViewPanelClose) self .Bind(wx.EVT_CLOSE, self.__onClose) @@ -423,12 +427,16 @@ class FSLEyesFrame(wx.Frame): ev.Skip() - size = self.GetSize().Get() - position = self.GetScreenPosition().Get() - - fslsettings.write('framesize', str(size)) - fslsettings.write('frameposition', str(position)) - + if self.__saveLayout: + + size = self.GetSize().Get() + position = self.GetScreenPosition().Get() + layout = perspectives.serialisePerspective(self) + + fslsettings.write('framesize', str(size)) + fslsettings.write('frameposition', str(position)) + fslsettings.write('framelayout', layout) + # It's nice to explicitly clean # up our FSLEyesPanels, otherwise # they'll probably complain @@ -448,59 +456,28 @@ class FSLEyesFrame(wx.Frame): """A proxy for the :meth:`__parseSavedSize` method.""" return self.__parseSavedSize(size) - - def __parseSavedLayout(self, layout): - """Parses the given string, which is assumed to contain an encoded - :class:`.AuiManager` perspective (see - :meth:`.AuiManager.SavePerspective`). - - Returns a list of class names, specifying the control panels - (e.g. :class:`.OverlayListPanel`) which were previously open, and need - to be created. - - .. warning:: This method is not currently being used - it is from a - previous version. I may use it in the future to restore - ``ViewPanel`` layouts, or I may re-write it. - """ - - try: - - names = [] - sections = layout.split('|')[1:] - - for section in sections: - - if section.strip() == '': continue - - attrs = section.split(';') - attrs = dict([tuple(nvpair.split('=')) for nvpair in attrs]) - - if 'name' in attrs: - names.append(attrs['name']) - - return names - except: - return [] - - def __restoreState(self, restore=True): + def __restoreState(self, restore): """Called by :meth:`__init__`. If any frame size/layout properties have previously been saved via the :mod:`~fsl.utils.settings` module, they are read in, and applied to this frame. - :arg bool default: If ``True``, any saved state is ignored. + :arg restore: If ``False``, any saved layout state is ignored. """ from operator import itemgetter as iget # Restore the saved frame size/position - size = self.__parseSavedSize( - fslsettings.read('framesize')) - position = self.__parseSavedPoint( - fslsettings.read('frameposition')) + size = self.__parseSavedSize( fslsettings.read('framesize')) + position = self.__parseSavedPoint(fslsettings.read('frameposition')) + layout = fslsettings.read('framelayout') + # We can only restore a saved layout + # if there is a saved layout to restore + restore = restore and (layout is not None) + if (size is not None) and (position is not None): # Turn the saved size/pos into @@ -579,10 +556,12 @@ class FSLEyesFrame(wx.Frame): else: self.Centre() - # TODO Restore the previous view panel layout. - # Currently, we just display an OrthoPanel. if restore: - self.addViewPanel(views.OrthoPanel) + perspectives.applyPerspective( + self, + 'framelayout', + layout, + message=strings.messages[self, 'restoringLayout'],) def __makeMenuBar(self): diff --git a/fsl/fsleyes/perspectives.py b/fsl/fsleyes/perspectives.py index 9e5a4e069bbeed3de564a80ca2f651ffbafcfc71..e482e7b5a19dd51ea357b17e1d47dddcbe046195 100644 --- a/fsl/fsleyes/perspectives.py +++ b/fsl/fsleyes/perspectives.py @@ -12,6 +12,7 @@ control panel layouts for *FSLeyes*. getAllPerspectives loadPerspective + applyPerspective savePerspective removePerspective serialisePerspective @@ -50,7 +51,7 @@ def getAllPerspectives(): return uniq -def loadPerspective(frame, name): +def loadPerspective(frame, name, **kwargs): """ """ @@ -67,16 +68,24 @@ def loadPerspective(frame, name): raise ValueError('No perspective named "{}" exists'.format(name)) log.debug('Serialised perspective:\n{}'.format(persp)) + applyPerspective(frame, name, persp, **kwargs) + + +def applyPerspective(frame, name, perspective, showMessage=True, message=None): - persp = deserialisePerspective(persp) + persp = deserialisePerspective(perspective) frameChildren, frameLayout, vpChildrens, vpLayouts = persp # Show a message while re-configuring the frame - dlg = fsldlg.SimpleMessageDialog( - frame, - strings.messages['perspectives.applyingPerspective'].format( - strings.perspectives.get(name, name))) - dlg.Show() + + if showMessage: + if message is None: + message = strings.messages[ + 'perspectives.applyingPerspective'].format( + strings.perspectives.get(name, name)) + + dlg = fsldlg.SimpleMessageDialog(frame, message) + dlg.Show() # Clear all existing view # panels from the frame @@ -101,8 +110,9 @@ def loadPerspective(frame, name): vp.getAuiManager().LoadPerspective(vpLayout) - dlg.Close() - dlg.Destroy() + if showMessage: + dlg.Close() + dlg.Destroy() def savePerspective(frame, name): diff --git a/fsl/fsleyes/views/plotpanel.py b/fsl/fsleyes/views/plotpanel.py index 876fcc50eabba567328aea835ff0f6be418e180f..112c57918cd9a67c1f349962ead1f171bad6a630 100644 --- a/fsl/fsleyes/views/plotpanel.py +++ b/fsl/fsleyes/views/plotpanel.py @@ -845,8 +845,8 @@ class OverlayPlotPanel(PlotPanel): self._overlayList.removeListener('overlays', self.__name) self._displayCtx .removeListener('selectedOverlay', self.__name) - for ds in self.__dataSeries.values(): - ds.destroy() + for overlay in list(self.__dataSeries.keys()): + self.clearDataSeries(overlay) self.__dataSeries = None self.__refreshProps = None diff --git a/fsl/tools/fsleyes.py b/fsl/tools/fsleyes.py index b98392d1e8e7358ac519d36e9f55282d327c7721..92ef1a4cdc59b704fead5254caecf4bf5c8e19af 100644 --- a/fsl/tools/fsleyes.py +++ b/fsl/tools/fsleyes.py @@ -204,9 +204,9 @@ def interface(parent, args, ctx): if ycentre is None: ycentre = displayCtx.location.xz if zcentre is None: zcentre = displayCtx.location.xy - viewPanel._xcanvas.centreDisplayAt(*xcentre) - viewPanel._ycanvas.centreDisplayAt(*ycentre) - viewPanel._zcanvas.centreDisplayAt(*zcentre) + viewPanel.getXCanvas().centreDisplayAt(*xcentre) + viewPanel.getYCanvas().centreDisplayAt(*ycentre) + viewPanel.getZCanvas().centreDisplayAt(*zcentre) # Make sure the new frame is shown # before destroying the splash screen