diff --git a/doc/images/overlaylistpanel.png b/doc/images/overlaylistpanel.png
new file mode 100644
index 0000000000000000000000000000000000000000..74bf7d1dba485353fea4ba6873646b94c89d92db
Binary files /dev/null and b/doc/images/overlaylistpanel.png differ
diff --git a/doc/images/timeseriescontrolpanel.png b/doc/images/timeseriescontrolpanel.png
new file mode 100644
index 0000000000000000000000000000000000000000..6876f4d8104e9be6f6ab1cb2edef2430e018838b
Binary files /dev/null and b/doc/images/timeseriescontrolpanel.png differ
diff --git a/fsl/fsleyes/controls/overlaylistpanel.py b/fsl/fsleyes/controls/overlaylistpanel.py
index 405dca8f0e618e2f37039559cfc71c755af9f2f1..43706c090c1a5894fa3c650280f918743d8bccb4 100644
--- a/fsl/fsleyes/controls/overlaylistpanel.py
+++ b/fsl/fsleyes/controls/overlaylistpanel.py
@@ -1,14 +1,14 @@
 #!/usr/bin/env python
 #
-# overlaylistpanel.py - A panel which displays a list of overlays in the 
-# overlay list.
+# overlaylistpanel.py - The OverlayListPanel.
 #
 # Author: Paul McCarthy <pauldmccarthy@gmail.com>
 #
-"""A panel which displays a list of overlays in the overlay list (see and
-allows the user to add/remove overlays, and to change their order.
+"""This module provides the ``OverlayListPanel``, a *FSLeyes control* which
+displays a list of all overlays currently in the :class:`.OverlayList`.
 """
 
+
 import logging
 
 import wx
@@ -25,135 +25,31 @@ import fsl.data.image    as fslimage
 log = logging.getLogger(__name__)
 
 
-class ListItemWidget(wx.Panel):
-
-    _enabledFG  = '#000000'
-    _disabledFG = '#CCCCCC'
-
-    def __init__(self, parent, overlay, display, displayCtx, listBox):
-        wx.Panel.__init__(self, parent)
-
-        self.overlay    = overlay
-        self.display    = display
-        self.displayCtx = displayCtx
-        self.listBox    = listBox
-        self.name       = '{}_{}'.format(self.__class__.__name__, id(self))
-
-        # BU_NOTEXT causes a segmentation fault under OSX
-        if wx.Platform == '__WXMAC__': btnStyle = wx.BU_EXACTFIT
-        else:                          btnStyle = wx.BU_EXACTFIT | wx.BU_NOTEXT
-
-        self.saveButton = wx.Button(      self, style=btnStyle)
-        self.lockButton = wx.ToggleButton(self, style=btnStyle)
-
-        self.saveButton.SetBitmap(icons.loadBitmap('floppydisk16'))
-        self.lockButton.SetBitmap(icons.loadBitmap('chainlink16'))
-        
-        self.visibility = props.makeWidget(
-            self,
-            display,
-            'enabled',
-            icon=icons.findImageFile('eye16'))
-
-        self.sizer = wx.BoxSizer(wx.HORIZONTAL)
-
-        self.SetSizer(self.sizer)
-
-        self.sizer.Add(self.saveButton, flag=wx.EXPAND, proportion=1)
-        self.sizer.Add(self.lockButton, flag=wx.EXPAND, proportion=1)
-        self.sizer.Add(self.visibility, flag=wx.EXPAND, proportion=1)
-
-        # There is currently only one overlay
-        # group in the application. In the
-        # future there may be multiple groups.
-        group = displayCtx.overlayGroups[0]
-
-        display.addListener('enabled',  self.name, self.__vizChanged)
-        group  .addListener('overlays', self.name, self.__overlayGroupChanged)
-        
-        if isinstance(overlay, fslimage.Image):
-            overlay.addListener('saved', self.name, self.__saveStateChanged)
-        else:
-            self.saveButton.Enable(False)
-
-        self.saveButton.Bind(wx.EVT_BUTTON,         self.__onSaveButton)
-        self.lockButton.Bind(wx.EVT_TOGGLEBUTTON,   self.__onLockButton)
-        self           .Bind(wx.EVT_WINDOW_DESTROY, self.__onDestroy)
-
-        self.__overlayGroupChanged()
-        self.__vizChanged()
-        self.__saveStateChanged()
-
-
-    def __overlayGroupChanged(self, *a):
-
-        group = self.displayCtx.overlayGroups[0]
-        self.lockButton.SetValue(self.overlay in group.overlays)
-
-        
-    def __onSaveButton(self, ev):
-        self.displayCtx.selectOverlay(self.overlay)
-        self.overlay.save()
-
-
-    def __onLockButton(self, ev):
-        self.displayCtx.selectOverlay(self.overlay)
-        group = self.displayCtx.overlayGroups[0]
-        
-        if self.lockButton.GetValue(): group.addOverlay(   self.overlay)
-        else:                          group.removeOverlay(self.overlay)
-
-        
-    def __onDestroy(self, ev):
-        ev.Skip()
-        if ev.GetEventObject() is not self:
-            return
-
-        group = self.displayCtx.overlayGroups[0]
-
-        self.display.removeListener('enabled', self.name)
-        group       .removeListener('overlays', self.name)
-
-        if isinstance(self.overlay, fslimage.Image):
-            self.overlay.removeListener('saved', self.name)
-
-        
-    def __saveStateChanged(self, *a):
-
-        if not isinstance(self.overlay, fslimage.Image):
-            return
-        
-        idx = self.listBox.IndexOf(self.overlay)
-        
-        self.saveButton.Enable(not self.overlay.saved)
-
-        if self.overlay.saved:
-            self.listBox.SetItemBackgroundColour(idx)
-        else:
-            self.listBox.SetItemBackgroundColour(idx, '#ffaaaa', '#aa4444') 
-
-            
-    def __vizChanged(self, *a):
-        self.displayCtx.selectOverlay(self.overlay)
-
-        idx = self.listBox.IndexOf(self.overlay)
-
-        if self.display.enabled: fgColour = ListItemWidget._enabledFG
-        else:                    fgColour = ListItemWidget._disabledFG
-
-        self.listBox.SetItemForegroundColour(idx, fgColour)
+class OverlayListPanel(fslpanel.FSLEyesPanel):
+    """The ``OverlayListPanel`` displays all overlays in the
+    :class:`.OverlayList`, and allows the user to add, remove, and re-order
+    overlays. An ``OverlayListPanel`` looks something like this:
 
+    .. image:: images/overlaylistpanel.png
+       :scale: 50%
+       :align: center
 
-class OverlayListPanel(fslpanel.FSLEyesPanel):
-    """A :class:`.ControlPanel` which contains an :class:`.EditableListBox`
-    displaying the list of loaded overlays.
     
-    The list box allows the overlay order to be changed, and allows overlays
-    to be added and removed from the list.
+    A :class:`ListItemWidget` is displayed alongside every overlay in the
+    list - this allows the user to enable/disable, group, and save each
+    overlay.
+
+    The ``OverlayListPanel`` is closely coupled to a few
+    :class:`.DisplayContext` properties: the
+    :attr:`.DisplayContext.selectedOverlay` property is linked to the currently
+    selected item in the overlay list, and the order in which the overlays are
+    shown is defined by the :attr:`.DisplayContext.overlayOrder` property. This
+    property is updated when the user changes the order of items in the list.
     """
+
     
     def __init__(self, parent, overlayList, displayCtx):
-        """Create and lay out an :class:`OverlayListPanel`.
+        """Create an ``OverlayListPanel``.
 
         :param parent:      The :mod:`wx` parent object.
         :param overlayList: An :class:`.OverlayList` instance.
@@ -164,49 +60,52 @@ class OverlayListPanel(fslpanel.FSLEyesPanel):
 
         # list box containing the list of overlays - it 
         # is populated in the _overlayListChanged method
-        self._listBox = elistbox.EditableListBox(
+        self.__listBox = elistbox.EditableListBox(
             self,
             style=(elistbox.ELB_REVERSE | 
                    elistbox.ELB_TOOLTIP))
 
         # listeners for when the user does
         # something with the list box
-        self._listBox.Bind(elistbox.EVT_ELB_SELECT_EVENT,   self._lbSelect)
-        self._listBox.Bind(elistbox.EVT_ELB_MOVE_EVENT,     self._lbMove)
-        self._listBox.Bind(elistbox.EVT_ELB_REMOVE_EVENT,   self._lbRemove)
-        self._listBox.Bind(elistbox.EVT_ELB_ADD_EVENT,      self._lbAdd)
-        self._listBox.Bind(elistbox.EVT_ELB_DBLCLICK_EVENT, self._lbDblClick)
+        self.__listBox.Bind(elistbox.EVT_ELB_SELECT_EVENT,   self.__lbSelect)
+        self.__listBox.Bind(elistbox.EVT_ELB_MOVE_EVENT,     self.__lbMove)
+        self.__listBox.Bind(elistbox.EVT_ELB_REMOVE_EVENT,   self.__lbRemove)
+        self.__listBox.Bind(elistbox.EVT_ELB_ADD_EVENT,      self.__lbAdd)
+        self.__listBox.Bind(elistbox.EVT_ELB_DBLCLICK_EVENT, self.__lbDblClick)
 
-        self._sizer = wx.BoxSizer(wx.HORIZONTAL)
-        self.SetSizer(self._sizer)
+        self.__sizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.SetSizer(self.__sizer)
 
-        self._sizer.Add(self._listBox, flag=wx.EXPAND, proportion=1)
+        self.__sizer.Add(self.__listBox, flag=wx.EXPAND, proportion=1)
 
         self._overlayList.addListener(
             'overlays',
             self._name,
-            self._overlayListChanged)
+            self.__overlayListChanged)
         
         self._displayCtx.addListener(
             'overlayOrder',
             self._name,
-            self._overlayListChanged) 
+            self.__overlayListChanged) 
 
         self._displayCtx.addListener(
             'selectedOverlay',
             self._name,
-            self._selectedOverlayChanged)
+            self.__selectedOverlayChanged)
 
-        self._overlayListChanged()
-        self._selectedOverlayChanged()
+        self.__overlayListChanged()
+        self.__selectedOverlayChanged()
 
         self.Layout()
 
-        self.SetMinSize(self._sizer.GetMinSize())
+        self.SetMinSize(self.__sizer.GetMinSize())
 
 
     def destroy(self):
-        """Deregisters property listeners."""
+        """Must be called when this ``OverlayListPanel`` 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)
@@ -221,18 +120,21 @@ class OverlayListPanel(fslpanel.FSLEyesPanel):
         fslpanel.FSLEyesPanel.destroy(self)
 
         
-    def _selectedOverlayChanged(self, *a):
+    def __selectedOverlayChanged(self, *a):
         """Called when the :attr:`.DisplayContext.selectedOverlay` property
         changes. Updates the selected item in the list box.
         """
 
         if len(self._overlayList) > 0:
-            self._listBox.SetSelection(
+            self.__listBox.SetSelection(
                 self._displayCtx.getOverlayOrder(
                     self._displayCtx.selectedOverlay))
 
 
-    def _overlayNameChanged(self, value, valid, display, propName):
+    def __overlayNameChanged(self, value, valid, display, propName):
+        """Called when the :attr:`.Display.name` of an overlay changes. Updates
+        the corresponding label in the overlay list.
+        """
 
         overlay = display.getOverlay()
         idx     = self._displayCtx.getOverlayOrder(overlay)
@@ -241,18 +143,15 @@ class OverlayListPanel(fslpanel.FSLEyesPanel):
         if name is None:
             name = ''
             
-        self._listBox.SetItemLabel(idx, name) 
+        self.__listBox.SetItemLabel(idx, name) 
 
         
-    def _overlayListChanged(self, *a):
-        """Called when the :class:`.OverlayList.overlays` list changes.
-
-        If the change was due to user action on the :class:`.EditableListBox`,
-        this method does nothing.  Otherwise, this method updates the
-        :class:`.EditableListBox`
+    def __overlayListChanged(self, *a):
+        """Called when the :class:`.OverlayList` changes.  All of the items
+        in the overlay list are re-created.
         """
         
-        self._listBox.Clear()
+        self.__listBox.Clear()
 
         for i, overlay in enumerate(self._displayCtx.getOrderedOverlays()):
 
@@ -262,39 +161,40 @@ class OverlayListPanel(fslpanel.FSLEyesPanel):
 
             tooltip = overlay.dataSource
             
-            self._listBox.Append(name, overlay, tooltip)
+            self.__listBox.Append(name, overlay, tooltip)
 
             widget = ListItemWidget(self,
                                     overlay,
                                     display,
                                     self._displayCtx,
-                                    self._listBox)
+                                    self.__listBox)
 
-            self._listBox.SetItemWidget(i, widget)
+            self.__listBox.SetItemWidget(i, widget)
 
             display.addListener('name',
                                 self._name,
-                                self._overlayNameChanged,
+                                self.__overlayNameChanged,
                                 overwrite=True)
 
         if len(self._overlayList) > 0:
-            self._listBox.SetSelection(
+            self.__listBox.SetSelection(
                 self._displayCtx.getOverlayOrder(
                     self._displayCtx.selectedOverlay))
         
         
-    def _lbMove(self, ev):
+    def __lbMove(self, ev):
         """Called when an overlay is moved in the :class:`.EditableListBox`.
-        Reorders the :class:`.OverlayList` to reflect the change.
+        Reorders the :attr:`.DisplayContext.overlayOrder` to reflect the
+        change.
         """
         self._displayCtx.disableListener('overlayOrder', self._name)
         self._displayCtx.overlayOrder.move(ev.oldIdx, ev.newIdx)
         self._displayCtx.enableListener('overlayOrder', self._name)
 
         
-    def _lbSelect(self, ev):
+    def __lbSelect(self, ev):
         """Called when an overlay is selected in the
-        :class:`.EditableListBox`. Sets the
+        :class:`.EditableListBox`. Updates the
         :attr:`.DisplayContext.selectedOverlay` property.
         """
         self._displayCtx.disableListener('selectedOverlay', self._name)
@@ -303,28 +203,236 @@ class OverlayListPanel(fslpanel.FSLEyesPanel):
         self._displayCtx.enableListener('selectedOverlay', self._name)
 
         
-    def _lbAdd(self, ev):
-        """Called when the 'add' button on the list box is pressed.
-        
+    def __lbAdd(self, ev):
+        """Called when the *add* button on the list box is pressed.
         Calls the :meth:`.OverlayList.addOverlays` method.
         """
         if self._overlayList.addOverlays():
             self._displayCtx.selectedOverlay = len(self._overlayList) - 1
 
 
-    def _lbRemove(self, ev):
+    def __lbRemove(self, ev):
         """Called when an item is removed from the overlay listbox.
-
         Removes the corresponding overlay from the :class:`.OverlayList`.
         """
         self._overlayList.pop(self._displayCtx.overlayOrder[ev.idx])
 
 
-    def _lbDblClick(self, ev):
-        """Called when an item label is double clickedon the overlay list
-        box. Toggles the enabled state of the overlay.
+    def __lbDblClick(self, ev):
+        """Called when an item label is double clicked on the overlay list
+        box. Toggles the visibility of the overlay, via the
+        :attr:`.Display.enabled` property..
         """
         idx             = self._displayCtx.overlayOrder[ev.idx]
         overlay         = self._overlayList[idx]
         display         = self._displayCtx.getDisplay(overlay)
         display.enabled = not display.enabled
+
+
+class ListItemWidget(wx.Panel):
+    """A ``LisItemWidget`` is created by the :class:`OverlayListPanel` for
+    every overlay in the :class:`.OverlayList`. A ``LisItemWidget`` contains
+    controls which allow the user to:
+
+     - Toggle the visibility of the overlay (via the :attr:`.Display.enabled`
+       property)
+
+     - Add the overlay to a group (see the
+       :attr:`.DisplayContext.overlayGroups` property, and the :mod:`.group`
+       module).
+
+     - Save the overlay (if it has been modified).
+
+    .. note:: While the :class:`.DisplayContext` allows multiple
+              :class:`.OverlayGroup` instances to be defined (and added to its
+              :attr:`.DisplayContext.overlayGroups` property), *FSLeyes*
+              currently only defines a single group . This ``OverlayGroup``
+              is created in the :func:`.fsleyes.context` function, and overlays
+              can be added/removed to/from it via the *lock* button on a
+              ``ListItemWidget``. This functionality might change in a future
+              version of *FSLeyes*.
+
+
+    .. note:: Currently, only :class:`.Image` overlays can be saved. The *save*
+              button is disabled for all other overlay types.
+    """
+
+    
+    enabledFG  = '#000000'
+    """This colour is used as the foreground (text) colour for overlays
+    where their :attr:`.Display.enabled` property is ``True``.
+    """
+
+    
+    disabledFG = '#CCCCCC'
+    """This colour is used as the foreground (text) colour for overlays
+    where their :attr:`.Display.enabled` property is ``False``.
+    """ 
+
+    
+    unsavedDefaultBG = '#ffaaaa'
+    """This colour is used as the default background colour for
+    :class:`.Image` overlays with an :attr:`.Image.saved` property
+    of ``False``.
+    """
+
+    
+    unsavedSelectedBG = '#aa4444'
+    """This colour is used as the background colour for :class:`.Image`
+    overlays with an :attr:`.Image.saved` property of ``False``, when
+    they are selected in the :class:`OverlayListPanel`.
+    """ 
+
+    
+    def __init__(self, parent, overlay, display, displayCtx, listBox):
+        """Create a ``ListItemWidget``.
+
+        :arg parent:     The :mod:`wx` parent object.
+        :arg overlay:    The overlay associated with this ``ListItemWidget``.
+        :arg display:    The :class:`.Display` associated with the overlay.
+        :arg displayCtx: The :class:`.DisplayContext` instance.
+        :arg listBox:    The :class:`.EditableListBox` that contains this
+                         ``ListItemWidget``.
+        """
+        wx.Panel.__init__(self, parent)
+
+        self.__overlay    = overlay
+        self.__display    = display
+        self.__displayCtx = displayCtx
+        self.__listBox    = listBox
+        self.__name       = '{}_{}'.format(self.__class__.__name__, id(self))
+
+        # BU_NOTEXT causes a segmentation fault under OSX
+        if wx.Platform == '__WXMAC__': btnStyle = wx.BU_EXACTFIT
+        else:                          btnStyle = wx.BU_EXACTFIT | wx.BU_NOTEXT
+
+        self.__saveButton = wx.Button(      self, style=btnStyle)
+        self.__lockButton = wx.ToggleButton(self, style=btnStyle)
+
+        self.__saveButton.SetBitmap(icons.loadBitmap('floppydisk16'))
+        self.__lockButton.SetBitmap(icons.loadBitmap('chainlink16'))
+        
+        self.__visibility = props.makeWidget(
+            self,
+            display,
+            'enabled',
+            icon=icons.findImageFile('eye16'))
+
+        self.__sizer = wx.BoxSizer(wx.HORIZONTAL)
+
+        self.SetSizer(self.__sizer)
+
+        self.__sizer.Add(self.__saveButton, flag=wx.EXPAND, proportion=1)
+        self.__sizer.Add(self.__lockButton, flag=wx.EXPAND, proportion=1)
+        self.__sizer.Add(self.__visibility, flag=wx.EXPAND, proportion=1)
+
+        # There is currently only one overlay
+        # group in the application. In the
+        # future there may be multiple groups.
+        group = displayCtx.overlayGroups[0]
+
+        display.addListener('enabled',
+                            self.__name,
+                            self.__vizChanged)
+        group  .addListener('overlays',
+                            self.__name,
+                            self.__overlayGroupChanged)
+        
+        if isinstance(overlay, fslimage.Image):
+            overlay.addListener('saved', self.__name, self.__saveStateChanged)
+        else:
+            self.__saveButton.Enable(False)
+
+        self.__saveButton.Bind(wx.EVT_BUTTON,         self.__onSaveButton)
+        self.__lockButton.Bind(wx.EVT_TOGGLEBUTTON,   self.__onLockButton)
+        self             .Bind(wx.EVT_WINDOW_DESTROY, self.__onDestroy)
+
+        self.__overlayGroupChanged()
+        self.__vizChanged()
+        self.__saveStateChanged()
+
+
+    def __overlayGroupChanged(self, *a):
+        """Called when the :class:`.OverlayGroup` changes. Updates the *lock*
+        button based on whether the overlay associated with this
+        ``ListItemWidget`` is in the group or not.
+        """
+        group = self.__displayCtx.overlayGroups[0]
+        self.__lockButton.SetValue(self.__overlay in group.overlays)
+
+        
+    def __onSaveButton(self, ev):
+        """Called when the *save* button is pushed. Calls the
+        :meth:`.Image.save` method.
+        """
+        self.__displayCtx.selectOverlay(self.__overlay)
+        self.__overlay.save()
+
+
+    def __onLockButton(self, ev):
+        """Called when the *lock* button is pushed. Adds/removes the overlay
+        to/from the :class:`.OverlayGroup`.
+        """
+        self.__displayCtx.selectOverlay(self.__overlay)
+        group = self.__displayCtx.overlayGroups[0]
+        
+        if self.__lockButton.GetValue(): group.addOverlay(   self.__overlay)
+        else:                            group.removeOverlay(self.__overlay)
+
+        
+    def __onDestroy(self, ev):
+        """Called when this ``ListItemWidget`` is destroyed (i.e. when the
+        associated overlay is removed from the :class:`OverlayListPanel`).
+        Removes some proprety listeners from the :class:`.Display` and
+        :class:`.OverlayGroup` instances, and from the overlay if it is an
+        :class:`.Image` instance.
+        """
+        ev.Skip()
+        if ev.GetEventObject() is not self:
+            return
+
+        group = self.__displayCtx.overlayGroups[0]
+
+        self.__display.removeListener('enabled',  self.__name)
+        group         .removeListener('overlays', self.__name)
+
+        if isinstance(self.__overlay, fslimage.Image):
+            self.__overlay.removeListener('saved', self.__name)
+
+        
+    def __saveStateChanged(self, *a):
+        """If the overlay is an :class:`.Image` instance, this method is
+        called when its :attr:`.Image.saved` property changes. Updates the
+        state of the *save* button.
+        """
+
+        if not isinstance(self.__overlay, fslimage.Image):
+            return
+        
+        idx = self.__listBox.IndexOf(self.__overlay)
+        
+        self.__saveButton.Enable(not self.__overlay.saved)
+
+        if self.__overlay.saved:
+            self.__listBox.SetItemBackgroundColour(idx)
+            
+        else:
+            self.__listBox.SetItemBackgroundColour(
+                idx,
+                ListItemWidget.unsavedDefaultBG,
+                ListItemWidget.unsavedSelectedBG),
+
+            
+    def __vizChanged(self, *a):
+        """Called when the :attr:`.Display.enabled` property of the overlay
+        changes. Updates the state of the *enabled* buton, and changes the
+        item foreground colour.
+        """
+        self.__displayCtx.selectOverlay(self.__overlay)
+
+        idx = self.__listBox.IndexOf(self.__overlay)
+
+        if self.__display.enabled: fgColour = ListItemWidget.enabledFG
+        else:                      fgColour = ListItemWidget.disabledFG
+
+        self.__listBox.SetItemForegroundColour(idx, fgColour)
diff --git a/fsl/fsleyes/controls/shellpanel.py b/fsl/fsleyes/controls/shellpanel.py
index 1ac8b1dcddd1124acf8aff4519e954957dd5d6b4..82ffd983e47da9c26f2700819adb6bad7e76ee37 100644
--- a/fsl/fsleyes/controls/shellpanel.py
+++ b/fsl/fsleyes/controls/shellpanel.py
@@ -1,9 +1,13 @@
 #!/usr/bin/env python
 #
-# shellpanel.py -
+# shellpanel.py - The ShellPanel class.
 #
 # Author: Paul McCarthy <pauldmccarthy@gmail.com>
 #
+"""This module provides the :class:`ShellPanel` class, a *FSLeyes control*
+which contains an interactive Python shell.
+"""
+
 
 import wx
 
@@ -13,8 +17,29 @@ import fsl.fsleyes.panel as fslpanel
 
 
 class ShellPanel(fslpanel.FSLEyesPanel):
+    """A ``ShellPanel`` is a :class:`.FSLEyesPanel` which contains an
+    interactive Python shell.
+
+    A ``ShellPanel`` allows the user to programmatically interact with the
+    :class:`.OverlayList`, and with the :class:`.DisplayContext` and
+    :class:`.SceneOpts` instances associated with the :class:`.CanvasPanel`
+    that owns this ``ShellPanel``.
+    """
 
     def __init__(self, parent, overlayList, displayCtx, sceneOpts):
+        """Create a ``ShellPanel``.
+
+        :arg parent:      The :mod:`wx` parent object, assumed to be the
+                          :class:`.CanvasPanel` that owns this ``ShellPanel``.
+        
+        :arg overlayList: The :class:`.OverlayList`.
+        
+        :arg displayCtx:  The :class:`.DisplayContext` of the
+                          :class:`.CanvasPanel` that owns this ``ShellPanel``.
+        
+        :arg sceneOpts:   The :class:`.SceneOpts` of the
+                          :class:`.CanvasPanel` that owns this ``ShellPanel``.
+        """
         fslpanel.FSLEyesPanel.__init__(self, parent, overlayList, displayCtx)
 
         lcls = {
@@ -30,7 +55,8 @@ class ShellPanel(fslpanel.FSLEyesPanel):
                       'Available variables are:\n'
                       '  - overlayList\n' 
                       '  - displayCtx\n'
-                      '  - sceneOpts\n\n',
+                      '  - sceneOpts\n\n'
+                      '  - viewPanel\n\n', 
             locals=lcls,
             showInterpIntro=False)
 
@@ -62,4 +88,7 @@ class ShellPanel(fslpanel.FSLEyesPanel):
 
 
     def destroy(self):
+        """Must be called when this ``ShellPanel`` is no longer needed.
+        Calls the :meth:`.FSLEyesPanel.destroy` method.
+        """
         fslpanel.FSLEyesPanel.destroy(self)
diff --git a/fsl/fsleyes/controls/timeseriescontrolpanel.py b/fsl/fsleyes/controls/timeseriescontrolpanel.py
index 7289a67e6fff3655a9b7aa9ec6362374fb20c2cf..c13d08a6fbedd89753fa51f6639f9c5658ca8e91 100644
--- a/fsl/fsleyes/controls/timeseriescontrolpanel.py
+++ b/fsl/fsleyes/controls/timeseriescontrolpanel.py
@@ -1,9 +1,13 @@
 #!/usr/bin/env python
 #
-# timeseriescontrolpanel.py -
+# timeseriescontrolpanel.py - The TimeSeriesControlPanel class.
 #
 # Author: Paul McCarthy <pauldmccarthy@gmail.com>
 #
+"""This module provides the :class:`TimeSeriesControlPanel` a *FSLeyes
+control* which allows the user to configure a :class:`.TimeSeriesPanel`.
+"""
+
 
 import wx
 
@@ -16,8 +20,52 @@ import fsl.data.strings     as strings
 
 
 class TimeSeriesControlPanel(fslpanel.FSLEyesPanel):
+    """The ``TimeSeriesControlPanel`` is a :class:`.FSLEyesPanel` which allows
+    the user to configure a :class:`.TimeSeriesPanel`. It contains controls
+    which are linked to the properties of the :class:`.TImeSeriesPanel`,
+    (which include properties defined on the :class:`.PlotPanel` base class),
+    and the :class:`.TimeSeries` class.
+
+    
+    A ``TimeSeriesControlPanel`` looks something like this:
+
+    .. image:: images/timeseriescontrolpanel.png
+       :scale: 50%
+       :align: center
 
+
+    The settings shown on a ``TimeSeriesControlPanel`` are organised into three
+    or four sections:
+
+     - The *Time series plot settings* section has controls which are linked to
+       properties of the :class:`.TimeSeriesPanel` class.
+    
+     - The *General plot settings* section has controls which are linked to
+       properties of the :class:`.PlotPanel` base class.
+    
+     - The *Settings for the current time course* section has controls which
+       are linked to properties of the :class:`.TimeSeries` class. These
+       properties define how the *current* time course is displayed (see the
+       :class:`.TimeSeriesPanel` class documentation).
+    
+     - The *FEAT plot settings* is only shown if the currently selected overlay
+       is a :class:`.FEATImage`. It has controls which are linked to properties
+       of the :class:`.FEATTimeSeries` class.
+    """
+
+    
     def __init__(self, parent, overlayList, displayCtx, tsPanel):
+        """Create a ``TimeSeriesControlPanel``.
+
+        :arg parent:      The :mod:`wx` parent object.
+        
+        :arg overlayList: The :class:`.OverlayList`.
+        
+        :arg displayCtx:  The :class:`.DisplayContext` instance.
+        
+        :arg tsPanel:     The :class:`.TimeSeriesPanel` associated with this
+                          ``TimeSeriesControlPanel``.
+        """
 
         fslpanel.FSLEyesPanel.__init__(self, parent, overlayList, displayCtx)
 
@@ -128,6 +176,10 @@ class TimeSeriesControlPanel(fslpanel.FSLEyesPanel):
 
 
     def destroy(self):
+        """Must be called when this ``TimeSeriesControlPanel`` is no longer
+        needed. Removes some property listeners, and calls the
+        :meth:`.FSLEyesPanel.destroy` method.
+        """
         self._displayCtx .removeListener('selectedOverlay', self._name)
         self._overlayList.removeListener('overlays',        self._name)
 
@@ -139,6 +191,10 @@ class TimeSeriesControlPanel(fslpanel.FSLEyesPanel):
 
 
     def __showCurrentChanged(self, *a):
+        """Called when the :attr:`.TimeSeriesPanel.showCurrent` property
+        changes. Shows hides the  *Settings for the current time course*
+        section.
+        """
         widgets     = self.__widgets
         tsPanel     = self.__tsPanel
         showCurrent = tsPanel.showCurrent
@@ -185,6 +241,11 @@ class TimeSeriesControlPanel(fslpanel.FSLEyesPanel):
             
 
     def __selectedOverlayNameChanged(self, *a):
+        """Called when the :attr:`.Display.name` property for the currently
+        selected overlay changes. Only called if the current overlay is a
+        :class:`.FEATImage`. Updates the display name of the *FEAT plot
+        settings* section.
+        """
         display = self._displayCtx.getDisplay(self.__selectedOverlay)
         self.__widgets.RenameGroup(
             'currentFEATSettings',
@@ -193,6 +254,10 @@ class TimeSeriesControlPanel(fslpanel.FSLEyesPanel):
 
     
     def __selectedOverlayChanged(self, *a):
+        """Called when the :attr:`.DisplayContext.selectedOverlay` or
+        :class:`.OverlayList` changes. If the newly selected overlay is a
+        :class:`.FEATImage`, the *FEAT plot settings* section is updated.
+        """
 
         # We're assuminbg that the TimeSeriesPanel has
         # already updated its current TimeSeries for
diff --git a/fsl/fsleyes/overlay.py b/fsl/fsleyes/overlay.py
index 39300f0ee51202a3901e176f832e1bb07964b5b8..c6d6fbf24709fb6fd77e1f00d0ccc772c127ff8c 100644
--- a/fsl/fsleyes/overlay.py
+++ b/fsl/fsleyes/overlay.py
@@ -40,6 +40,7 @@ Currently (``fslpy`` version |version|) the only overlay types in existence
    :nosignatures:
 
    ~fsl.data.image.Image
+   ~fsl.data.featimage.FEATImage
    ~fsl.data.model.Model
 
 
@@ -53,7 +54,6 @@ A few other utility functions are provided by this module:
    loadOverlays
    interactiveLoadOverlays
    saveOverlay
-
 """
 
 import logging