From e46718dda5d3b64d67e1c8d391e9e956da18ce5c Mon Sep 17 00:00:00 2001
From: Paul McCarthy <pauld.mccarthy@gmail.com>
Date: Thu, 26 Mar 2015 14:33:04 +0000
Subject: [PATCH] GLVolume code was assuming the presence of a 'clippingRange'
 property - was failing for GLMask types. Hack to make image display panel
 size more useful. DisplayOpts instances have a 'destroy' method, so listeners
 can be de-registered

---
 fsl/data/strings.py                       |  2 +-
 fsl/fslview/controls/imagedisplaypanel.py |  7 +++----
 fsl/fslview/displaycontext/display.py     |  7 +++++++
 fsl/fslview/displaycontext/maskopts.py    | 16 +++++++++++-----
 fsl/fslview/displaycontext/volumeopts.py  |  6 ++++++
 fsl/fslview/gl/glmask.py                  |  8 ++++----
 fsl/fslview/layouts.py                    |  6 +++---
 fsl/tools/fslview_parseargs.py            | 14 +++++++-------
 8 files changed, 42 insertions(+), 24 deletions(-)

diff --git a/fsl/data/strings.py b/fsl/data/strings.py
index 85d9f5ae9..d2b71fa42 100644
--- a/fsl/data/strings.py
+++ b/fsl/data/strings.py
@@ -203,7 +203,7 @@ properties = TypeDict({
 
     'MaskOpts.colour'         : 'Colour',
     'MaskOpts.invert'         : 'Invert',
-    'MaskOpts.threshold'      : 'Threshold',
+    'MaskOpts.clippingRange'  : 'Threshold',
 
     'VectorOpts.displayMode'   : 'Display mode',
     'VectorOpts.xColour'       : 'X Colour',
diff --git a/fsl/fslview/controls/imagedisplaypanel.py b/fsl/fslview/controls/imagedisplaypanel.py
index 8e60c04ad..1581294fd 100644
--- a/fsl/fslview/controls/imagedisplaypanel.py
+++ b/fsl/fslview/controls/imagedisplaypanel.py
@@ -70,13 +70,12 @@ class ImageDisplayPanel(fslpanel.FSLViewPanel):
         self._lastImage = None
         self._selectedImageChanged()
 
+        self.propSizer.Layout()
         self.Layout()
-
+        
         pSize = self.propSizer.GetMinSize().Get()
         size  = self.sizer    .GetMinSize().Get()
-
-        self.SetMinSize((max(pSize[0], size[0]),
-                         max(pSize[1], size[1]) / 4.0))
+        self.SetMinSize((max(pSize[0], size[0]), max(pSize[1], size[1]) + 20))
 
         
     def destroy(self):
diff --git a/fsl/fslview/displaycontext/display.py b/fsl/fslview/displaycontext/display.py
index ba4e18cee..81367cac7 100644
--- a/fsl/fslview/displaycontext/display.py
+++ b/fsl/fslview/displaycontext/display.py
@@ -50,6 +50,10 @@ class DisplayOpts(props.SyncableHasProperties):
         self.imageType  = image.imageType
         self.name       = '{}_{}'.format(type(self).__name__, id(self))
 
+        
+    def destroy(self):
+        pass
+
 
 class Display(props.SyncableHasProperties):
     """
@@ -323,6 +327,9 @@ class Display(props.SyncableHasProperties):
 
         if (self.__displayOpts           is None) or \
            (self.__displayOpts.imageType != self.imageType):
+
+            if self.__displayOpts is not None:
+                self.__displayOpts.destroy()
             
             self.__displayOpts = self.__makeDisplayOpts()
             
diff --git a/fsl/fslview/displaycontext/maskopts.py b/fsl/fslview/displaycontext/maskopts.py
index f6e7a3994..4b011d55e 100644
--- a/fsl/fslview/displaycontext/maskopts.py
+++ b/fsl/fslview/displaycontext/maskopts.py
@@ -18,7 +18,8 @@ class MaskOpts(fsldisplay.DisplayOpts):
 
     colour     = props.Colour()
     invert     = props.Boolean(default=False)
-    threshold  = props.Bounds(
+
+    clippingRange = props.Bounds(
         ndims=1,
         labels=[strings.choices['VolumeOpts.displayRange.min'],
                 strings.choices['VolumeOpts.displayRange.max']]) 
@@ -36,10 +37,15 @@ class MaskOpts(fsldisplay.DisplayOpts):
         dRangeLen    = abs(self.dataMax - self.dataMin)
         dMinDistance = dRangeLen / 10000.0
 
-        self.threshold.setMin(  0, self.dataMin - 0.5 * dRangeLen)
-        self.threshold.setMax(  0, self.dataMax + 0.5 * dRangeLen)
-        self.threshold.setRange(0, 0.1, self.dataMax + 0.1)
-        self.setConstraint('threshold', 'minDistance', dMinDistance)
+        self.clippingRange.xmin = self.dataMin - dMinDistance
+        self.clippingRange.xmax = self.dataMax + dMinDistance
+        
+        # By default, the lowest values
+        # in the image are clipped
+        self.clippingRange.xlo  = self.dataMin + dMinDistance
+        self.clippingRange.xhi  = self.dataMax + dMinDistance 
+
+        self.setConstraint('clippingRange', 'minDistance', dMinDistance)
 
         fsldisplay.DisplayOpts.__init__(self,
                                         image,
diff --git a/fsl/fslview/displaycontext/volumeopts.py b/fsl/fslview/displaycontext/volumeopts.py
index 2a981c930..9b0a1d0db 100644
--- a/fsl/fslview/displaycontext/volumeopts.py
+++ b/fsl/fslview/displaycontext/volumeopts.py
@@ -90,6 +90,7 @@ class VolumeOpts(fsldisplay.DisplayOpts):
     _propHelp = _tooltips
 
 
+
     def __init__(self, image, display, imageList, displayCtx, parent=None):
         """Create an :class:`ImageDisplay` for the specified image.
 
@@ -168,6 +169,11 @@ class VolumeOpts(fsldisplay.DisplayOpts):
                                 self.name,
                                 self.displayRangeChanged)
 
+    def destroy(self):
+        self.display.removeListener('brightness',   self.name)
+        self.display.removeListener('contrast',     self.name)
+        self        .removeListener('displayRange', self.name)
+
 
     def briconChanged(self, *a):
         """Called when the ``brightness``/``contrast`` properties of the
diff --git a/fsl/fslview/gl/glmask.py b/fsl/fslview/gl/glmask.py
index 36231c7c0..d347bd496 100644
--- a/fsl/fslview/gl/glmask.py
+++ b/fsl/fslview/gl/glmask.py
@@ -59,7 +59,7 @@ class GLMask(glvolume.GLVolume):
         self.display    .addListener('transform',     lnrName, vertexUpdate)
         self.display    .addListener('alpha',         lnrName, colourUpdate)
         self.displayOpts.addListener('colour',        lnrName, colourUpdate)
-        self.displayOpts.addListener('threshold',     lnrName, colourUpdate)
+        self.displayOpts.addListener('clippingRange', lnrName, colourUpdate)
         self.displayOpts.addListener('invert',        lnrName, colourUpdate)
 
 
@@ -79,7 +79,7 @@ class GLMask(glvolume.GLVolume):
         self.display    .removeListener('volume',        lnrName)
         self.image      .removeListener('data',          lnrName)
         self.displayOpts.removeListener('colour',        lnrName)
-        self.displayOpts.removeListener('threshold',     lnrName)
+        self.displayOpts.removeListener('clippingRange', lnrName)
         self.displayOpts.removeListener('invert',        lnrName) 
 
         
@@ -98,8 +98,8 @@ class GLMask(glvolume.GLVolume):
 
         opts   = self.displayOpts
         colour = opts.colour
-        imin   = opts.threshold[0]
-        imax   = opts.threshold[1]
+        imin   = opts.clippingRange[0]
+        imax   = opts.clippingRange[1]
         
         colour[3] = 1.0
 
diff --git a/fsl/fslview/layouts.py b/fsl/fslview/layouts.py
index 7a97c2a0b..83c3be965 100644
--- a/fsl/fslview/layouts.py
+++ b/fsl/fslview/layouts.py
@@ -182,14 +182,14 @@ DisplayLayout = props.VGroup(
 VolumeOptsLayout = props.VGroup(
     (widget(VolumeOpts, 'cmap'),
      widget(VolumeOpts, 'invert'),
-     widget(VolumeOpts, 'displayRange',  slider=True),
-     widget(VolumeOpts, 'clippingRange', slider=True)))
+     widget(VolumeOpts, 'displayRange',  slider=True, showLimits=False),
+     widget(VolumeOpts, 'clippingRange', slider=True, showLimits=False)))
 
 
 MaskOptsLayout = props.VGroup(
     (widget(MaskOpts, 'colour'),
      widget(MaskOpts, 'invert'),
-     widget(MaskOpts, 'threshold')))
+     widget(MaskOpts, 'clippingRange', showLimits=False)))
 
 
 VectorOptsLayout = props.VGroup((
diff --git a/fsl/tools/fslview_parseargs.py b/fsl/tools/fslview_parseargs.py
index a484a2135..2a3de481d 100644
--- a/fsl/tools/fslview_parseargs.py
+++ b/fsl/tools/fslview_parseargs.py
@@ -128,7 +128,7 @@ OPTIONS = td.TypeDict({
                        'cmap'],
     'MaskOpts'      : ['colour',
                        'invert',
-                       'threshold'],
+                       'clippingRange'],
     'VectorOpts'    : ['displayMode',
                        'xColour',
                        'yColour',
@@ -210,9 +210,9 @@ ARGUMENTS = td.TypeDict({
     'VolumeOpts.cmap'          : ('cm', 'cmap'),
     'VolumeOpts.invert'        : ('ci', 'cmapInvert'),
 
-    'MaskOpts.colour'    : ('co', 'colour'),
-    'MaskOpts.invert'    : ('mi', 'maskInvert'),
-    'MaskOpts.threshold' : ('t',  'threshold'),
+    'MaskOpts.colour'        : ('co', 'colour'),
+    'MaskOpts.invert'        : ('mi', 'maskInvert'),
+    'MaskOpts.clippingRange' : ('t',  'threshold'),
 
     'VectorOpts.displayMode' : ('d',  'displayMode'),
     'VectorOpts.xColour'     : ('xc', 'xColour'),
@@ -280,9 +280,9 @@ HELP = td.TypeDict({
     'VolumeOpts.cmap'          : 'Colour map',
     'VolumeOpts.invert'        : 'Invert colour map',
 
-    'MaskOpts.colour'    : 'Colour',
-    'MaskOpts.invert'    : 'Invert',
-    'MaskOpts.threshold' : 'Threshold',
+    'MaskOpts.colour'        : 'Colour',
+    'MaskOpts.invert'        : 'Invert',
+    'MaskOpts.clippingRange' : 'Threshold',
 
     'VectorOpts.displayMode'  : 'Display mode',
     'VectorOpts.xColour'      : 'X colour',
-- 
GitLab