Skip to content
Snippets Groups Projects
Commit 4a835d45 authored by Paul McCarthy's avatar Paul McCarthy
Browse files

Properties and interface for negative colour map control.

parent 50ca939d
No related branches found
No related tags found
No related merge requests found
...@@ -527,15 +527,16 @@ properties = TypeDict({ ...@@ -527,15 +527,16 @@ properties = TypeDict({
'ImageOpts.transform' : 'Image transform', 'ImageOpts.transform' : 'Image transform',
'ImageOpts.volume' : 'Volume', 'ImageOpts.volume' : 'Volume',
'VolumeOpts.displayRange' : 'Display range', 'VolumeOpts.displayRange' : 'Display range',
'VolumeOpts.clippingRange' : 'Clipping range', 'VolumeOpts.clippingRange' : 'Clipping range',
'VolumeOpts.centreRanges' : 'Centre display/clipping ranges at 0', 'VolumeOpts.linkLowRanges' : 'Link low display/clipping ranges',
'VolumeOpts.linkLowRanges' : 'Link low display/clipping ranges', 'VolumeOpts.linkHighRanges' : 'Link high display/clipping ranges',
'VolumeOpts.linkHighRanges' : 'Link high display/clipping ranges', 'VolumeOpts.cmap' : 'Colour map',
'VolumeOpts.cmap' : 'Colour map', 'VolumeOpts.negativeCmap' : '-ve colour map',
'VolumeOpts.invert' : 'Invert colour map', 'VolumeOpts.enableNegativeCmap' : '-ve colour map',
'VolumeOpts.invertClipping' : 'Invert clipping range', 'VolumeOpts.invert' : 'Invert colour map',
'VolumeOpts.interpolation' : 'Interpolation', 'VolumeOpts.invertClipping' : 'Invert clipping range',
'VolumeOpts.interpolation' : 'Interpolation',
'MaskOpts.colour' : 'Colour', 'MaskOpts.colour' : 'Colour',
'MaskOpts.invert' : 'Invert', 'MaskOpts.invert' : 'Invert',
......
...@@ -275,16 +275,19 @@ def registerColourMap(cmapFile, ...@@ -275,16 +275,19 @@ def registerColourMap(cmapFile,
# update the VolumeOpts colour map property # update the VolumeOpts colour map property
# for any existing VolumeOpts instances # for any existing VolumeOpts instances
cmapProp = fsldisplay.VolumeOpts.getProp('cmap') cmapProp = fsldisplay.VolumeOpts.getProp('cmap')
negCmapProp = fsldisplay.VolumeOpts.getProp('negativeCmap')
for overlay in overlayList: for overlay in overlayList:
opts = displayCtx.getOpts(overlay) opts = displayCtx.getOpts(overlay)
if isinstance(opts, fsldisplay.VolumeOpts): if isinstance(opts, fsldisplay.VolumeOpts):
cmapProp.addColourMap(key, opts) cmapProp .addColourMap(key, opts)
negCmapProp.addColourMap(key, opts)
# and for all future volume overlays # and for all future volume overlays
cmapProp.addColourMap(key) cmapProp .addColourMap(key)
negCmapProp.addColourMap(key)
def registerLookupTable(lut, def registerLookupTable(lut,
......
...@@ -216,15 +216,11 @@ class OverlayDisplayPanel(fslpanel.FSLEyesPanel): ...@@ -216,15 +216,11 @@ class OverlayDisplayPanel(fslpanel.FSLEyesPanel):
for p in dispProps: for p in dispProps:
widget = props.buildGUI(self.__widgets, widget = props.buildGUI(self.__widgets, target, p)
target,
p,
showUnlink=False)
# Add a 'load colour map' button next # Build a panel for the VolumeOpts colour map controls.
# to the VolumeOpts.cmap control
if isinstance(target, displayctx.VolumeOpts) and p.key == 'cmap': if isinstance(target, displayctx.VolumeOpts) and p.key == 'cmap':
widget = self.__buildColourMapWidget(widget) widget = self.__buildColourMapWidget(target, widget)
widgets.append(widget) widgets.append(widget)
...@@ -238,24 +234,43 @@ class OverlayDisplayPanel(fslpanel.FSLEyesPanel): ...@@ -238,24 +234,43 @@ class OverlayDisplayPanel(fslpanel.FSLEyesPanel):
self.Layout() self.Layout()
def __buildColourMapWidget(self, cmapWidget): def __buildColourMapWidget(self, target, cmapWidget):
"""Creates a control which allows the user to load a custom colour """Builds a panel which contains widgets for controlling the
map. This control is added to the settings for :class:`.Image` :attr:`.VolumeOpts.cmap`, :attr:`.VolumeOpts.negativeCmap`, and
overlays with a :attr:`.Display.overlayType` of ``'volume'``. :attr:`.VolumeOpts.enableNegativeCmap`.
""" """
action = loadcmap.LoadColourMapAction(self._displayCtx, widgets = self.__widgets
self._overlayList)
button = wx.Button(self.__widgets) # Button to load a new
button.SetLabel(strings.labels[self, 'loadCmap']) # colour map from file
loadAction = loadcmap.LoadColourMapAction(self._displayCtx,
self._overlayList)
action.bindToWidget(self, wx.EVT_BUTTON, button) loadButton = wx.Button(widgets)
loadButton.SetLabel(strings.labels[self, 'loadCmap'])
sizer = wx.BoxSizer(wx.HORIZONTAL) loadAction.bindToWidget(self, wx.EVT_BUTTON, loadButton)
sizer.Add(cmapWidget, flag=wx.EXPAND, proportion=1) # Negative colour map widget
sizer.Add(button, flag=wx.EXPAND) negCmap = props.Widget('negativeCmap',
enabledWhen=lambda i, enc: enc,
dependencies=['enableNegativeCmap'])
enableNegCmap = props.Widget('enableNegativeCmap')
negCmap = props.buildGUI(widgets, target, negCmap)
enableNegCmap = props.buildGUI(widgets, target, enableNegCmap)
enableNegCmap.SetLabel(
strings.properties[target, 'enableNegativeCmap'])
sizer = wx.FlexGridSizer(2, 2)
sizer.AddGrowableCol(0)
sizer.Add(cmapWidget, flag=wx.EXPAND)
sizer.Add(loadButton, flag=wx.EXPAND)
sizer.Add(negCmap, flag=wx.EXPAND)
sizer.Add(enableNegCmap, flag=wx.EXPAND)
return sizer return sizer
...@@ -288,7 +303,6 @@ _DISPLAY_PROPS = td.TypeDict({ ...@@ -288,7 +303,6 @@ _DISPLAY_PROPS = td.TypeDict({
props.Widget('cmap'), props.Widget('cmap'),
props.Widget('invert'), props.Widget('invert'),
props.Widget('invertClipping'), props.Widget('invertClipping'),
props.Widget('centreRanges'),
props.Widget('linkLowRanges'), props.Widget('linkLowRanges'),
props.Widget('linkHighRanges'), props.Widget('linkHighRanges'),
props.Widget('displayRange', props.Widget('displayRange',
......
...@@ -492,6 +492,28 @@ class VolumeOpts(ImageOpts): ...@@ -492,6 +492,28 @@ class VolumeOpts(ImageOpts):
cmap = props.ColourMap() cmap = props.ColourMap()
"""The colour map, a :class:`matplotlib.colors.Colourmap` instance.""" """The colour map, a :class:`matplotlib.colors.Colourmap` instance."""
negativeCmap = props.ColourMap()
"""A second colour map, used if :attr:`enableNegativeCmap` is ``True``.
When active, the :attr:`cmap` is used to colour positive values, and
the :attr:`negativeCmap` is used to colour negative values.
"""
enableNegativeCmap = props.Boolean(default=False)
"""When ``True``, the :attr:`cmap` is used to colour positive values,
and the :attr:`negativeCmap` is used to colour negative values.
When this property is enabled, the minimum value for both the
:attr:`displayRange` and :attr:`clippingRange` is set to zero. Both
ranges are applied to positive values, and negated/inverted for negative
values.
.. note:: When this property is set to ``True``, the
:attr:`.Display.brightness` and :attr:`.Display.contrast`
properties are disabled, as managing the interaction between
them would be far too complicated.
"""
interpolation = props.Choice(('none', 'linear', 'spline')) interpolation = props.Choice(('none', 'linear', 'spline'))
"""How the value shown at a real world location is derived from the """How the value shown at a real world location is derived from the
...@@ -506,19 +528,6 @@ class VolumeOpts(ImageOpts): ...@@ -506,19 +528,6 @@ class VolumeOpts(ImageOpts):
""" """
centreRanges = props.Boolean(default=False)
"""If ``True``, the :attr:`displayRange` and :attr:`clippingRange` ranges
will be kept centred at zero. A change to the negative end of either range
will result in the positive end being changed, and vice versa.
.. note:: When this property is set to ``True``, the
:attr:`.Display.brightness`, :attr:`.Display.contrast`,
:attr:`.linkLowRanges` and :attr:`.linkHighRanges` properties
are all disabled, as managing the interaction between all of
them would be far too complicated.
"""
linkLowRanges = props.Boolean(default=True) linkLowRanges = props.Boolean(default=True)
"""If ``True``, the low bounds on both the :attr:`displayRange` and """If ``True``, the low bounds on both the :attr:`displayRange` and
:attr:`clippingRange` ranges will be linked together. :attr:`clippingRange` ranges will be linked together.
...@@ -598,9 +607,9 @@ class VolumeOpts(ImageOpts): ...@@ -598,9 +607,9 @@ class VolumeOpts(ImageOpts):
self .addListener('displayRange', self .addListener('displayRange',
self.name, self.name,
self.__displayRangeChanged) self.__displayRangeChanged)
self .addListener('centreRanges', self .addListener('enableNegativeCmap',
self.name, self.name,
self.__centreRangesChanged) self.__enableNegativeCmapChanged)
self .addListener('linkLowRanges', self .addListener('linkLowRanges',
self.name, self.name,
self.__linkLowRangesChanged) self.__linkLowRangesChanged)
...@@ -621,14 +630,13 @@ class VolumeOpts(ImageOpts): ...@@ -621,14 +630,13 @@ class VolumeOpts(ImageOpts):
display, display,
display.getSyncPropertyName('contrast')) display.getSyncPropertyName('contrast'))
# If centreRanges, linkLowRanges or linkHighRanges # If enableNegativeCmap, linkLowRanges or linkHighRanges
# have been set to True (this will happen if they # have been set to True (this will happen if they
# are true on the parent VolumeOpts instance), make # are true on the parent VolumeOpts instance), make
# sure the property / listener states are up to date. # sure the property / listener states are up to date.
if self.centreRanges: self.__centreRangesChanged() if self.enableNegativeCmap: self.__enableNegativeCmapChanged()
else: if self.linkLowRanges: self.__linkLowRangesChanged()
if self.linkLowRanges: self.__linkLowRangesChanged() if self.linkHighRanges: self.__linkHighRangesChanged()
if self.linkHighRanges: self.__linkHighRangesChanged()
@actions.action @actions.action
...@@ -733,7 +741,7 @@ class VolumeOpts(ImageOpts): ...@@ -733,7 +741,7 @@ class VolumeOpts(ImageOpts):
See :func:`.colourmaps.displayRangeToBricon`. See :func:`.colourmaps.displayRangeToBricon`.
""" """
if self.centreRanges: if self.enableNegativeCmap:
return return
brightness, contrast = fslcm.displayRangeToBricon( brightness, contrast = fslcm.displayRangeToBricon(
...@@ -749,32 +757,24 @@ class VolumeOpts(ImageOpts): ...@@ -749,32 +757,24 @@ class VolumeOpts(ImageOpts):
self.__toggleListeners(True) self.__toggleListeners(True)
def __centreRangesChanged(self, *a): def __enableNegativeCmapChanged(self, *a):
"""Called when the :attr:`centreRanges` property changes. Configures """Called when the :attr:`enableNegativeCmap` property changes.
property listeners on the :attr:`clippingRange` and Enables/disables the :attr:`.Display.brightness` and
:attr:`displayRange` properties. :attr:`.Display.contrast` properties, and configures limits
on the :attr:`clippingRange` and :attr:`displayRange` properties.
""" """
if self.centreRanges: if self.enableNegativeCmap:
self.display.disableProperty('brightness') self.display.disableProperty('brightness')
self.display.disableProperty('contrast') self.display.disableProperty('contrast')
self .disableProperty('linkLowRanges') self.displayRange .xmin = 0.0
self .disableProperty('linkHighRanges') self.clippingRange.xmin = 0.0
self.setConstraint('displayRange', 'dimCentres', [0.0])
self.setConstraint('clippingRange', 'dimCentres', [0.0])
# Make sure that lowLinkRanges and
# highLinkRanges are not active
self.__linkRangesChanged(False, 0)
self.__linkRangesChanged(False, 1)
else: else:
self.display.enableProperty('brightness') self.display.enableProperty('brightness')
self.display.enableProperty('contrast') self.display.enableProperty('contrast')
self .enableProperty('linkLowRanges') self.displayRange .xmin = self.dataMin
self .enableProperty('linkHighRanges') self.clippingRange.xmin = self.dataMin
self.setConstraint('displayRange', 'dimCentres', [None])
self.setConstraint('clippingRange', 'dimCentres', [None])
def __linkLowRangesChanged(self, *a): def __linkLowRangesChanged(self, *a):
......
...@@ -80,12 +80,12 @@ properties = TypeDict({ ...@@ -80,12 +80,12 @@ properties = TypeDict({
'voxels outside of the range are displayed.' 'voxels outside of the range are displayed.'
'This option is useful for displaying ' 'This option is useful for displaying '
'statistic images.', 'statistic images.',
'VolumeOpts.centreRanges' : 'If checked, the low and high values ' 'VolumeOpts.cmap' : 'The colour map to use.',
'of both the clipping and display ' 'VolumeOpts.negativeCmap' : 'The colour map to use for negative '
'ranges will be yoked together, so ' 'values.',
'that both ranges stay centered at ' 'VolumeOpts.enableNegativeCmap' : 'Enable the negative colour map - '
'zero.' , 'this allows positive and negative '
'VolumeOpts.cmap' : 'The colour map to use.', 'values to be coloured independently.',
'VolumeOpts.interpolation' : 'Interpolate the image data for display ' 'VolumeOpts.interpolation' : 'Interpolate the image data for display '
'purposes. You can choose no ' 'purposes. You can choose no '
'interpolation (equivalent to nearest ' 'interpolation (equivalent to nearest '
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment