From 085ca86e63260155db6f7a8d34290b789017fd17 Mon Sep 17 00:00:00 2001
From: Paul McCarthy <pauld.mccarthy@gmail.com>
Date: Mon, 7 Sep 2015 13:52:43 +0100
Subject: [PATCH] 1. Documentation for all DisplayOpts modules.

2. Updated documentation references to __init__ methods, as sphinx
   doesn't resolve them.

3. Bug fix in featresults module when loading contrast matrix.
---
 fsl/data/featimage.py                        |   2 +-
 fsl/data/featresults.py                      |   2 +-
 fsl/data/image.py                            |   9 +-
 fsl/fsleyes/actions/__init__.py              |   3 +-
 fsl/fsleyes/actions/copyoverlay.py           |   2 +-
 fsl/fsleyes/actions/saveoverlay.py           |   2 +-
 fsl/fsleyes/displaycontext/display.py        |   2 +-
 fsl/fsleyes/displaycontext/displaycontext.py |   8 +-
 fsl/fsleyes/displaycontext/labelopts.py      |  39 +++++-
 fsl/fsleyes/displaycontext/lightboxopts.py   |   2 +-
 fsl/fsleyes/displaycontext/maskopts.py       |  30 +++-
 fsl/fsleyes/displaycontext/modelopts.py      |  99 ++++++++++++--
 fsl/fsleyes/displaycontext/orthoopts.py      |   2 +-
 fsl/fsleyes/displaycontext/vectoropts.py     |  50 +++++--
 fsl/fsleyes/displaycontext/volumeopts.py     | 137 +++++++++++++------
 fsl/fsleyes/frame.py                         |   2 +-
 16 files changed, 298 insertions(+), 93 deletions(-)

diff --git a/fsl/data/featimage.py b/fsl/data/featimage.py
index 59d409158..6c536ba10 100644
--- a/fsl/data/featimage.py
+++ b/fsl/data/featimage.py
@@ -55,7 +55,7 @@ class FEATImage(fslimage.Image):
         :arg path:   A FEAT analysis directory, or an image file contained
                      within such a directory.
 
-        :arg kwargs: Passed to the :meth:`.Image.__init__` constructor.
+        :arg kwargs: Passed to the :class:`.Image` constructor.
 
         .. note:: If a FEAT directory is passed in for the ``path``
                   argument, this ``FEATImage`` instance will encapsulate
diff --git a/fsl/data/featresults.py b/fsl/data/featresults.py
index 1a65c2ce9..5148d99d5 100644
--- a/fsl/data/featresults.py
+++ b/fsl/data/featresults.py
@@ -170,7 +170,7 @@ def loadContrasts(featdir):
             elif line == '/Matrix':
                 break
 
-        matrix = np.loadtxt(f)
+        matrix = np.loadtxt(f, ndmin=2)
 
     if matrix       is None             or \
        numContrasts != matrix.shape[0]:
diff --git a/fsl/data/image.py b/fsl/data/image.py
index 2547614fd..31988dc6e 100644
--- a/fsl/data/image.py
+++ b/fsl/data/image.py
@@ -535,10 +535,11 @@ def loadImage(filename):
     """Given the name of an image file, loads it using nibabel.
 
     If the file is large, and is gzipped, it is decompressed to a temporary
-    location, so that it can be memory-mapped.  A tuple is returned,
-    consisting of the nibabel image object, and the name of the file that it
-    was loaded from (either the passed-in file name, or the name of the
-    temporary decompressed file).
+    location, so that it can be memory-mapped.
+
+    In any case, a tuple is returned, consisting of the nibabel image object,
+    and the name of the file that it was loaded from (either the passed-in
+    file name, or the name of the temporary decompressed file).
     """
 
     # If we have a GUI, we can display a dialog
diff --git a/fsl/fsleyes/actions/__init__.py b/fsl/fsleyes/actions/__init__.py
index 8101d5ba5..f789ab0c5 100644
--- a/fsl/fsleyes/actions/__init__.py
+++ b/fsl/fsleyes/actions/__init__.py
@@ -241,7 +241,8 @@ class ActionProvider(props.SyncableHasProperties):
                           mappings, where each function is an action that
                           should be made available to the user.
 
-        :arg kwargs:      Passed to :meth:`.SyncableHasProperties.__init__`.
+        :arg kwargs:      Passed to the :class:`.SyncableHasProperties`
+                          constructor.
         """
 
         actions = kwargs.pop('actions', None)
diff --git a/fsl/fsleyes/actions/copyoverlay.py b/fsl/fsleyes/actions/copyoverlay.py
index e7205d35c..67fbd04ad 100644
--- a/fsl/fsleyes/actions/copyoverlay.py
+++ b/fsl/fsleyes/actions/copyoverlay.py
@@ -27,7 +27,7 @@ class CopyOverlayAction(actions.Action):
     
     def __init__(self, *args, **kwargs):
         """Create a ``CopyOverlayAction``. All arguments are passed through
-        to the :meth:`.Action.__init__` constructor.
+        to the :class:`.Action` constructor.
         """
         actions.Action.__init__(self, *args, **kwargs)
 
diff --git a/fsl/fsleyes/actions/saveoverlay.py b/fsl/fsleyes/actions/saveoverlay.py
index ddbcb4508..56d95c38a 100644
--- a/fsl/fsleyes/actions/saveoverlay.py
+++ b/fsl/fsleyes/actions/saveoverlay.py
@@ -26,7 +26,7 @@ class SaveOverlayAction(actions.Action):
     
     def __init__(self, *args, **kwargs):
         """Create a ``SaveOverlayAction``. All arguments are passed through
-        to the :meth:`.Action.__init__` constructor.
+        to the :class:`.Action` constructor.
         """
         actions.Action.__init__(self, *args, **kwargs)        
 
diff --git a/fsl/fsleyes/displaycontext/display.py b/fsl/fsleyes/displaycontext/display.py
index 6fa4ce49a..43cdbe300 100644
--- a/fsl/fsleyes/displaycontext/display.py
+++ b/fsl/fsleyes/displaycontext/display.py
@@ -397,7 +397,7 @@ class DisplayOpts(actions.ActionProvider):
                           how the overlays are to be displayed.
         
         :arg kwargs:      Passed through to the
-                          :meth:`.ActionProvider.__init__` constructor.
+                          :class:`.ActionProvider` constructor.
         """
 
         actions.ActionProvider.__init__(
diff --git a/fsl/fsleyes/displaycontext/displaycontext.py b/fsl/fsleyes/displaycontext/displaycontext.py
index 73c69bc96..53d6217f9 100644
--- a/fsl/fsleyes/displaycontext/displaycontext.py
+++ b/fsl/fsleyes/displaycontext/displaycontext.py
@@ -108,8 +108,8 @@ class DisplayContext(props.SyncableHasProperties):
     every overlay will be unsynchronised from the parent.
 
     .. note:: This property is accessed by the :class:`.Display` class, in its
-             :meth:`.Display.__init__` method, and when it creates new
-             :class:`.DisplayOpts` instances, to set initial sync states.
+              constructor, and when it creates new :class:`.DisplayOpts`
+              instances, to set initial sync states.
     """
 
 
@@ -120,7 +120,7 @@ class DisplayContext(props.SyncableHasProperties):
 
         :arg parent:      Another ``DisplayContext`` instance to be used
                           as the parent of this instance, passed to the
-                          :meth:`.SyncableHasProperties.__init__` constructor.
+                          :class:`.SyncableHasProperties` constructor.
         """
 
         props.SyncableHasProperties.__init__(
@@ -208,7 +208,7 @@ class DisplayContext(props.SyncableHasProperties):
         :arg overlayType: If a ``Display`` instance for the specified
                           ``overlay`` does not exist, one is created - in
                           this case, the specified ``overlayType`` is passed
-                          to the :meth:`.Display.__init__` method.
+                          to the :class:`.Display` constructor.
         """
 
         if overlay is None:
diff --git a/fsl/fsleyes/displaycontext/labelopts.py b/fsl/fsleyes/displaycontext/labelopts.py
index 86dcbbcfc..1665e762d 100644
--- a/fsl/fsleyes/displaycontext/labelopts.py
+++ b/fsl/fsleyes/displaycontext/labelopts.py
@@ -1,9 +1,13 @@
 #!/usr/bin/env python
 #
-# labelopts.py -
+# labelopts.py - The LabelOpts class.
 #
 # Author: Paul McCarthy <pauldmccarthy@gmail.com>
 #
+"""This module provides the :class:`LabelOpts` class, which defines settings
+for displaying :class:`.Image` overlays as label images., such as anatomical
+atlas images, tissue segmentation images, and so on.
+"""
 
 import props
 
@@ -13,14 +17,39 @@ import fsl.fsleyes.colourmaps as fslcm
 
 
 class LabelOpts(volumeopts.ImageOpts):
-
-    lut          = props.Choice()
-    outline      = props.Boolean(default=False)
+    """The ``LabelOpts`` class defines settings for displaying
+    :class:`.Image` overlays as label images., such as anatomical atlas
+    images, tissue segmentation images, and so on.
+    """
+
+    lut = props.Choice()
+    """The :class:`.LookupTable` used to colour each label.
+    """
+    
+    outline = props.Boolean(default=False)
+    """If ``True`` only the outline of contiguous regions with the same label
+    value will be shown. If ``False``, contiguous regions will be filled.
+    """
+
+    
     outlineWidth = props.Real(minval=0, maxval=1, default=0.25, clamped=True)
-    showNames    = props.Boolean(default=False)
+    """Width of labelled region outlines, if :attr:``outline` is ``True``.
+    This value is in terms of the image voxels - a value of 1 will result in
+    an outline that is one voxel wide.
+    """
+
+    
+    showNames = props.Boolean(default=False)
+    """If ``True``, region names (as defined by the current
+    :class:`.LookupTable`) will be shown alongside each labelled region.
+    """
 
 
     def __init__(self, overlay, *args, **kwargs):
+        """Create a ``LabelOpts`` instance for the specified ``overlay``.
+        All arguments are passed through to the :class:`.ImageOpts`
+        constructor.
+        """
         volumeopts.ImageOpts.__init__(self, overlay, *args, **kwargs)
 
         luts  = fslcm.getLookupTables()
diff --git a/fsl/fsleyes/displaycontext/lightboxopts.py b/fsl/fsleyes/displaycontext/lightboxopts.py
index a1ed726d6..c8cb53c76 100644
--- a/fsl/fsleyes/displaycontext/lightboxopts.py
+++ b/fsl/fsleyes/displaycontext/lightboxopts.py
@@ -35,7 +35,7 @@ class LightBoxOpts(sceneopts.SceneOpts):
     
     def __init__(self, *args, **kwargs):
         """Create a ``LightBoxOpts`` instance. All arguments are passed
-        through to the :meth:`.SceneOpts.__init__` method.
+        through to the :class:`.SceneOpts` constructor.
 
         The :attr:`.SceneOpts.zoom` attribute is modified, as
         :class:`LightBoxPanel` uses it slightly differently to the
diff --git a/fsl/fsleyes/displaycontext/maskopts.py b/fsl/fsleyes/displaycontext/maskopts.py
index 6616f4752..972f1eb18 100644
--- a/fsl/fsleyes/displaycontext/maskopts.py
+++ b/fsl/fsleyes/displaycontext/maskopts.py
@@ -1,9 +1,12 @@
 #!/usr/bin/env python
 #
-# maskopts.py -
+# maskopts.py - The MaskOpts class.
 #
 # Author: Paul McCarthy <pauldmccarthy@gmail.com>
 #
+"""This module provides the :class:`MaskOpts` class, which defines settings
+for displaying an :class:`.Image` overlay as a binary mask.
+"""
 
 
 import numpy as np
@@ -15,15 +18,32 @@ import                     volumeopts
 
 
 class MaskOpts(volumeopts.ImageOpts):
+    """The ``MaskOpts`` class defines settings for displaying an
+    :class:`.Image` overlay as a binary mask.
+    """
 
-    colour     = props.Colour()
-    invert     = props.Boolean(default=False)
-    threshold  = props.Bounds(
+    threshold = props.Bounds(
         ndims=1,
         labels=[strings.choices['VolumeOpts.displayRange.min'],
-                strings.choices['VolumeOpts.displayRange.max']])
+                strings.choices['VolumeOpts.displayRange.max']]) 
+    """The mask threshold range - values outside of this range are not
+    displayed.
+    """
+    
+    invert = props.Boolean(default=False)
+    """If ``True``, the :attr:`threshold` range is inverted - values
+    inside the range are not shown, and values outside of the range are shown.
+    """
+
+    
+    colour = props.Colour()
+    """The mask colour."""
+
 
     def __init__(self, overlay, *args, **kwargs):
+        """Create a ``MaskOpts`` instance for the given overlay. All arguments
+        are passed through to the :class:`.ImageOpts` constructor.
+        """
 
         if np.prod(overlay.shape) > 2 ** 30:
             sample = overlay.data[..., overlay.shape[-1] / 2]
diff --git a/fsl/fsleyes/displaycontext/modelopts.py b/fsl/fsleyes/displaycontext/modelopts.py
index 5491ba4bf..7f10ad46e 100644
--- a/fsl/fsleyes/displaycontext/modelopts.py
+++ b/fsl/fsleyes/displaycontext/modelopts.py
@@ -1,9 +1,13 @@
 #!/usr/bin/env python
 #
-# modelopts.py -
+# modelopts.py - The ModelOpts class.
 #
 # Author: Paul McCarthy <pauldmccarthy@gmail.com>
 #
+"""This module provides the :class:`ModelOpts` class, which defines settings
+for displaying :class:`.Model` overlays.
+"""
+
 
 import copy
 
@@ -21,17 +25,61 @@ import volumeopts
 
 
 class ModelOpts(fsldisplay.DisplayOpts):
+    """The ``ModelOpts`` class defines settings for displaying :class:`.Model`
+    overlays.
+    """
+
+    colour = props.Colour()
+    """The model colour. """
+
+    
+    outline = props.Boolean(default=False)
+    """If ``True``, an outline of the model is shown. Otherwise the model is
+    filled.
+    """
 
-    colour       = props.Colour()
-    outline      = props.Boolean(default=False)
+    
     outlineWidth = props.Real(minval=0, maxval=1, default=0.25, clamped=True)
-    showName     = props.Boolean(default=False)
-    refImage     = props.Choice()
-    coordSpace   = copy.copy(volumeopts.ImageOpts.transform)
-    transform    = copy.copy(volumeopts.ImageOpts.transform)
+    """If :attr:`outline` is ``True``, this property defines the width of the
+    outline.
+    """
+    
+
+    showName = props.Boolean(default=False)
+    """If ``True``, the model name is shown alongside the model. """
+
+    
+    refImage = props.Choice()
+    """A reference :class:`.Image` instance which the model coordinates are
+    in terms of.
+
+    For example, if this :class:`.Model` represents the segmentation of a
+    sub-cortical region from a T1 image, you would set the ``refImage`` to that
+    T1 image.
+
+    Any :class:`.Image` instance in the :class:`.OverlayList` may be chosen
+    as the reference image.
+    """
+
+    
+    coordSpace = copy.copy(volumeopts.ImageOpts.transform)
+    """If :attr:`refImage` is not ``None``, this property defines the
+    reference image coordinate space in whihc the model coordinates are
+    defined (i.e. voxels, scaled voxels, or world coordinates).
+    """
+
+    
+    transform = copy.copy(volumeopts.ImageOpts.transform)
+    """If :attr:`refImage` is not ``None``, this property is bound to 
+    the :attr:`~.ImageOpts.transform` property of the reference image
+    :class:`.ImageOpts` instance.
+    """
 
 
     def __init__(self, *args, **kwargs):
+        """Create a ``ModelOpts`` instance. All arguments are passed through
+        to the :class:`.DisplayOpts` constructor.
+        """
 
         # Create a random, highly
         # saturated colour
@@ -73,6 +121,9 @@ class ModelOpts(fsldisplay.DisplayOpts):
 
 
     def destroy(self):
+        """Removes some property listeners, and calls the
+        :meth:`.DisplayOpts.destroy` method.
+        """
         self.overlayList.removeListener('overlays', self.name)
 
         for overlay in self.overlayList:
@@ -92,9 +143,14 @@ class ModelOpts(fsldisplay.DisplayOpts):
 
     
     def getCoordSpaceTransform(self):
+        """Returns a transformation matrix which can be used to transform
+        the :class:`.Model` vertex coordinates into the display coordinate
+        system.
 
-        if self.refImage is None or \
-           self.coordSpace == self.transform:
+        If no :attr:`refImage` is selected, this method returns ``None``.
+        """
+
+        if self.refImage is None or self.coordSpace == self.transform:
             return None
 
         opts = self.displayCtx.getOpts(self.refImage)
@@ -103,6 +159,11 @@ class ModelOpts(fsldisplay.DisplayOpts):
 
 
     def transformDisplayLocation(self, oldLoc):
+        """If the :attr:`refImage`, :attr:`coordSpace` or :attr:`transform`
+        properties have changed, this method will transform the specified
+        location from the old :class:`.Model` display coordinate system to
+        the new model display coordinate system.
+        """
 
         newLoc   = oldLoc
         propName = self.__lastPropChanged
@@ -152,16 +213,32 @@ class ModelOpts(fsldisplay.DisplayOpts):
 
 
     def __transformChanged(self, *a):
+        """Called when the :attr:`transfrom` property changes.
+        Calls :meth:`__updateBounds`.
+        """
         self.__lastPropChanged = 'transform'
         self.__updateBounds()
 
 
     def __coordSpaceChanged(self, *a):
+        """Called when the :attr:`coordSpace` property changes.
+        Calls :meth:`__updateBounds`.
+        """ 
         self.__lastPropChanged = 'coordSpace'
         self.__updateBounds()
 
 
     def __refImageChanged(self, *a):
+        """Called when the :attr:`refImage` property changes.  Configures the
+        :attr:`transform` property to track the :attr:`~.ImageOpts.transform`
+        property of the :class:`.ImageOpts` instance associated with the new
+        reference image, and calls :meth:`__updateBounds`.
+        """
+
+        # TODO You are not tracking changes to the
+        # refImage overlay type -  if this changes,
+        # you will need to re-bind to the transform
+        # property of the new DisplayOpts instance
 
         self.__lastPropChanged = 'refImage'
 
@@ -200,7 +277,7 @@ class ModelOpts(fsldisplay.DisplayOpts):
             
     
     def __overlayListChanged(self, *a):
-        """Called when the overlay list changes. Updates the ``refImage``
+        """Called when the overlay list changes. Updates the :attr:`refImage`
         property so that it contains a list of overlays which can be
         associated with the model.
         """
@@ -219,7 +296,7 @@ class ModelOpts(fsldisplay.DisplayOpts):
 
         for overlay in overlays:
             
-            # The image must be an Image instance.
+            # The overlay must be an Image instance.
             if not isinstance(overlay, fslimage.Image):
                 continue
 
diff --git a/fsl/fsleyes/displaycontext/orthoopts.py b/fsl/fsleyes/displaycontext/orthoopts.py
index 5fd075c7e..942b4a53e 100644
--- a/fsl/fsleyes/displaycontext/orthoopts.py
+++ b/fsl/fsleyes/displaycontext/orthoopts.py
@@ -66,7 +66,7 @@ class OrthoOpts(sceneopts.SceneOpts):
     
     def __init__(self, *args, **kwargs):
         """Create an ``OrthoOpts`` instance. All arguments are passed
-        through to the :meth:`.SceneOpts.__init__` constructor.
+        through to the :class:`.SceneOpts` constructor.
 
         This method sets up a binding from the :attr:`.SceneOpts.zoom`
         property to the :attr:`xzoom`, :attr:`yzoom`, and :attr:`zzoom`
diff --git a/fsl/fsleyes/displaycontext/vectoropts.py b/fsl/fsleyes/displaycontext/vectoropts.py
index 8bf19edf7..a9eace481 100644
--- a/fsl/fsleyes/displaycontext/vectoropts.py
+++ b/fsl/fsleyes/displaycontext/vectoropts.py
@@ -4,10 +4,12 @@
 #
 # Author: Paul McCarthy <pauldmccarthy@gmail.com>
 #
-"""This module defines the :class:`VectorOpts` class, which contains display
-options for rendering :class:`.GLVector` instances.
+"""This module provides the :class:`VectorOpts`, :class:`LineVectorOpts`, and
+:class:`RGBVectorOpts` classes, which contain options for displaying NIFTI1
+vector images.
 """
 
+
 import copy
 
 import props
@@ -17,6 +19,10 @@ import                   volumeopts
 
 
 class VectorOpts(volumeopts.ImageOpts):
+    """The ``VectorOpts`` class is the base class for :class:`LineVectorOpts` and
+    :class:`RGBVectorOpts`. It contains display settings which are common to
+    both.
+    """
 
 
     xColour = props.Colour(default=(1.0, 0.0, 0.0))
@@ -44,20 +50,25 @@ class VectorOpts(volumeopts.ImageOpts):
 
 
     modulate  = props.Choice()
-    """Modulate the vector colours by another image."""
+    """Modulate the vector colours by another image. Any image which is in the
+     :class:`.OverlayList`, and which has the same voxel dimensions as the
+     vector image can be selected for modulation.
+    """
 
     
     # TODO This is currently a percentage
     # of the modulation image data range.
     # It should be an absolute value
     modThreshold = props.Percentage(default=0.0)
-    """Hide voxels for which the modulation value is below this threshold."""
+    """Hide voxels for which the modulation value is below this threshold,
+    as a percentage of the :attr:`modulate` image data range.
+    """
 
     
     def __init__(self, *args, **kwargs):
-        """Create a ``VectorOpts`` instance for the given image.
-
-        See the :class:`.ImageOpts` documentation for more details.
+        """Create a ``VectorOpts`` instance for the given image.  All
+        arguments are passed through to the :class:`.ImageOpts`
+        constructor.
         """
         
         volumeopts.ImageOpts.__init__(self, *args, **kwargs)
@@ -70,6 +81,9 @@ class VectorOpts(volumeopts.ImageOpts):
 
 
     def destroy(self):
+        """Removes some property listeners, and calls the
+        :meth:`.ImageOpts.destroy` method.
+        """
         self.overlayList.removeListener('overlays', self.name)
 
         for overlay in self.overlayList:
@@ -80,7 +94,7 @@ class VectorOpts(volumeopts.ImageOpts):
 
         
     def __overlayListChanged(self, *a):
-        """Called when the overlay list changes. Updates the ``modulate``
+        """Called when the overlay list changes. Updates the :attr:`modulate`
         property so that it contains a list of overlays which could be used
         to modulate the vector image.
         """
@@ -129,17 +143,25 @@ class VectorOpts(volumeopts.ImageOpts):
 
 
 class LineVectorOpts(VectorOpts):
+    """The ``LineVectorOpts`` class contains settings for displaying vector
+    images, using a line to represent the vector value at each voxel.
+    """
 
+    
     lineWidth = props.Int(minval=1, maxval=10, default=1)
-
-    directed  = props.Boolean(default=False)
+    """Width of the line in pixels.
     """
 
-    The directed property cannot be unbound across multiple LineVectorOpts
-    instances, as it affects the OpenGL representation
+    directed = props.Boolean(default=False)
+    """If ``True``, the vector data is interpreted as directed. Otherwise,
+    the vector data is assumed to be undirected.
     """
 
+    
     def __init__(self, *args, **kwargs):
+        """Create a ``LineVectorOpts`` instance. All arguments are passed
+        through  to the :class:`VectorOpts` constructor.
+        """
 
         kwargs['nounbind'] = ['directed']
 
@@ -148,5 +170,9 @@ class LineVectorOpts(VectorOpts):
 
 
 class RGBVectorOpts(VectorOpts):
+    """The ``RGBVectorOpts`` class contains settings for displaying vector
+    images, using a combination of three colours to represent the vector value
+    at each voxel.
+    """
 
     interpolation = copy.copy(volumeopts.VolumeOpts.interpolation)
diff --git a/fsl/fsleyes/displaycontext/volumeopts.py b/fsl/fsleyes/displaycontext/volumeopts.py
index 2635e1d01..19fd4331a 100644
--- a/fsl/fsleyes/displaycontext/volumeopts.py
+++ b/fsl/fsleyes/displaycontext/volumeopts.py
@@ -7,6 +7,8 @@
 """This module defines the :class:`ImageOpts` and :class:`VolumeOpts` classes.
 
 
+.. _volumeopts-coordinate-systems:
+
 ---------------------------------------
 An important note on coordinate systems
 ---------------------------------------
@@ -28,7 +30,7 @@ overlays can potentially be displayed in one of three coordinate systems:
  **world** space        (a.k.a. ``affine``) The image data voxel coordinates
                         are transformed by the ``qform``/``sform``
                         transformation matrix stored in the NIFTI1 header.
- ==================== ====================================================
+ ====================== ====================================================
 
 
 The :attr:`Image.transform` property controls how the image data is
@@ -108,38 +110,37 @@ class ImageOpts(fsldisplay.DisplayOpts):
     """The ``ImageOpts`` class describes how an :class:`.Image` overlay
     should be displayed.
 
+    
     ``ImageOpts`` is the base class for a number of :class:`.DisplayOpts`
-    sub-classes - it contains display options which are common to all.
+    sub-classes - it contains display options which are common to all overlay
+    types that represent a NIFTI1 image.
     """
 
     
     volume = props.Int(minval=0, maxval=0, default=0, clamped=True)
-    """If the ``Image`` is 4D, the current volume to display."""    
+    """If the ``Image`` is 4D, the current volume to display.""" 
 
     
     resolution = props.Real(maxval=10, default=1, clamped=True)
     """Data resolution in the image world coordinate system. The minimum
-    value is set in :meth:`__init__`.
+    value is configured in :meth:`__init__`.
     """ 
 
 
     transform = props.Choice(('affine', 'pixdim', 'id'), default='pixdim')
-    """This property defines how the overlay should be transformd into the
-    display coordinate system.
-
-      - ``affine``: Use the affine transformation matrix stored in the image
-        (the ``qform``/``sform`` fields in NIFTI1 headers).
-                    
-      - ``pixdim``: Scale voxel sizes by the ``pixdim`` fields in the image
-        header.
-    
-      - ``id``: Perform no scaling or transformation - voxels will be
-        interpreted as :math:`1mm^3` isotropic, with the origin at voxel
-        (0,0,0).
+    """This property defines how the overlay should be transformd into
+    the display coordinate system. See the
+    :ref:`note on coordinate systems <volumeopts-coordinate-systems>`
+    for important information regarding this property.
     """
 
  
     def __init__(self, *args, **kwargs):
+        """Create an ``ImageOpts`` instance.
+
+        All arguments are passed through to the :class:`.DisplayOpts`
+        constructor.
+        """
 
         # The transform property cannot be unsynced
         # across different displays, as it affects
@@ -172,11 +173,14 @@ class ImageOpts(fsldisplay.DisplayOpts):
 
 
     def destroy(self):
+        """Calls the :meth:`.DisplayOpts.destroy` method. """
         fsldisplay.DisplayOpts.destroy(self)
 
         
     def __transformChanged(self, *a):
-        """Calculates the min/max values of a 3D bounding box, in the display
+        """Called when the :attr:`transform` property changes.
+        
+        Calculates the min/max values of a 3D bounding box, in the display
         coordinate system, which is big enough to contain the image. Sets the
         :attr:`.DisplayOpts.bounds` property accordingly.
         """
@@ -234,20 +238,24 @@ class ImageOpts(fsldisplay.DisplayOpts):
         """Return a matrix which may be used to transform coordinates
         from ``from_`` to ``to``. Valid values for ``from_`` and ``to``
         are:
-          - ``id``:      Voxel coordinates
+
         
-          - ``pixdim``:  Voxel coordinates, scaled by voxel dimensions
+        =========== ======================================================
+        ``id``      Voxel coordinates
         
-          - ``affine``:  World coordinates, as defined by the NIFTI1
-                         ``qform``/``sform``. See
-                         :attr:`~fsl.data.image.Image.voxToWorldMat`.
+        ``pixdim``  Voxel coordinates, scaled by voxel dimensions
         
-          - ``voxel``:   Equivalent to ``id``.
+        ``affine``  World coordinates, as defined by the NIFTI1
+                    ``qform``/``sform``. See :attr:`.Image.voxToWorldMat`.
         
-          - ``display``: Equivalent to the current value of :attr:`transform`.
+        ``voxel``   Equivalent to ``id``.
         
-          - ``world``;   Equivalent to ``affine``.
+        ``display`` Equivalent to the current value of :attr:`transform`.
+        
+        ``world``   Equivalent to ``affine``.
+        =========== ======================================================
 
+        
         If the ``xform`` parameter is provided, and one of ``from_`` or ``to``
         is ``display``, the value of ``xform`` is used instead of the current
         value of :attr:`transform`.
@@ -268,9 +276,9 @@ class ImageOpts(fsldisplay.DisplayOpts):
 
 
     def getTransformOffsets(self, from_, to_):
-        """When an image is displayed in id/pixdim space, voxel coordinates
-        map to the voxel corner; i.e.  a voxel at ``(0, 1, 2)`` occupies the
-        space ``(0 - 1, 1 - 2, 2 - 3)``.
+        """When an image is displayed in ``id``/``pixdim`` space, voxel
+        coordinates map to the voxel corner; i.e.  a voxel at ``(0, 1, 2)``
+        occupies the space ``(0 - 1, 1 - 2, 2 - 3)``.
         
         In contrast, when an image is displayed in affine space, voxel
         coordinates map to the voxel centre, so our voxel from above will
@@ -289,6 +297,12 @@ class ImageOpts(fsldisplay.DisplayOpts):
         See also the :meth:`transformCoords` method, which will perform the
         transformation correctly for you, without you having to worry about
         these offsets.
+
+
+        .. note:: These offsets, and this method, will soon become obsolete -
+                  see the note about **GedMode** in the
+                  :ref:`note on coordinate systems
+                  <volumeopts-coordinate-systems>`.
         """ 
         displaySpace = self.transform
         pixdim       = np.array(self.overlay.pixdim[:3])
@@ -354,9 +368,10 @@ class ImageOpts(fsldisplay.DisplayOpts):
         correcting for display space offsets (see :meth:`getTransformOffsets`).
 
         The ``from_`` and ``to_`` parameters must both be one of:
-           - ``display``
-           - ``voxel``
-           - ``world``
+        
+           - ``display``: The display coordinate system
+           - ``voxel``:   The image voxel coordinate system
+           - ``world``:   The image world coordinate system
         """
 
         xform     = self.getTransform(       from_, to_)
@@ -369,6 +384,12 @@ class ImageOpts(fsldisplay.DisplayOpts):
 
 
     def transformDisplayLocation(self, oldLoc):
+        """Overrides :meth:`.DisplayOpts.transformDisplayLocation`.
+
+        If the :attr:`transform` property has changed, returns the given
+        location, assumed to be in the old display coordinate system,
+        transformed into the new display coordinate system.
+        """
 
         lastVal = self.getLastValue('transform')
 
@@ -390,11 +411,31 @@ class ImageOpts(fsldisplay.DisplayOpts):
 
 
 class VolumeOpts(ImageOpts):
-    """A class which describes how an :class:`.Image` should be displayed.
-
-    This class doesn't have much functionality - it is up to things which
-    actually display an :class:`.Image` to adhere to the properties stored in
-    the associated :class:`.Display` and :class:`VolumeOpts` object.
+    """The ``VolumeOpts`` class defines options for displaying :class:`.Image`
+    instances as regular 3D volumes.
+
+    The ``VolumeOpts`` class links the :attr:`.Display.brightness` and
+    :attr:`.Display.contrast` properties to its own :attr:`displayRange`
+    property, so changes in either of the former will result in a change to
+    the latter, and vice versa. This relationship is defined by the
+    :func:`~.colourmaps.displayRangeToBricon` and
+    :func:`~.colourmaps.briconToDisplayRange` functions, in the
+    :mod:`.colourmaps` module.
+
+    In addition to all of the display properties, ``VolumeOpts`` instances
+    have the following attributes:
+
+    =========== ===============================
+    ``dataMin`` The minimum value in the image.
+    ``dataMax`` The maximum value in the image.
+    =========== ===============================
+
+    For large images (where *large* is arbitrarily defined in
+    :meth:`__init__`), the ``dataMin`` and ``dataMax`` attributes will contain
+    range of a sample of the image data, rather their actual values. This is
+    purely to eliminate the need to calculate minimum/maximum values over very
+    large (and potentially memory-mapped) images, which can be a time
+    consuming operation.
     """
 
     
@@ -407,7 +448,7 @@ class VolumeOpts(ImageOpts):
 
     
     invertClipping = props.Boolean(default=False)
-    """If ``True``, the behaviour of ``clippingRange`` is inverted, i.e.
+    """If ``True``, the behaviour of :attr:`clippingRange` is inverted, i.e.
     values inside the clipping range are clipped, instead of those outside
     the clipping range.
     """
@@ -419,13 +460,15 @@ class VolumeOpts(ImageOpts):
     
     interpolation = props.Choice(('none', 'linear', 'spline'))
     """How the value shown at a real world location is derived from the
-    corresponding data value(s). 'No interpolation' is equivalent to nearest
-    neighbour interpolation.
+    corresponding data value(s). ``none`` is equivalent to nearest neighbour
+    interpolation.
     """
 
 
     invert = props.Boolean(default=False)
-    """Invert the colour map."""
+    """Use an inverted version of the current colour map (see the :attr:`cmap`
+    property).
+    """
 
 
     def __init__(self,
@@ -434,7 +477,12 @@ class VolumeOpts(ImageOpts):
                  overlayList,
                  displayCtx,
                  **kwargs):
-        """Create a :class:`VolumeOpts` instance for the specified image."""
+        """Create a :class:`VolumeOpts` instance for the specified ``overlay``,
+        assumed to be an :class:`.Image` instance.
+
+        All arguments are passed through to the :class:`.DisplayOpts`
+        constructor.
+        """
 
         # Attributes controlling image display. Only
         # determine the real min/max for small images -
@@ -516,6 +564,9 @@ class VolumeOpts(ImageOpts):
 
 
     def destroy(self):
+        """Removes property listeners, and calls the :meth:`ImageOpts.destroy`
+        method.
+        """
 
         if self.getParent() is not None:
             display = self.display
@@ -535,7 +586,7 @@ class VolumeOpts(ImageOpts):
     def __toggleListeners(self, enable=True):
         """This method enables/disables the property listeners which
         are registered on the :attr:`displayRange` and
-        :attr:`.Display.brightness`/:attr:`.Display.contrast`/ properties.
+        :attr:`.Display.brightness`/:attr:`.Display.contrast`/properties.
         
         Because these properties are linked via the
         :meth:`__displayRangeChanged` and :meth:`__briconChanged` methods,
@@ -596,7 +647,7 @@ class VolumeOpts(ImageOpts):
 
         
     def __displayRangeChanged(self, *a):
-        """Called when the `attr`:displayRange: property changes.
+        """Called when the `attr:`displayRange` property changes.
 
         Updates the :attr:`.Display.brightness` and :attr:`.Display.contrast`
         properties accordingly.
diff --git a/fsl/fsleyes/frame.py b/fsl/fsleyes/frame.py
index 143df255e..5c1f8d63a 100644
--- a/fsl/fsleyes/frame.py
+++ b/fsl/fsleyes/frame.py
@@ -405,7 +405,7 @@ class FSLEyesFrame(wx.Frame):
 
     
     def __restoreState(self, restore=True):
-        """Called on :meth:`__init__`.
+        """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
-- 
GitLab