From fb12da26adeb5848741c301bc9a2c5b7cad13d28 Mon Sep 17 00:00:00 2001
From: Paul McCarthy <pauld.mccarthy@gmail.com>
Date: Mon, 23 Feb 2015 21:33:36 +0000
Subject: [PATCH] In process of replacing ImageDisplayPanel with a
 nicer-looking toolbar which does the same thing.

---
 fsl/data/strings.py                         |   4 +-
 fsl/fslview/controls/__init__.py            |  14 +-
 fsl/fslview/controls/imagedisplaytoolbar.py | 195 ++++++++++++++++++++
 fsl/fslview/panel.py                        |  63 ++++---
 fsl/fslview/views/canvaspanel.py            |  45 +++--
 5 files changed, 274 insertions(+), 47 deletions(-)
 create mode 100644 fsl/fslview/controls/imagedisplaytoolbar.py

diff --git a/fsl/data/strings.py b/fsl/data/strings.py
index c96ba7042..d8247cbf7 100644
--- a/fsl/data/strings.py
+++ b/fsl/data/strings.py
@@ -55,7 +55,7 @@ titles = TypeDict({
     'ImageListPanel'    : 'Image list',
     'AtlasPanel'        : 'Atlases',
     'LocationPanel'     : 'Location',
-    'ImageDisplayPanel' : 'Display',
+    'ImageDisplayToolBar' : 'Display',
 })
 
 
@@ -96,6 +96,8 @@ labels = TypeDict({
     'LocationPanel.volumeLabel' : 'Volume',
     'LocationPanel.spaceLabel'  : 'Space',
     'LocationPanel.outOfBounds' : 'Out of bounds',
+
+    'ImageDisplayToolBar.more' : 'More settings',
 })
 
 
diff --git a/fsl/fslview/controls/__init__.py b/fsl/fslview/controls/__init__.py
index bc0cfb1e8..6f03570cd 100644
--- a/fsl/fslview/controls/__init__.py
+++ b/fsl/fslview/controls/__init__.py
@@ -19,14 +19,16 @@ dynamic lookup of all :class:`~fsl.fslview.panel.ControlPanel` types.
 import fsl.fslview.panel as fslpanel 
 import locationpanel
 import imagelistpanel
-import imagedisplaypanel
+# import imagedisplaypanel
+import imagedisplaytoolbar
 import atlaspanel
 
-FSLViewPanel      = fslpanel         .FSLViewPanel
-LocationPanel     = locationpanel    .LocationPanel
-ImageListPanel    = imagelistpanel   .ImageListPanel
-ImageDisplayPanel = imagedisplaypanel.ImageDisplayPanel
-AtlasPanel        = atlaspanel.       AtlasPanel
+FSLViewPanel        = fslpanel         .FSLViewPanel
+LocationPanel       = locationpanel    .LocationPanel
+ImageListPanel      = imagelistpanel   .ImageListPanel
+# ImageDisplayPanel  = imagedisplaypanel.ImageDisplayPanel
+ImageDisplayToolBar = imagedisplaytoolbar.ImageDisplayToolBar
+AtlasPanel          = atlaspanel.       AtlasPanel
 
 
 def listControlPanels():
diff --git a/fsl/fslview/controls/imagedisplaytoolbar.py b/fsl/fslview/controls/imagedisplaytoolbar.py
new file mode 100644
index 000000000..234b8efe9
--- /dev/null
+++ b/fsl/fslview/controls/imagedisplaytoolbar.py
@@ -0,0 +1,195 @@
+#!/usr/bin/env python
+#
+# imagedisplaytoolbar.py - A toolbar which shows display control options for
+#                          the currently selected image.
+#
+# Author: Paul McCarthy <pauldmccarthy@gmail.com>
+
+"""A :class:`wx.panel` which shows display control optionns for the currently
+selected image - see :attr:`fsl.data.image.ImageList.selectedImage`.
+"""
+
+import logging
+log = logging.getLogger(__name__)
+
+
+import                   wx
+
+import props
+
+import fsl.fslview.panel as fslpanel
+import fsl.data.strings  as strings
+import imageselectpanel  as imageselect
+
+
+class ImageDisplayToolBar(fslpanel.FSLViewToolBar):
+    
+    def __init__(self, parent, imageList, displayCtx):
+        fslpanel.FSLViewToolBar.__init__(self, parent, imageList, displayCtx)
+
+        self._imageSelect = imageselect.ImageSelectPanel(
+            self, imageList, displayCtx, False)
+
+        self._moreButton = wx.Button(
+            self, label=strings.labels['ImageDisplayToolBar.more'])
+
+        self._sepLine = wx.StaticLine(
+            self, size=(-1, 25), style=wx.LI_VERTICAL)
+
+        self._displayPanels = {}
+        self._optsPanels    = {}
+
+        self._currentDisplayPanel = None
+        self._currentOptsPanel    = None
+
+        self._sizer = wx.BoxSizer(wx.HORIZONTAL)
+        self._sizer.Add(self._imageSelect)
+        self._sizer.Add((0, 0))
+        self._sizer.Add(self._sepLine)
+        self._sizer.Add((0, 0))
+        self._sizer.Add(self._moreButton)
+
+        self.SetSizer(self._sizer)
+
+        self._displayCtx.addListener(
+            'selectedImage',
+            self._name,
+            self._selectedImageChanged)
+        
+        self._selectedImageChanged()
+
+
+    def destroy(self):
+        """Deregisters property listeners. """
+        fslpanel.FSLViewToolBar.destroy(self)
+
+        self._imageSelect.destroy()
+
+        self._displayCtx.removeListener('selectedImage', self._name)
+
+        for image in self._imageList:
+            image.removeListener('imageType', self._name)
+
+
+    def _imageTypeChanged(self, value, valid, image, name):
+
+        oldOptsPanel = self._optsPanels.get(image, None)
+        newOptsPanel = self._makeOptsPanel(image)
+        
+        self._optsPanels[image] = newOptsPanel
+        
+        if oldOptsPanel is not None:
+            current = self._sizer.GetItem(3).GetWindow()
+            if current is oldOptsPanel:
+                self._sizer.Detach(3)
+                self._sizer.Insert(3, newOptsPanel, flag=wx.EXPAND)
+            oldOptsPanel.Destroy()
+            
+
+    def _selectedImageChanged(self, *a):
+        """Called when the :attr:`~fsl.data.image.ImageList.selectedImage`
+        index changes. Ensures that the correct display panel is visible.
+        """
+
+        currDispPanel = self._sizer.GetItem(1).GetWindow()
+        currOptsPanel = self._sizer.GetItem(3).GetWindow()
+
+        self._sizer.Detach(3)
+        self._sizer.Detach(1)
+
+        if currDispPanel is not None: currDispPanel.Show(False)
+        if currOptsPanel is not None: currOptsPanel.Show(False) 
+
+        image = self._displayCtx.getSelectedImage()
+
+        if image is None:
+            self._sizer.Insert(1, (0, 0))
+            self._sizer.Insert(3, (0, 0))
+        else:
+
+            displayPanel = self._displayPanels.get(image, None)
+            optsPanel    = self._optsPanels   .get(image, None)
+
+            if displayPanel is None:
+                displayPanel = self._makeDisplayPanel(image)
+                self._displayPanels[image] = displayPanel
+
+            if optsPanel is None:
+                optsPanel = self._makeOptsPanel(image)
+                self._optsPanels[image] = optsPanel
+                
+                image.addListener(
+                    'imageType',
+                    self._name,
+                    self._imageTypeChanged,
+                    overwrite=True)
+                
+            self._sizer.Insert(1, displayPanel, flag=wx.EXPAND)
+            self._sizer.Insert(3, optsPanel,    flag=wx.EXPAND)
+
+            displayPanel.Show(True)
+            optsPanel   .Show(True)
+
+        self.Layout()
+            
+        
+    def _makeDisplayPanel(self, image):
+        """Creates and returns panel containing widgets allowing
+        the user to edit the display properties of the given
+        :class:`~fsl.data.image.Image` instance. 
+        """
+
+        display = self._displayCtx.getDisplayProperties(image)
+        panel   = wx.Panel(self)
+        
+        enabled    = props.makeWidget(panel, display, 'enabled')
+        name       = props.makeWidget(panel, display, 'name')
+        imageType  = props.makeWidget(panel, display, 'imageType')
+        
+        alpha      = wx.Slider(panel, value=100, minValue=0, maxValue=100)
+        brightness = wx.Slider(panel, value=50,  minValue=0, maxValue=100)
+        contrast   = wx.Slider(panel, value=50,  minValue=0, maxValue=100)
+        
+        props.bindWidget(alpha,      display, 'alpha',      wx.EVT_SLIDER)
+        props.bindWidget(brightness, display, 'brightness', wx.EVT_SLIDER)
+        props.bindWidget(contrast,   display, 'contrast',   wx.EVT_SLIDER)
+
+        sizer = wx.BoxSizer(wx.HORIZONTAL)
+        panel.SetSizer(sizer)
+
+        sizer.Add(enabled,    flag=wx.EXPAND)
+        sizer.Add(name,       flag=wx.EXPAND)
+        sizer.Add(imageType,  flag=wx.EXPAND)
+        sizer.Add(alpha,      flag=wx.EXPAND)
+        sizer.Add(brightness, flag=wx.EXPAND)
+        sizer.Add(contrast,   flag=wx.EXPAND)
+
+        panel.Layout()
+
+        return panel
+
+    
+    def _makeOptsPanel(self, image):
+
+        display = self._displayCtx.getDisplayProperties(image)
+        opts    = display.getDisplayOpts()
+        panel   = wx.Panel(self)
+        widgets = []
+
+        if display.imageType == 'volume':
+            widgets.append(props.makeWidget(panel, opts, 'cmap'))
+            
+        elif display.imageType == 'mask':
+            widgets.append(props.makeWidget(panel, opts, 'colour'))
+            
+        elif display.imageType == 'vector':
+            widgets.append(props.makeWidget(panel, opts, 'displayMode'))
+
+        sizer = wx.BoxSizer(wx.HORIZONTAL)
+        panel.SetSizer(sizer)
+
+        for w in widgets:
+            sizer.Add(w)
+
+        panel.Layout()
+        return panel
diff --git a/fsl/fslview/panel.py b/fsl/fslview/panel.py
index 38e2bae22..9cba35b1d 100644
--- a/fsl/fslview/panel.py
+++ b/fsl/fslview/panel.py
@@ -4,22 +4,23 @@
 #
 # Author: Paul McCarthy <pauldmccarthy@gmail.com>
 #
-"""This module provides a single class - the :class:`FSLViewPanel`.
-
+"""This module provides two classes - the :class:`FSLViewPanel`, and the
+:class:`FSLViewToolBar`.
 
 A :class:`FSLViewPanel` object is a :class:`wx.Panel` which provides some
 sort of view of a collection of :class:`~fsl.data.image.Image` objects,
-contained within an :class:`~fsl.data.image.ImageList`.
-
+contained within an :class:`~fsl.data.image.ImageList`. Similarly, a
+:class:`FSLViewToolBar` is a :class:`wx.lib.agw.aui.AuiToolBar` which
+provides some sort of control over the view.
 
-A :class:`ViewPanel` is also a :class:`~fsl.fslview.actions.ActionProvider`
-instance - any actions which are specified during construction are exposed
-to the user. Furthermore, any display configuration options which should be
-made available available to the user should be added as
-:class:`~props.PropertyBase` attributes of the :class:`FSLViewPanel`
-subclass.
+Instances of these classes are also
+:class:`~fsl.fslview.actions.ActionProvider` instances - any actions which
+are specified during construction may be exposed to the user. Furthermore,
+any display configuration options which should be made available available
+to the user should be added as :class:`~props.PropertyBase` attributes of
+the :class:`FSLViewPanel` subclass.
 
-See the following for examples of :class:`ViewPanel` subclasses:
+See the following for examples of :class:`FSLViewPanel` subclasses:
 
   - :class:`~fsl.fslview.views.OrthoPanel`
   - :class:`~fsl.fslview.views.LightBoxPanel`
@@ -44,8 +45,7 @@ import displaycontext
 
 log = logging.getLogger(__name__)
 
-
-class FSLViewPanel(wx.Panel, actions.ActionProvider):
+class _FSLViewPanel(actions.ActionProvider):
     """Superclass for FSLView view panels.
 
     A :class:`ViewPanel` has the following attributes, intended to be
@@ -64,14 +64,11 @@ class FSLViewPanel(wx.Panel, actions.ActionProvider):
 
     
     def __init__(self,
-                 parent,
                  imageList,
                  displayCtx,
                  actionz=None):
         """Create a :class:`ViewPanel`.
 
-        :arg parent:     The :mod:`wx` parent object of this panel.
-        
         :arg imageList:  A :class:`~fsl.data.image.ImageList` instance.
         
         :arg displayCtx: A :class:`~fsl.fslview.displaycontext.DisplayContext`
@@ -82,7 +79,6 @@ class FSLViewPanel(wx.Panel, actions.ActionProvider):
                          :class:`~fsl.fslview.actions.ActionProvider`).
         """
         
-        wx.Panel.__init__(self, parent)
         actions.ActionProvider.__init__(self, imageList, displayCtx, actionz)
 
         if not isinstance(imageList, fslimage.ImageList):
@@ -99,9 +95,6 @@ class FSLViewPanel(wx.Panel, actions.ActionProvider):
         self._name       = '{}_{}'.format(self.__class__.__name__, id(self))
         self.__destroyed = False
 
-        import fsl.fslview.layouts as layouts
-        self.SetMinSize(layouts.minSizes.get(self, (-1, -1)))
-
         
     def destroy(self):
         """This method must be called by whatever is managing this
@@ -138,11 +131,37 @@ class FSLViewPanel(wx.Panel, actions.ActionProvider):
             log.warning('The {}.destroy() method has not been called '
                         '- unless the application is shutting down, '
                         'this is probably a bug!'.format(type(self).__name__))
-        
-        wx.Panel              .__del__(self)
+
         actions.ActionProvider.__del__(self)
 
 
+class FSLViewPanel(_FSLViewPanel, wx.Panel):
+    def __init__(self, parent, imageList, displayCtx, actionz=None):
+        wx.Panel.__init__(self, parent)
+        _FSLViewPanel.__init__(self, imageList, displayCtx, actionz)
+
+        import fsl.fslview.layouts as layouts
+        self.SetMinSize(layouts.minSizes.get(self, (-1, -1)))
+
+        
+    def __del__(self):
+        wx.Panel     .__del__(self)
+        _FSLViewPanel.__del__(self)
+
+class FSLViewToolBar(_FSLViewPanel, wx.Panel):
+    def __init__(self, parent, imageList, displayCtx, actionz=None):
+        wx.Panel.__init__(self, parent)
+        _FSLViewPanel.__init__(self, imageList, displayCtx, actionz)
+
+        import fsl.fslview.layouts as layouts
+        self.SetMinSize(layouts.minSizes.get(self, (-1, -1)))
+
+        
+    def __del__(self):
+        wx.Panel     .__del__(self)
+        _FSLViewPanel.__del__(self)        
+
+        
 class ConfigPanel(wx.Panel):
 
     def __init__(self, parent, target, layout=None):
diff --git a/fsl/fslview/views/canvaspanel.py b/fsl/fslview/views/canvaspanel.py
index 4c7a3fa21..b4d5c286b 100644
--- a/fsl/fslview/views/canvaspanel.py
+++ b/fsl/fslview/views/canvaspanel.py
@@ -24,15 +24,16 @@ import wx.lib.agw.aui as aui
 
 import props
 
-import fsl.data.strings                       as strings
-import fsl.fslview.panel                      as fslpanel
-import fsl.fslview.profiles                   as profiles
-import fsl.fslview.displaycontext             as displayctx
-import fsl.fslview.controls.imagelistpanel    as imagelistpanel
-import fsl.fslview.controls.imagedisplaypanel as imagedisplaypanel
-import fsl.fslview.controls.locationpanel     as locationpanel
-import fsl.fslview.controls.atlaspanel        as atlaspanel
-import                                           colourbarpanel
+import fsl.data.strings                         as strings
+import fsl.fslview.panel                        as fslpanel
+import fsl.fslview.profiles                     as profiles
+import fsl.fslview.displaycontext               as displayctx
+import fsl.fslview.controls.imagelistpanel      as imagelistpanel
+# import fsl.fslview.controls.imagedisplaypanel as imagedisplaypanel
+import fsl.fslview.controls.imagedisplaytoolbar as imagedisplaytoolbar
+import fsl.fslview.controls.locationpanel       as locationpanel
+import fsl.fslview.controls.atlaspanel          as atlaspanel
+import                                             colourbarpanel
 
 
 def _takeScreenShot(imageList, displayCtx, canvas):
@@ -189,7 +190,7 @@ class CanvasPanel(fslpanel.FSLViewPanel):
             'toggleAtlasPanel'        : lambda *a: self.toggleControlPanel(
                 atlaspanel.AtlasPanel, *a),
             'toggleDisplayProperties' : lambda *a: self.toggleControlPanel(
-                imagedisplaypanel.ImageDisplayPanel, *a),
+                imagedisplaytoolbar.ImageDisplayToolBar, *a),
             'toggleLocationPanel'     : lambda *a: self.toggleControlPanel(
                 locationpanel.LocationPanel, *a),
             'toggleCanvasProperties'  : lambda  *a: self.toggleConfigPanel(
@@ -370,15 +371,23 @@ class CanvasPanel(fslpanel.FSLViewPanel):
             self.__auiMgr.DetachPane(window)
             self.__onPaneClose(None, window)
         else:
-            window = panelType(self, self._imageList, self._displayCtx)
+            window   = panelType(self, self._imageList, self._displayCtx)
+
+            if isinstance(window, fslpanel.FSLViewPanel):
+                paneInfo = aui.AuiPaneInfo()        \
+                    .Top()                          \
+                    .MinSize(window.GetMinSize())   \
+                    .BestSize(window.GetBestSize()) \
+                    .Caption(strings.titles[window])
+
+            elif isinstance(window, fslpanel.FSLViewToolBar):
+                paneInfo = aui.AuiPaneInfo()        \
+                    .Top()                          \
+                    .ToolbarPane()                  \
+                    .Caption(strings.titles[window])
+                
             
-            self.__auiMgr.AddPane(
-                window,
-                aui.AuiPaneInfo() 
-                .Top()
-                .MinSize(window.GetMinSize()) 
-                .BestSize(window.GetBestSize()) 
-                .Caption(strings.titles[window]))
+            self.__auiMgr.AddPane(window, paneInfo)
             self.__controlPanels[panelType] = window
             
         self.__auiMgr.Update()
-- 
GitLab