From 086588debc582ffb6e6b7e28e7a896a11d8223f7 Mon Sep 17 00:00:00 2001
From: Paul McCarthy <pauld.mccarthy@gmail.com>
Date: Wed, 18 Nov 2015 13:30:47 +0000
Subject: [PATCH] Skeleton modules in place for loading/saving 'perspectives'

---
 fsl/data/strings.py                    | 16 ++++---
 fsl/fsleyes/__init__.py                | 11 +++--
 fsl/fsleyes/actions/copyoverlay.py     |  4 --
 fsl/fsleyes/actions/loadperspective.py | 32 ++++++++++++++
 fsl/fsleyes/actions/openfile.py        |  5 ---
 fsl/fsleyes/actions/openstandard.py    |  5 ---
 fsl/fsleyes/actions/saveoverlay.py     |  5 ---
 fsl/fsleyes/actions/saveperspective.py | 35 +++++++++++++++
 fsl/fsleyes/frame.py                   | 59 +++++++++++++++++++-------
 fsl/fsleyes/perspectives.py            | 40 +++++++++++++++++
 fsl/fsleyes/platform.py                | 33 ++++++++++++++
 fsl/fsleyes/views/__init__.py          | 25 +----------
 12 files changed, 202 insertions(+), 68 deletions(-)
 create mode 100644 fsl/fsleyes/actions/loadperspective.py
 create mode 100644 fsl/fsleyes/actions/saveperspective.py
 create mode 100644 fsl/fsleyes/perspectives.py
 create mode 100644 fsl/fsleyes/platform.py

diff --git a/fsl/data/strings.py b/fsl/data/strings.py
index df5cb2f08..a825943f0 100644
--- a/fsl/data/strings.py
+++ b/fsl/data/strings.py
@@ -195,11 +195,12 @@ titles = TypeDict({
 
 actions = TypeDict({
 
-    'OpenFileAction'      : 'Add overlay file',
-    'OpenStandardAction'  : 'Add standard',
-    'CopyOverlayAction'   : 'Copy overlay',
-    'SaveOverlayAction'   : 'Save overlay',
-    'LoadColourMapAction' : 'Load custom colour map',
+    'OpenFileAction'        : 'Add overlay file',
+    'OpenStandardAction'    : 'Add standard',
+    'CopyOverlayAction'     : 'Copy overlay',
+    'SaveOverlayAction'     : 'Save overlay',
+    'LoadColourMapAction'   : 'Load custom colour map',
+    'SavePerspectiveAction' : 'Save current perspective',
 
     'FSLEyesFrame.closeViewPanel' : 'Close',
 
@@ -779,3 +780,8 @@ melodic = TypeDict({
     'tr'             : 'TR time',
     'report'         : 'Link to report',
 })
+
+perspectives = {
+    'melview' : 'Melodic mode',
+    'feat'    : 'FEAT mode',
+}
diff --git a/fsl/fsleyes/__init__.py b/fsl/fsleyes/__init__.py
index 0e47190c4..cd6493777 100644
--- a/fsl/fsleyes/__init__.py
+++ b/fsl/fsleyes/__init__.py
@@ -26,8 +26,10 @@ Amongst other things, *FSLeyes* provides the following features:
   - Lightbox view (:mod:`.lightboxpanel`)
   - Time series plotting (:mod:`.timeseriespanel`)
   - Histogram plotting (:mod:`.histogrampanel`)
+  - Power spectrum plotting (:mod:`.powerspectrumpanel`)
   - FSL atlas explorer (:mod:`.atlaspanel`)
   - FEAT cluster results explorer (:mod:`.clusterpanel`)
+  - Melodic component classification (:mod:`.melodicclassificationpanel`)
   - NIFTI1 image editing (:mod:`.editor`)
   - A comprehensive command line interface (:mod:`.fsleyes_parseargs`)
 
@@ -180,12 +182,13 @@ The rest of *FSLeyes* is organised into the following sub-packages:
 Some other miscellaneous modules are contained in the ``fsleyes`` package:
 
 .. autosummary::
+   ~fsl.fsleyes.frame
+   ~fsl.fsleyes.panel
+   ~fsl.fsleyes.toolbar
    ~fsl.fsleyes.tooltips
+   ~fsl.fsleyes.perspectives
    ~fsl.fsleyes.icons
    ~fsl.fsleyes.colourmaps
+   ~fsl.fsleyes.plotting
    ~fsl.fsleyes.splash
-   ~fsl.fsleyes.frame
-   ~fsl.fsleyes.panel
-   ~fsl.fsleyes.toolbar
-
 """
diff --git a/fsl/fsleyes/actions/copyoverlay.py b/fsl/fsleyes/actions/copyoverlay.py
index 6adc966d9..7c78fb11d 100644
--- a/fsl/fsleyes/actions/copyoverlay.py
+++ b/fsl/fsleyes/actions/copyoverlay.py
@@ -8,7 +8,6 @@
 which creates a copy of the currently selected overlay.
 """
 
-import logging
 
 import numpy               as np
 
@@ -16,9 +15,6 @@ import fsl.fsleyes.actions as actions
 import fsl.data.image      as fslimage
 
 
-log = logging.getLogger(__name__)
-
-
 class CopyOverlayAction(actions.Action):
     """The ``CopyOverlayAction`` does as its name suggests - it creates a
     copy of the currently selected overlay.
diff --git a/fsl/fsleyes/actions/loadperspective.py b/fsl/fsleyes/actions/loadperspective.py
new file mode 100644
index 000000000..ee7e37fcf
--- /dev/null
+++ b/fsl/fsleyes/actions/loadperspective.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+#
+# loadperspective.py -
+#
+# Author: Paul McCarthy <pauldmccarthy@gmail.com>
+#
+"""
+"""
+
+
+import fsl.fsleyes.actions      as actions
+import fsl.fsleyes.perspectives as perspectives
+
+
+class LoadPerspectiveAction(actions.Action):
+    """
+    """
+
+    def __init__(self, frame, perspective):
+        """
+        """
+
+        self.__frame       = frame
+        self.__perspective = perspective
+         
+        actions.Action.__init__(self, self.__loadPerspective)
+
+        
+    def __loadPerspective(self):
+        """
+        """
+        perspectives.loadPerspective(self.__frame, self.__perspective)
diff --git a/fsl/fsleyes/actions/openfile.py b/fsl/fsleyes/actions/openfile.py
index d051475dd..7e0e127c5 100644
--- a/fsl/fsleyes/actions/openfile.py
+++ b/fsl/fsleyes/actions/openfile.py
@@ -9,14 +9,9 @@ load overlay files into the :class:`.OverlayList`.
 """
 
 
-import logging
-
 import fsl.fsleyes.actions as actions
 
 
-log = logging.getLogger(__name__)
-
-
 class OpenFileAction(actions.Action):
     """The ``OpenFileAction`` allows the user to add files to the
     :class:`.OverlayList`. This functionality is provided by the
diff --git a/fsl/fsleyes/actions/openstandard.py b/fsl/fsleyes/actions/openstandard.py
index 0c059665a..3603264c2 100644
--- a/fsl/fsleyes/actions/openstandard.py
+++ b/fsl/fsleyes/actions/openstandard.py
@@ -13,14 +13,9 @@ to load in standard space images from the ``$FSLDIR/data/standard/`` directory.
 import os
 import os.path as op
 
-import logging
-
 import fsl.fsleyes.actions as actions
 
 
-log = logging.getLogger(__name__)
-
-
 class OpenStandardAction(actions.Action):
     """The ``OpenStandardAction`` prompts the user to open one or more
     overlays, using ``$FSLDIR/data/standard/`` as the default directory.
diff --git a/fsl/fsleyes/actions/saveoverlay.py b/fsl/fsleyes/actions/saveoverlay.py
index 2538c007c..639fb2f0f 100644
--- a/fsl/fsleyes/actions/saveoverlay.py
+++ b/fsl/fsleyes/actions/saveoverlay.py
@@ -9,15 +9,10 @@ to save the currently selected overlay.
 """
 
 
-import logging
-
 import fsl.data.image      as fslimage
 import fsl.fsleyes.actions as actions
 
 
-log = logging.getLogger(__name__)
-
-
 class SaveOverlayAction(actions.Action):
     """The ``SaveOverlayAction`` allows the user to save the currently
     selected overlay, if it has been edited, or only exists in memory.
diff --git a/fsl/fsleyes/actions/saveperspective.py b/fsl/fsleyes/actions/saveperspective.py
new file mode 100644
index 000000000..5c3b8de19
--- /dev/null
+++ b/fsl/fsleyes/actions/saveperspective.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+#
+# saveperspective.py -
+#
+# Author: Paul McCarthy <pauldmccarthy@gmail.com>
+#
+"""
+"""
+
+
+import fsl.fsleyes.actions      as actions
+import fsl.fsleyes.perspectives as perspectives
+
+
+class SavePerspectiveAction(actions.Action):
+    """
+    """
+
+    def __init__(self, frame):
+        """
+        """
+
+        self.__frame = frame
+         
+        actions.Action.__init__(self, self.__savePerspective)
+
+        
+    def __savePerspective(self):
+        """
+        """
+
+        # TODO prompt for name
+        name = 'blah'
+
+        perspectives.savePerspective(self.__frame, name)
diff --git a/fsl/fsleyes/frame.py b/fsl/fsleyes/frame.py
index c6f55f902..6f3c2b146 100644
--- a/fsl/fsleyes/frame.py
+++ b/fsl/fsleyes/frame.py
@@ -20,10 +20,14 @@ import fsl.utils.settings as fslsettings
 
 import views
 import actions
-import actions.copyoverlay  as copyoverlay
-import actions.openfile     as openfile
-import actions.openstandard as openstandard
-import actions.saveoverlay  as saveoverlay
+import actions.copyoverlay     as copyoverlay
+import actions.openfile        as openfile
+import actions.openstandard    as openstandard
+import actions.saveoverlay     as saveoverlay
+import actions.loadperspective as loadperspective
+import actions.saveperspective as saveperspective 
+import perspectives
+
 import displaycontext
 
 
@@ -551,10 +555,11 @@ class FSLEyesFrame(wx.Frame):
         menuBar = wx.MenuBar()
         self.SetMenuBar(menuBar)
 
-        fileMenu     = wx.Menu()
-        viewMenu     = wx.Menu()
-        settingsMenu = wx.Menu() 
- 
+        fileMenu        = wx.Menu()
+        viewMenu        = wx.Menu()
+        perspectiveMenu = wx.Menu() 
+        settingsMenu    = wx.Menu()
+        
         menuBar.Append(fileMenu,     'File')
         menuBar.Append(viewMenu,     'View')
         menuBar.Append(settingsMenu, 'Settings') 
@@ -563,21 +568,43 @@ class FSLEyesFrame(wx.Frame):
         self.__viewMenu     = viewMenu
         self.__settingsMenu = settingsMenu
 
-        viewPanels = views   .listViewPanels()
-        actionz    = [openfile    .OpenFileAction,
-                      openstandard.OpenStandardAction,
-                      copyoverlay .CopyOverlayAction,
-                      saveoverlay .SaveOverlayAction]
-
+        # Global actions
+        actionz = [openfile    .OpenFileAction,
+                   openstandard.OpenStandardAction,
+                   copyoverlay .CopyOverlayAction,
+                   saveoverlay .SaveOverlayAction]
+ 
         for action in actionz:
-            menuItem = fileMenu.Append(wx.ID_ANY, strings.actions[action])
-            
+            menuItem  = fileMenu.Append(wx.ID_ANY, strings.actions[action])
             actionObj = action(self.__overlayList, self.__displayCtx)
 
             actionObj.bindToWidget(self, wx.EVT_MENU, menuItem)
 
+        # Shortcuts to open a new view panel
+        viewPanels = [views.OrthoPanel,
+                      views.LightBoxPanel,
+                      views.TimeSeriesPanel,
+                      views.PowerSpectrumPanel,
+                      views.HistogramPanel]
+        
         for viewPanel in viewPanels:
             viewAction = viewMenu.Append(wx.ID_ANY, strings.titles[viewPanel]) 
             self.Bind(wx.EVT_MENU,
                       lambda ev, vp=viewPanel: self.addViewPanel(vp),
                       viewAction)
+
+        # Perspectives
+        viewMenu.AppendSubMenu(perspectiveMenu, 'Perspectives')
+        for persp in perspectives.getAllPerspectives():
+            
+            menuItem  = perspectiveMenu.Append(wx.ID_ANY,
+                                               strings.perspectives[persp])
+            actionObj = loadperspective.LoadPerspectiveAction(self, persp)
+            actionObj.bindToWidget(self, wx.EVT_MENU, menuItem)
+
+        # Save perspective
+        savePerspAction   = saveperspective.SavePerspectiveAction(self)
+        savePerspMenuItem = perspectiveMenu.Append(
+            wx.ID_ANY, strings.actions[savePerspAction])
+
+        savePerspAction.bindToWidget(self, wx.EVT_MENU, savePerspMenuItem)
diff --git a/fsl/fsleyes/perspectives.py b/fsl/fsleyes/perspectives.py
new file mode 100644
index 000000000..577eb314e
--- /dev/null
+++ b/fsl/fsleyes/perspectives.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+#
+# perspectives.py -
+#
+# Author: Paul McCarthy <pauldmccarthy@gmail.com>
+#
+
+import logging
+
+
+log = logging.getLogger(__name__)
+
+
+def getAllPerspectives():
+    return ['melview', 'feat']
+
+
+def loadPerspective(frame, name):
+    log.debug('Loading perspective {}'.format(name))
+
+
+def savePerspective(frame, name):
+    log.debug('Saving current perspective with name {}'.format(name))
+
+
+    
+def serialisePerspective(frame):
+    log.debug('Serialising current perspective')
+
+
+class Perspective(object):
+
+    # Views
+    # 
+    # View layout
+    # 
+    # For each view:
+    #   - Controls
+    #   - Control layout
+    pass
diff --git a/fsl/fsleyes/platform.py b/fsl/fsleyes/platform.py
new file mode 100644
index 000000000..9575f46e2
--- /dev/null
+++ b/fsl/fsleyes/platform.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+#
+# platform.py -
+#
+# Author: Paul McCarthy <pauldmccarthy@gmail.com>
+#
+
+haveGui    = False
+wxFlavour  = None
+wxPlatform = None
+
+
+WX_PYTHON  = 1
+WX_PHOENIX = 2
+
+WX_MAC = 1
+WX_GTK = 2
+
+
+try:
+    import wx
+    haveGui = True
+
+except ImportError:
+    haveGui = False
+
+
+if 'phoenix' in wx.PlatformInformation: wxFlavour = WX_PHOENIX
+else:                                   wxFlavour = WX_PYTHON
+
+
+if   'MAC' in wx.Platform: wxPlatform = WX_MAC
+elif 'GTK' in wx.Platform: wxPlatform = WX_GTK
diff --git a/fsl/fsleyes/views/__init__.py b/fsl/fsleyes/views/__init__.py
index 3d472c5d3..0b457ffb6 100644
--- a/fsl/fsleyes/views/__init__.py
+++ b/fsl/fsleyes/views/__init__.py
@@ -12,9 +12,7 @@ the :mod:`~fsl.fsleyes` package documentation.  It contains a collection of
 views.
 
 
-A package-level convenience function, :func:`listViewPanels`, is provided to
-allow dynamic lookup of all :class:`.ViewPanel` sub-classes. The following
-:class:`.ViewPanel` sub-classes currently exist:
+The following :class:`.ViewPanel` sub-classes currently exist:
 
 .. autosummary::
    :nosignatures:
@@ -51,24 +49,3 @@ LightBoxPanel      = lightboxpanel     .LightBoxPanel
 TimeSeriesPanel    = timeseriespanel   .TimeSeriesPanel
 PowerSpectrumPanel = powerspectrumpanel.PowerSpectrumPanel
 HistogramPanel     = histogrampanel    .HistogramPanel
-
-
-def listViewPanels():
-    """Convenience function which returns a list containing all
-    :class:`.ViewPanel` sub-classes in the ``views`` package.
-    """
-
-    atts = globals()
-
-    viewPanels = []
-
-    for name, val in atts.items():
-        
-        if not isinstance(val, type): continue
-        if val == FSLEyesPanel:       continue
-            
-        if issubclass(val, FSLEyesPanel) and \
-           val not in (CanvasPanel, ViewPanel, PlotPanel):
-            viewPanels.append(val)
-            
-    return viewPanels
-- 
GitLab