diff --git a/doc/images/canvassettingspanel.png b/doc/images/canvassettingspanel.png
new file mode 100644
index 0000000000000000000000000000000000000000..6a2d17378c83635af6d022bd4fef4ded588b6d0d
Binary files /dev/null and b/doc/images/canvassettingspanel.png differ
diff --git a/doc/images/clusterpanel.png b/doc/images/clusterpanel.png
new file mode 100644
index 0000000000000000000000000000000000000000..97188d1a4f62406cb97b52a4a00029c69a164975
Binary files /dev/null and b/doc/images/clusterpanel.png differ
diff --git a/doc/images/histogramcontrolpanel.png b/doc/images/histogramcontrolpanel.png
new file mode 100644
index 0000000000000000000000000000000000000000..f6ab2e0341e69cc8f004585edeac023e09f26fcf
Binary files /dev/null and b/doc/images/histogramcontrolpanel.png differ
diff --git a/doc/images/histogramlistpanel.png b/doc/images/histogramlistpanel.png
new file mode 100644
index 0000000000000000000000000000000000000000..365f343b7a1ed20dda066ff4bdd23a5995b98c5c
Binary files /dev/null and b/doc/images/histogramlistpanel.png differ
diff --git a/doc/images/timeserieslistpanel.png b/doc/images/timeserieslistpanel.png
new file mode 100644
index 0000000000000000000000000000000000000000..d60dedbb7873f796248eccb4a9e10753cc00b280
Binary files /dev/null and b/doc/images/timeserieslistpanel.png differ
diff --git a/fsl/fsleyes/controls/canvassettingspanel.py b/fsl/fsleyes/controls/canvassettingspanel.py
index af21df901d569bf7d9d4964cf41f76141d4246be..a4ac500b6f21971990cbcadc40829025687f472b 100644
--- a/fsl/fsleyes/controls/canvassettingspanel.py
+++ b/fsl/fsleyes/controls/canvassettingspanel.py
@@ -1,9 +1,13 @@
 #!/usr/bin/env python
 #
-# canvassettingspanel.py -
+# canvassettingspanel.py - The CanvasSettingsPanel class.
 #
 # Author: Paul McCarthy <pauldmccarthy@gmail.com>
 #
+"""This module provides the :class:`CanvasSettingsPanel` class, a *FSLeyes
+control* panel which displays settings for a :class:`.CanvasPanel`.
+"""
+
 
 import wx
 
@@ -16,59 +20,55 @@ import fsl.fsleyes.panel    as fslpanel
 import fsl.fsleyes.tooltips as fsltooltips
 
 
-_CANVASPANEL_PROPS = [
-    props.Widget('syncOverlayOrder'),
-    props.Widget('syncLocation'),
-    props.Widget('syncOverlayDisplay'),
-    props.Widget('movieMode'),
-    props.Widget('movieRate', spin=False, showLimits=False),
-]
+class CanvasSettingsPanel(fslpanel.FSLEyesPanel):
+    """The ``CanvasSettingsPanel`` is a *FSLeyes control* which displays
+    settings for a :class:`.CanvasPanel` instance. A ``CanvasSettingsPanel``
+    looks something like this:
 
-_SCENEOPTS_PROPS = [
-    props.Widget('showCursor'),
-    props.Widget('bgColour'),
-    props.Widget('cursorColour'),
-    props.Widget('performance',
-                 spin=False,
-                 showLimits=False,
-                 labels=strings.choices['SceneOpts.performance']),
-    props.Widget('showColourBar'),
-    props.Widget('colourBarLabelSide',
-                 labels=strings.choices['SceneOpts.colourBarLabelSide'],
-                 enabledWhen=lambda o: o.showColourBar),
-    props.Widget('colourBarLocation',
-                 labels=strings.choices['SceneOpts.colourBarLocation'],
-                 enabledWhen=lambda o: o.showColourBar)
-]
+    .. image:: images/canvassettingspanel.png
+       :scale: 50%
+       :align: center
 
-_ORTHOOPTS_PROPS = [
-    props.Widget('layout', labels=strings.choices['OrthoOpts.layout']), 
-    props.Widget('zoom', spin=False, showLimits=False),
-    props.Widget('showLabels'),
-    props.Widget('showXCanvas'),
-    props.Widget('showYCanvas'),
-    props.Widget('showZCanvas')
-]
 
-_LIGHTBOXOPTS_PROPS = [
-    props.Widget('zax', labels=strings.choices['CanvasOpts.zax']),
-    props.Widget('zoom',         showLimits=False, spin=False),
-    props.Widget('sliceSpacing', showLimits=False),
-    props.Widget('zrange',       showLimits=False),
-    props.Widget('highlightSlice'),
-    props.Widget('showGridLines') 
-]
+    The ``CanvasSettingsPanel`` displays controls which modify properties on
+    the following classes:
 
+    .. autosummary::
+       :nosignatures:
 
-class CanvasSettingsPanel(fslpanel.FSLEyesPanel):
+       ~fsl.fsleyes.views.CanvasPanel
+       ~fsl.fsleyes.displaycontext.SceneOpts
+       ~fsl.fsleyes.displaycontext.OrthoOpts
+       ~fsl.fsleyes.displaycontext.LightBoxOpts
 
-    def __init__(self, parent, overlayList, displayCtx, canvasPanel):
+    The ``CanvasSettingsPanel`` divides the displayed settings into those
+    which are common to all :class:`.CanvasPanel` instances, and those which
+    are specific to the :class:`.CanvasPanel` sub-class (i.e.
+    :class:`.OrthoPanel` or :class:`.LightBoxPanel`).  The specific settings
+    that are displayed are defined in the following lists:
+
+    .. autosummary::
+
+       _CANVASPANEL_PROPS
+       _SCENEOPTS_PROPS
+       _ORTHOOPTS_PROPS
+       _LIGHTBOXOPTS_PROPS
+    """
 
+    
+    def __init__(self, parent, overlayList, displayCtx, canvasPanel):
+        """Create a ``CanvasSettingsPanel``.
+
+        :arg parent:      The :mod:`wx` parent object
+        :arg overlayList: The :class:`.OverlayList` instance.
+        :arg displayCtx:  The :class:`.DisplayContext` instance.
+        :arg canvasPanel: The :class:`.CanvasPanel` instance.
+        """
+        
         fslpanel.FSLEyesPanel.__init__(self, parent, overlayList, displayCtx)
 
         self.__widgets = widgetlist.WidgetList(self)
-
-        self.__sizer = wx.BoxSizer(wx.VERTICAL)
+        self.__sizer   = wx.BoxSizer(wx.VERTICAL)
 
         self.SetSizer(self.__sizer)
 
@@ -133,3 +133,62 @@ class CanvasSettingsPanel(fslpanel.FSLEyesPanel):
         self.__widgets.Expand(panelGroup)
 
         self.SetMinSize((21, 21))
+
+
+_CANVASPANEL_PROPS = [
+    props.Widget('syncOverlayOrder'),
+    props.Widget('syncLocation'),
+    props.Widget('syncOverlayDisplay'),
+    props.Widget('movieMode'),
+    props.Widget('movieRate', spin=False, showLimits=False),
+]
+"""A list of :class:`props.Widget` items defining controls to
+display for :class:`.CanvasPanel` properties.
+"""
+
+
+_SCENEOPTS_PROPS = [
+    props.Widget('showCursor'),
+    props.Widget('bgColour'),
+    props.Widget('cursorColour'),
+    props.Widget('performance',
+                 spin=False,
+                 showLimits=False,
+                 labels=strings.choices['SceneOpts.performance']),
+    props.Widget('showColourBar'),
+    props.Widget('colourBarLabelSide',
+                 labels=strings.choices['SceneOpts.colourBarLabelSide'],
+                 enabledWhen=lambda o: o.showColourBar),
+    props.Widget('colourBarLocation',
+                 labels=strings.choices['SceneOpts.colourBarLocation'],
+                 enabledWhen=lambda o: o.showColourBar)
+]
+"""A list of :class:`props.Widget` items defining controls to
+display for :class:`.SceneOpts` properties.
+"""
+
+
+_ORTHOOPTS_PROPS = [
+    props.Widget('layout', labels=strings.choices['OrthoOpts.layout']), 
+    props.Widget('zoom', spin=False, showLimits=False),
+    props.Widget('showLabels'),
+    props.Widget('showXCanvas'),
+    props.Widget('showYCanvas'),
+    props.Widget('showZCanvas')
+]
+"""A list of :class:`props.Widget` items defining controls to
+display for :class:`.OrthoOpts` properties.
+"""
+
+
+_LIGHTBOXOPTS_PROPS = [
+    props.Widget('zax', labels=strings.choices['CanvasOpts.zax']),
+    props.Widget('zoom',         showLimits=False, spin=False),
+    props.Widget('sliceSpacing', showLimits=False),
+    props.Widget('zrange',       showLimits=False),
+    props.Widget('highlightSlice'),
+    props.Widget('showGridLines') 
+]
+"""A list of :class:`props.Widget` items defining controls to
+display for :class:`.LightBoxOpts` properties.
+"""
diff --git a/fsl/fsleyes/controls/clusterpanel.py b/fsl/fsleyes/controls/clusterpanel.py
index f9b9a0ccc6eaf7f81fbefed0e135a4b899573ae5..c6900e8cf432faab6852b2cd43d9415eb16e7b09 100644
--- a/fsl/fsleyes/controls/clusterpanel.py
+++ b/fsl/fsleyes/controls/clusterpanel.py
@@ -1,9 +1,12 @@
 #!/usr/bin/env python
 #
-# clusterpanel.py -
+# clusterpanel.py - The ClusterPanel class.
 #
 # Author: Paul McCarthy <pauldmccarthy@gmail.com>
 #
+"""This module provides the :class:`ClusterPanel` class, a *FSLeyes control*
+panel for viewing cluster results from a FEAT analysis.
+"""
 
 import                        logging
 import                        wx
@@ -11,7 +14,6 @@ import                        wx
 import pwidgets.widgetgrid as widgetgrid
 
 import fsl.fsleyes.panel   as fslpanel
-import fsl.utils.transform as transform
 import fsl.utils.dialog    as fsldlg
 import fsl.data.strings    as strings
 import fsl.data.featimage  as featimage
@@ -21,8 +23,35 @@ log = logging.getLogger(__name__)
 
 
 class ClusterPanel(fslpanel.FSLEyesPanel):
+    """The ``ClusterPanel`` shows a table of cluster results from the analysis
+    associated with a :class:`.FEATImage` overlay. A ``ClusterPanel`` looks
+    something like the following:
+
+    .. image:: images/clusterpanel.png
+       :scale: 50%
+       :align: center
+
+    The ``ClusterPanel`` contains controls which allow the user to:
+
+      - Select the COPE for which cluster results are displayed
+    
+      - Add a Z statistic overlay for the currently displayed COPE
+    
+      - Add a cluster mask overlay for the currently displayed COPE
+    
+      - Navigate to the Z maximum location, Z centre-of-gravity location,
+        or COPE maximum location, for a specific cluster.
+    """
 
     def __init__(self, parent, overlayList, displayCtx):
+        """Create a ``ClusterPanel``.
+
+        :arg parent:      The :mod:`wx` parent object.
+
+        :arg overlayList: The :class:`.OverlayList` instance.
+
+        :arg displayCtx:  The :class:`.DisplayContext` instance.
+        """
         fslpanel.FSLEyesPanel.__init__(self, parent, overlayList, displayCtx)
 
         self.__disabledText = wx.StaticText(
@@ -80,9 +109,19 @@ class ClusterPanel(fslpanel.FSLEyesPanel):
         self.__selectedOverlayChanged()
 
 
+    def destroy(self):
+        """Must be called when this ``ClusterPanel`` is no longer needed.
+        Removes some property listeners, and calls
+        :meth:`.FSLEyesPanel.destroy`.
+        """
+        self._overlayList.removeListener('overlays',        self._name)
+        self._displayCtx .removeListener('selectedOverlay', self._name)
+        fslpanel.FSLEyesPanel.destroy(self)
+
+
     def __calcMinSize(self):
         """Figures out the minimum size that this ``ClusterPanel`` should
-        have.
+        have. Called by :meth:`__init__`.
 
         When the ``ClusterPanel`` is created, the COPE combo box is not
         populated, so has no minimum size. Here, we figure out a good minimum
@@ -101,22 +140,36 @@ class ClusterPanel(fslpanel.FSLEyesPanel):
         
         return self.__sizer.GetMinSize()
 
-
-    def destroy(self):
-        self._overlayList.removeListener('overlays',        self._name)
-        self._displayCtx .removeListener('selectedOverlay', self ._name)
-        fslpanel.FSLEyesPanel.destroy(self)
-
         
     def __disable(self, message):
+        """Disables the ``ClusterPanel``, and displays the given message.
+        Called when the selected overlay  is not a :class:`.FEATImage`, or
+        when cluster results cannot be displayed for some reason.
+        """
 
         self.__disabledText.SetLabel(message)
         self.__sizer.Show(self.__disabledText, True)
         self.__sizer.Show(self.__mainSizer,    False)
         self.Layout()
 
+    
+    def __statSelected(self, ev):
+        """Called when a COPE is selected. Clears the cluster table, and
+        displays clusters for the newly selected COPE (see the
+        :meth:`__displayClusterData` method)
+        """
+        idx  = self.__statSelect.GetSelection()
+        data = self.__statSelect.GetClientData(idx)
+        self.__displayClusterData(data)
+        self.__enableOverlayButtons()
+
 
     def __addZStatsClick(self, ev):
+        """Called when the *Add Z statistics* button is pushed. Retrieves
+        the Z statistic image for the current COPE (see the
+        :meth:`.FEATImage.getZStats` method), and adds it as an overlay
+        to the :class:`.OverlayList`.
+        """
 
         overlay  = self.__selectedOverlay
         contrast = self.__statSelect.GetSelection()
@@ -147,6 +200,11 @@ class ClusterPanel(fslpanel.FSLEyesPanel):
 
     
     def __addClustMaskClick(self, ev):
+        """Called when the *Add cluster mask* button is pushed. Retrieves the
+        cluster mask image for the currewnt contrast (see the
+        :meth:`.FEATImage.getClusterMask` method)
+
+        """
         overlay  = self.__selectedOverlay
         contrast = self.__statSelect.GetSelection()
         mask     = overlay.getClusterMask(contrast)
@@ -161,13 +219,102 @@ class ClusterPanel(fslpanel.FSLEyesPanel):
         self._overlayList.append(mask)
         self._displayCtx.getDisplay(mask).overlayType = 'label'
 
+        
+    def __displayClusterData(self, clusters):
+        """Updates the cluster table so that it is displaying the given list
+        of clusters.
+
+        :arg clusters: A sequence of objects, each representing one cluster.
+                       See the :meth:`.FEATImage.clusterResults` method.
+        """
+
+        cols = {'index'         : 0,
+                'nvoxels'       : 1,
+                'p'             : 2,
+                'logp'          : 3,
+                'zmax'          : 4,
+                'zmaxcoords'    : 5,
+                'zcogcoords'    : 6,
+                'copemax'       : 7,
+                'copemaxcoords' : 8,
+                'copemean'      : 9}
+
+        grid    = self.__clusterList
+        overlay = self.__selectedOverlay
+        opts    = self._displayCtx.getOpts(overlay)
+
+        grid.ClearGrid()
+        grid.SetGridSize(len(clusters), 10)
+
+        for col, i in cols.items():
+            grid.SetColLabel(i, strings.labels[self, col])
+
+        def makeCoordButton(coords):
+
+            label = wx.StaticText(grid, label='[{} {} {}]'.format(*coords))
+            btn   = wx.Button(grid, label=u'\u2192', style=wx.BU_EXACTFIT)
+            
+            sizer = wx.BoxSizer(wx.HORIZONTAL)
+            sizer.Add(label, flag=wx.EXPAND, proportion=1)
+            sizer.Add(btn)
+
+            def onClick(ev):
+                dloc = opts.transformCoords([coords], 'voxel', 'display')[0]
+                self._displayCtx.location = dloc
+
+            btn.Bind(wx.EVT_BUTTON, onClick)
+
+            return sizer
+
+        dlg = fsldlg.SimpleMessageDialog()
+
+        dlg.Show()
+
+        for i, clust in enumerate(clusters):
+
+            dlg.SetMessage(
+                strings.messages[self, 'loadingCluster'].format(clust.index))
+
+            zmaxbtn    = makeCoordButton((clust.zmaxx,
+                                          clust.zmaxy,
+                                          clust.zmaxz))
+            zcogbtn    = makeCoordButton((clust.zcogx,
+                                          clust.zcogy,
+                                          clust.zcogz))
+            copemaxbtn = makeCoordButton((clust.copemaxx,
+                                          clust.copemaxy,
+                                          clust.copemaxz))
+
+            fmt = lambda v: '{}'.format(v)
+            grid.SetText(  i, cols['index'],         fmt(clust.index))
+            grid.SetText(  i, cols['nvoxels'],       fmt(clust.nvoxels))
+            grid.SetText(  i, cols['p'],             fmt(clust.p))
+            grid.SetText(  i, cols['logp'],          fmt(clust.logp))
+            grid.SetText(  i, cols['zmax'],          fmt(clust.zmax))
+            grid.SetWidget(i, cols['zmaxcoords'],    zmaxbtn)
+            grid.SetWidget(i, cols['zcogcoords'],    zcogbtn)
+            grid.SetText(  i, cols['copemax'],       fmt(clust.copemax))
+            grid.SetWidget(i, cols['copemaxcoords'], copemaxbtn)
+            grid.SetText(  i, cols['copemean'],      fmt(clust.copemean))
+
+        dlg.Close()
+        dlg.Destroy()
+        
 
     def __overlayListChanged(self, *a):
+        """Called when the :class:`.OverlayList` changes. Updates the *Add Z
+        statistic* and *Add cluster mask* buttons, in case the user removed
+        them. Also calls :meth:`__selectedOverlayChanged`.
+        """
         self.__selectedOverlayChanged()
         self.__enableOverlayButtons()
 
 
     def __enableOverlayButtons(self):
+        """Enables/disables the *Add Z statistic* and *Add cluster mask*
+        buttons depending on whether the corresponding overlays are in the
+        :class:`.OverlayList`.
+        """
         
         if self.__selectedOverlay is None:
             return
@@ -185,6 +332,13 @@ class ClusterPanel(fslpanel.FSLEyesPanel):
         
     
     def __selectedOverlayChanged(self, *a):
+        """Called when the :attr:`.DisplayContext.selectedOverlay` changes,
+        and by the :meth:`__overlayListChanged` method.
+
+        If the newly selected overlay is a :class:`.FEATImage` which has
+        cluster results, they are loaded in, and displayed on the cluster
+        table.
+        """
 
         prevOverlay            = self.__selectedOverlay
         self.__selectedOverlay = None
@@ -255,85 +409,3 @@ class ClusterPanel(fslpanel.FSLEyesPanel):
 
         self.Layout()
         return
-
-    
-    def __statSelected(self, ev):
-        idx  = self.__statSelect.GetSelection()
-        data = self.__statSelect.GetClientData(idx)
-        self.__displayClusterData(data)
-        self.__enableOverlayButtons()
-
-        
-    def __displayClusterData(self, clusters):
-
-        cols = {'index'         : 0,
-                'nvoxels'       : 1,
-                'p'             : 2,
-                'logp'          : 3,
-                'zmax'          : 4,
-                'zmaxcoords'    : 5,
-                'zcogcoords'    : 6,
-                'copemax'       : 7,
-                'copemaxcoords' : 8,
-                'copemean'      : 9}
-
-        grid    = self.__clusterList
-        overlay = self.__selectedOverlay
-        opts    = self._displayCtx.getOpts(overlay)
-
-        grid.ClearGrid()
-        grid.SetGridSize(len(clusters), 10)
-
-        for col, i in cols.items():
-            grid.SetColLabel(i, strings.labels[self, col])
-
-        def makeCoordButton(coords):
-
-            label = wx.StaticText(grid, label='[{} {} {}]'.format(*coords))
-            btn   = wx.Button(grid, label=u'\u2192', style=wx.BU_EXACTFIT)
-            
-            sizer = wx.BoxSizer(wx.HORIZONTAL)
-            sizer.Add(label, flag=wx.EXPAND, proportion=1)
-            sizer.Add(btn)
-
-            def onClick(ev):
-                dloc = opts.transformCoords([coords], 'voxel', 'display')[0]
-                self._displayCtx.location = dloc
-
-            btn.Bind(wx.EVT_BUTTON, onClick)
-
-            return sizer
-
-        dlg = fsldlg.SimpleMessageDialog()
-
-        dlg.Show()
-
-        for i, clust in enumerate(clusters):
-
-            dlg.SetMessage(
-                strings.messages[self, 'loadingCluster'].format(clust.index))
-
-            zmaxbtn    = makeCoordButton((clust.zmaxx,
-                                          clust.zmaxy,
-                                          clust.zmaxz))
-            zcogbtn    = makeCoordButton((clust.zcogx,
-                                          clust.zcogy,
-                                          clust.zcogz))
-            copemaxbtn = makeCoordButton((clust.copemaxx,
-                                          clust.copemaxy,
-                                          clust.copemaxz))
-
-            fmt = lambda v: '{}'.format(v)
-            grid.SetText(  i, cols['index'],         fmt(clust.index))
-            grid.SetText(  i, cols['nvoxels'],       fmt(clust.nvoxels))
-            grid.SetText(  i, cols['p'],             fmt(clust.p))
-            grid.SetText(  i, cols['logp'],          fmt(clust.logp))
-            grid.SetText(  i, cols['zmax'],          fmt(clust.zmax))
-            grid.SetWidget(i, cols['zmaxcoords'],    zmaxbtn)
-            grid.SetWidget(i, cols['zcogcoords'],    zcogbtn)
-            grid.SetText(  i, cols['copemax'],       fmt(clust.copemax))
-            grid.SetWidget(i, cols['copemaxcoords'], copemaxbtn)
-            grid.SetText(  i, cols['copemean'],      fmt(clust.copemean))
-
-        dlg.Close()
-        dlg.Destroy()
diff --git a/fsl/fsleyes/controls/histogramcontrolpanel.py b/fsl/fsleyes/controls/histogramcontrolpanel.py
index 47446de99a5a9be2d2c24d94b8dbf70c9c21f7ad..c2f7ff6e2a5e010d32c08aebc4e31a42f7a3030c 100644
--- a/fsl/fsleyes/controls/histogramcontrolpanel.py
+++ b/fsl/fsleyes/controls/histogramcontrolpanel.py
@@ -1,9 +1,13 @@
 #!/usr/bin/env python
 #
-# histogramcontrolpanel.py -
+# histogramcontrolpanel.py - The HistogramControlPanel class.
 #
 # Author: Paul McCarthy <pauldmccarthy@gmail.com>
 #
+"""This module provides the :class:`HistogramControlPanel` class, a *FSLeyes
+control* panel which allows a :class:`.HistogramPanel` to be configured.
+"""
+
 
 import wx
 
@@ -16,8 +20,40 @@ import fsl.data.strings     as strings
 
 
 class HistogramControlPanel(fslpanel.FSLEyesPanel):
-
+    """The ``HistogramControlPanel`` is a *FSLeyes control* panel which
+    allows the user to configure a :class:`.HistogramPanel`. A
+    ``HistogramControlPanel`` looks something like the following:
+
+    .. image:: images/histogramcontrolpanel.png
+       :scale: 50%
+       :align: center
+
+
+    The ``HistogramControlPanel`` divides its settings into three main
+    sections:
+
+      - The *Histogram settings* section contains controls which modify
+        properties defined in the :class:`.HistogramPanel`.
+    
+      - *General plot settings* section contains controls which modify
+        properties defined in the :class:`.PlotPanel`.
+    
+      - The *Settings for the current histogram plot* section contains
+        controls which modify properties defined in the
+        :class:`.HistogramSeries` class, and affect the currently
+        selected plot in the :class:`.HistogramListPanel`. If no plots
+        have been added to the histogram list, this section is hidden.
+    """
+
+    
     def __init__(self, parent, overlayList, displayCtx, hsPanel):
+        """Create a ``HistogramControlPanel``.
+
+        :arg parent:      The :mod:`wx` parent object.
+        :arg overlayList: The :class:`.OverlayList` instance.
+        :arg displayCtx:  The :class:`.DisplayContext` instance.
+        :arg hsPanel:     The :class:`.HistogramPanel` instance.
+        """
 
         fslpanel.FSLEyesPanel.__init__(self, parent, overlayList, displayCtx)
 
@@ -127,6 +163,11 @@ class HistogramControlPanel(fslpanel.FSLEyesPanel):
 
 
     def destroy(self):
+        """Must be called when this ``HistogramControlPanel`` is no longer
+        needed. Removes some property listeners, and calls the
+        :meth:`.FSLEyesPanel.destroy` method.
+        """
+        
         self.__hsPanel.removeListener('selectedSeries', self._name)
         self.__hsPanel.removeListener('dataSeries',     self._name)
         if self.__currentHs is not None:
@@ -136,6 +177,10 @@ class HistogramControlPanel(fslpanel.FSLEyesPanel):
 
         
     def __selectedSeriesChanged(self, *a):
+        """Called when the :attr:`.HistogramPanel.selectedSeries` property
+        changes. Updates the section containing settings for the current
+        histogram plot (see the :meth:`__updateCurrentProperties` method).
+        """
 
         panel = self.__hsPanel 
         
@@ -149,6 +194,10 @@ class HistogramControlPanel(fslpanel.FSLEyesPanel):
 
 
     def __hsLabelChanged(self, *a):
+        """Called when the :attr:`.DataSeries.label` property, on the currently
+        selected :class:`.HistogramSeries`, changes. Updates the label for the
+        relevant settings section.
+        """
         if self.__currentHs is None:
             return
         
@@ -159,6 +208,9 @@ class HistogramControlPanel(fslpanel.FSLEyesPanel):
 
 
     def __updateCurrentProperties(self):
+        """Updates the settings shown in the section for the current histogram
+        plot.
+        """
 
         expanded  = False
         scrollPos = self.__widgets.GetViewStart()
@@ -185,7 +237,7 @@ class HistogramControlPanel(fslpanel.FSLEyesPanel):
 
         self.__nbins = props.makeWidget(wlist, hs, 'nbins', showLimits=False)
         
-        volume    = props.makeWidget(wlist, hs, 'volume',    showLimits=False)
+        volume    = props.makeWidget(wlist, hs, 'volume', showLimits=False)
         dataRange = props.makeWidget(
             wlist,
             hs,
@@ -233,6 +285,10 @@ class HistogramControlPanel(fslpanel.FSLEyesPanel):
         
 
     def __autoBinChanged(self, *a):
+        """Called when the :attr:`.HistogramPanel.autoBin` setting changes.  If
+        necessary, enables/disables the control which is bound to the
+        :attr:`.HistogramSeries.nbins` property for the current histogram plot.
+        """
 
         if self.__currentHs is None or self.__nbins is None:
             return
diff --git a/fsl/fsleyes/controls/histogramlistpanel.py b/fsl/fsleyes/controls/histogramlistpanel.py
index f4a9bdec5bd043fd284490d70522520e47a502a5..ad23ffea053f2ccff21c82af8cffadd4680cba2f 100644
--- a/fsl/fsleyes/controls/histogramlistpanel.py
+++ b/fsl/fsleyes/controls/histogramlistpanel.py
@@ -1,9 +1,12 @@
 #!/usr/bin/env python
 #
-# histogramlistpanel.py -
+# histogramlistpanel.py - The HistogramListPanel class.
 #
 # Author: Paul McCarthy <pauldmccarthy@gmail.com>
 #
+"""This module provides the :class:`HistogramListPanel` class, which
+is a *FSLeyes control* panel for use with :class:`.HistogramPanel` views.
+"""
 
 
 import wx
@@ -16,8 +19,34 @@ import                           timeserieslistpanel
     
 
 class HistogramListPanel(fslpanel.FSLEyesPanel):
+    """The ``HistogramListPanel`` is a control panel for use with the
+    :class:`.HistogramPanel` view. It allows the user to add/remove
+    :class:`.HistogramSeries` plots to/from the :class:`.HistogramPanel`,
+    and to configure the display settings for each. A ``HistogramListPanel``
+    looks something like this:
 
+    .. image:: images/histogramlistpanel.png
+       :scale: 50%
+       :align: center
+
+    
+    The ``HistogramListPanel`` performs the same task for the
+    :class:`.HistogramPanel` that the :class:`.TimeSeriesListPanel` does for
+    the :class:`.TimeSeriesPanel`. For each :class:`.HistogramSeries` that is
+    in the :attr:`.PlotPanel.dataSeries` list of the :class:`.HistogramPanel`,
+    a :class:`.TimeSeriesWidget` control is added to a
+    :class:`pwidgets.EditableListBox`.
+    """
+
+    
     def __init__(self, parent, overlayList, displayCtx, histPanel):
+        """Create a ``HistogramListPanel``.
+
+        :arg parent:      The :mod:`wx` parent object.
+        :arg overlayList: The :class:`.OverlayList` instance.
+        :arg displayCtx:  The :class:`.DisplayContext` instance.
+        :arg histPanel:   The :class:`.HistogramPanel` instance. 
+        """
 
         fslpanel.FSLEyesPanel.__init__(self, parent, overlayList, displayCtx)
 
@@ -45,15 +74,26 @@ class HistogramListPanel(fslpanel.FSLEyesPanel):
 
         
     def destroy(self):
+        """Must be called when this ``HistogramListPanel`` is no longer needed.
+        Removes some property listeners, and calls the
+        :meth:`.FSLEyesPanel.destroy` method.
+        """
         self.__hsPanel.removeListener('dataSeries', self._name)
         fslpanel.FSLEyesPanel.destroy(self)
 
 
     def getListBox(self):
+        """Returns the :class:`pwidgets.EditableListBox` instance contained
+        within this ``HistogramListPanel``.
+        """
         return self.__hsList
 
 
     def __histSeriesChanged(self, *a):
+        """Called when the :attr:`.PlotPanel.dataSeries` list of the
+        :class:`.HistogramPanel` changes. Refreshes the contents of the
+        :class:`pwidgets.EditableListBox`.
+        """
 
         self.__hsList.Clear()
 
@@ -69,6 +109,10 @@ class HistogramListPanel(fslpanel.FSLEyesPanel):
         
     
     def __onListAdd(self, ev):
+        """Called when the user pushes the *add* button on the
+        :class:`pwidgets.EditableListBox`. Adds the *current* histogram plot
+        to the list (see the :class:`.HistogramPanel` class documentation).
+        """
         hs = self.__hsPanel.getCurrent()
 
         if hs is None:
@@ -85,16 +129,31 @@ class HistogramListPanel(fslpanel.FSLEyesPanel):
 
         
     def __onListEdit(self, ev):
+        """Called when the user edits a label on the
+        :class:`pwidgets.EditableListBox`. Updates the
+        :attr:`.DataSeries.label` property of the corresponding
+        :class:`.HistogramSeries` instance.
+        """ 
         ev.data.label = ev.label
 
         
     def __onListSelect(self, ev):
+        """Called when the user selects an item in the
+        :class:`pwidgets.EditableListBox`. Sets the
+        :attr:`.DisplayContext.selectedOverlay` to the overlay associated with
+        the corresponding :class:`.HistogramSeries` instance.
+        """ 
         overlay = ev.data.overlay
         self._displayCtx.selectedOverlay = self._overlayList.index(overlay)
         self.__hsPanel.selectedSeries = ev.idx
 
         
     def __onListRemove(self, ev):
+        """Called when the user removes an item from the
+        :class:`pwidgets.EditableListBox`. Removes the corresponding
+        :class:`.HistogramSeries` instance from the
+        :attr:`.PlotPanel.dataSeries` list of the :class:`.HistogramPanel`.
+        """ 
         self.__hsPanel.dataSeries.remove(ev.data)
         self.__hsPanel.selectedSeries = self.__hsList.GetSelection()
         ev.data.destroy()
diff --git a/fsl/fsleyes/controls/timeserieslistpanel.py b/fsl/fsleyes/controls/timeserieslistpanel.py
index d39e6308d5d35397a767be152672d55df945ed5e..7d94536c8250dc3cf218abf57944568c7e1e3001 100644
--- a/fsl/fsleyes/controls/timeserieslistpanel.py
+++ b/fsl/fsleyes/controls/timeserieslistpanel.py
@@ -1,9 +1,14 @@
 #!/usr/bin/env python
 #
-# timeserieslistpanel.py -
+# timeserieslistpanel.py - The TimeSeriesListPanel and TimeSeriesWidget
+#                          classes.
 #
 # Author: Paul McCarthy <pauldmccarthy@gmail.com>
 #
+"""This module provides the :class:`TimeSeriesListPanel` class, which is a
+*FSLeyes control* panel for use with the :class:`.TimeSeriesPanel` view.
+"""
+
 
 import          copy
 
@@ -18,54 +23,42 @@ import fsl.data.strings       as strings
 import fsl.fsleyes.colourmaps as fslcm
 
 
-class TimeSeriesWidget(wx.Panel):
-
-    def __init__(self, parent, timeSeries):
-
-        wx.Panel.__init__(self, parent)
-
-        self.colour    = props.makeWidget(self,
-                                          timeSeries,
-                                          'colour')
-        self.alpha     = props.makeWidget(self,
-                                          timeSeries,
-                                          'alpha',
-                                          slider=True,
-                                          spin=False,
-                                          showLimits=False) 
-        self.lineWidth = props.makeWidget(self,
-                                          timeSeries,
-                                          'lineWidth')
-        self.lineStyle = props.makeWidget(
-            self,
-            timeSeries,
-            'lineStyle',
-            labels=strings.choices['DataSeries.lineStyle'])
-
-        self.colour.SetToolTipString(
-            fsltooltips.properties[timeSeries, 'colour'])
-        self.alpha.SetToolTipString(
-            fsltooltips.properties[timeSeries, 'alpha'])
-        self.lineWidth.SetToolTipString(
-            fsltooltips.properties[timeSeries, 'lineWidth'])
-        self.lineStyle.SetToolTipString(
-            fsltooltips.properties[timeSeries, 'lineStyle'])
-
-        self.sizer = wx.BoxSizer(wx.HORIZONTAL)
-        self.SetSizer(self.sizer)
-
-        self.sizer.Add(self.colour)
-        self.sizer.Add(self.alpha)
-        self.sizer.Add(self.lineWidth)
-        self.sizer.Add(self.lineStyle)
-
-        self.Layout()
-    
-
 class TimeSeriesListPanel(fslpanel.FSLEyesPanel):
+    """The ``TimeSeriesListPanel`` is a control panel for use with the
+    :class:`.TimeSeriesPanel` view. It allows the user to add/remove
+    :class:`.TimeSeries` plots to/from the ``TimeSeriesPanel``, and to
+    configure the settings for each ``TimeSeries`` in the
+    :attr:`.PlotPanel.dataSeries` list. A ``TimeSeriesListPanel`` looks
+    something like the following:
+
+    .. image:: images/timeserieslistpanel.png
+       :scale: 50%
+       :align: center
+
+
+    For every :class:`.TimeSeries` instance in the
+    :attr:`.PlotPanel.dataSeries` list of the :class:`.TimeSeriesPanel`, the
+    ``TimeSeriesListPanel`` creates a :class:`.TimeSeriesWidget`, which allows
+    the user to change the display settings of the :class:`.TimeSeries`
+    instance. A :class:`pwidgets.EditableListBox` is used to display the
+    labels for each :class:`.TimeSeries` instance, and the associated
+    :class:`.TimeSeriesWidget` controls.
+
+    A label is also shown above the list, containing the name of the currently
+    selected overlay, and the voxel coordinates of the current
+    :attr:`.DisplayContext.location`.
+    """
 
+    
     def __init__(self, parent, overlayList, displayCtx, timeSeriesPanel):
+        """Create a ``TimeSeriesListPanel``.
 
+        :arg parent:          The :mod:`wx` parent object.
+        :arg overlayList:     The :class:`.OverlayList` instance.
+        :arg displayCtx:      The :class:`.DisplayContext` instance.
+        :arg timeSeriesPanel: The :class:`.TimeSeriesPanel` instance.
+        """
+        
         fslpanel.FSLEyesPanel.__init__(self, parent, overlayList, displayCtx)
 
         self.__tsPanel      = timeSeriesPanel
@@ -104,6 +97,11 @@ class TimeSeriesListPanel(fslpanel.FSLEyesPanel):
 
         
     def destroy(self):
+        """Must be called when this ``TimeSeriesListPanel`` is no longer
+        needed. Removes some property listeners, and calls the
+        :meth:`.FSLEyesPanel.destroy` method.
+        """
+        
         self._displayCtx .removeListener('selectedOverlay', self._name)
         self._displayCtx .removeListener('location',        self._name)
         self._overlayList.removeListener('overlays',        self._name)
@@ -113,6 +111,8 @@ class TimeSeriesListPanel(fslpanel.FSLEyesPanel):
 
 
     def __makeLabel(self, ts):
+        """Creates a label to use for the given :class:`.TimeSeries` instance.
+        """
 
         display = self._displayCtx.getDisplay(ts.overlay)
 
@@ -123,6 +123,9 @@ class TimeSeriesListPanel(fslpanel.FSLEyesPanel):
 
 
     def __makeFEATModelTSLabel(self, parentTs, modelTs):
+        """Creates a label to use for the given :class:`.FEATTimeSeries`
+        instance.
+        """ 
 
         import fsl.fsleyes.views.timeseriespanel as tsp
 
@@ -157,6 +160,10 @@ class TimeSeriesListPanel(fslpanel.FSLEyesPanel):
 
 
     def __timeSeriesChanged(self, *a):
+        """Called when the :attr:`.PlotPanel.dataSeries` list of the
+        :class:`.TimeSeriesPanel` changes. Updates the list of
+        :class:`.TimeSeriesWidget` controls.
+        """
 
         self.__tsList.Clear()
 
@@ -170,17 +177,28 @@ class TimeSeriesListPanel(fslpanel.FSLEyesPanel):
 
 
     def __locationChanged(self, *a):
+        """Called when the :attr:`.DisplayContext.location` changes.
+
+        Updates a label at the top of the ``TimeSeriesListPanel``, displaying
+        the currently selected overlay, and the current voxel location.
+        """
 
         ts = self.__tsPanel.getCurrent()
 
         if ts is None:
             self.__currentLabel.SetLabel('')
             return
-        
+
         self.__currentLabel.SetLabel(self.__makeLabel(ts))
 
     
     def __onListAdd(self, ev):
+        """Called when the user pushes the *add* button on the
+        :class:`pwidgets.EditableListBox`.  Adds the *current* time series
+        plot to the :attr:`.PlotPanel.dataSeries` list of the
+        :class:`.TimeSeriesPanel` (see the :class:`.TimeSeriesPanel` class
+        documentation).
+        """
         
         import fsl.fsleyes.views.timeseriespanel as tsp
         
@@ -215,10 +233,20 @@ class TimeSeriesListPanel(fslpanel.FSLEyesPanel):
 
         
     def __onListEdit(self, ev):
+        """Called when the user edits a label on the
+        :class:`pwidgets.EditableListBox`. Updates the
+        :attr:`.DataSeries.label` property of the corresponding
+        :class:`.TimeSeries` instance.
+        """
         ev.data.label = ev.label
 
         
     def __onListSelect(self, ev):
+        """Called when the user selects an item in the
+        :class:`pwidgets.EditableListBox`. Sets the
+        :attr:`.DisplayContext.selectedOverlay` to the overlay associated with
+        the corresponding :class:`.TimeSeries` instance.
+        """
 
         overlay = ev.data.overlay
         coords  = ev.data.coords
@@ -231,4 +259,68 @@ class TimeSeriesListPanel(fslpanel.FSLEyesPanel):
 
         
     def __onListRemove(self, ev):
+        """Called when the user removes an item from the
+        :class:`pwidgets.EditableListBox`. Removes the corresponding
+        :class:`.TimeSeries` instance from the :attr:`.PlotPanel.dataSeries`
+        list of the :class:`.TimeSeriesPanel`.
+        """
         self.__tsPanel.dataSeries.remove(ev.data)
+
+        
+class TimeSeriesWidget(wx.Panel):
+    """The ``TimeSeriesWidget`` class is a panel which contains controls
+    that modify the properties of a :class:`.TimeSeries` instance. A
+    ``TimeSeriesWidget`` is created by the :class:`TimeSeriesListPanel` for
+    every ``TimeSeries`` in the :attr:`.TimeSeriesPanel.dataSeries` list.
+
+    A ``TimeSeriesWidget`` may be created for instances of any sub-class
+    of :class:`.DataSeries`, not just the :class:`.TimeSeries` class.
+    """
+
+    
+    def __init__(self, parent, timeSeries):
+        """Create a ``TimeSeriesWidget``.
+
+        :arg parent:     The :mod:`wx` parent object.
+        
+        :arg timeSeries: The :class:`.DataSeries` instance.
+        """
+
+        wx.Panel.__init__(self, parent)
+
+        self.__colour    = props.makeWidget(self,
+                                            timeSeries,
+                                            'colour')
+        self.__alpha     = props.makeWidget(self,
+                                            timeSeries,
+                                            'alpha',
+                                            slider=True,
+                                            spin=False,
+                                            showLimits=False) 
+        self.__lineWidth = props.makeWidget(self,
+                                            timeSeries,
+                                            'lineWidth')
+        self.__lineStyle = props.makeWidget(
+            self,
+            timeSeries,
+            'lineStyle',
+            labels=strings.choices['DataSeries.lineStyle'])
+
+        self.__colour.SetToolTipString(
+            fsltooltips.properties[timeSeries, 'colour'])
+        self.__alpha.SetToolTipString(
+            fsltooltips.properties[timeSeries, 'alpha'])
+        self.__lineWidth.SetToolTipString(
+            fsltooltips.properties[timeSeries, 'lineWidth'])
+        self.__lineStyle.SetToolTipString(
+            fsltooltips.properties[timeSeries, 'lineStyle'])
+
+        self.__sizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.SetSizer(self.__sizer)
+
+        self.__sizer.Add(self.__colour)
+        self.__sizer.Add(self.__alpha)
+        self.__sizer.Add(self.__lineWidth)
+        self.__sizer.Add(self.__lineStyle)
+
+        self.Layout()