diff --git a/fsl/data/fslimage.py b/fsl/data/fslimage.py
index b3fcd2d448bb41fb1505de82e3c24c499f194663..4d480e16873580d55d74d0fbe7626f7e9955f126 100644
--- a/fsl/data/fslimage.py
+++ b/fsl/data/fslimage.py
@@ -6,6 +6,8 @@
 # Author: Paul McCarthy <pauldmccarthy@gmail.com>
 #
 
+import collections
+
 import os.path            as op
 
 import numpy              as np
@@ -145,6 +147,9 @@ class ImageList(object):
         
         if images is None: images = []
 
+        if not isinstance(images, collections.Iterable):
+           raise TypeError('images must be a sequence of images')
+
         self._images    = images
         self._listeners = []
 
diff --git a/fsl/fslview/__init__.py b/fsl/fslview/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/fsl/fslview/orthopanel.py b/fsl/fslview/orthopanel.py
new file mode 100644
index 0000000000000000000000000000000000000000..30f90bc300091d85e22923885256d7bdc323d72d
--- /dev/null
+++ b/fsl/fslview/orthopanel.py
@@ -0,0 +1,201 @@
+#!/usr/bin/env python
+#
+# orthopanel.py - A wx/OpenGL widget for displaying and interacting with a
+# collection of 3D images. Displays three canvases, each of which shows the
+# same image(s) on a different orthogonal plane.
+#
+# Author: Paul McCarthy <pauldmccarthy@gmail.com>
+#
+
+import sys
+
+if True:
+    import logging
+    logging.basicConfig(
+        format='%(levelname)8s '\
+               '%(filename)20s '\
+               '%(lineno)4d: '\
+               '%(funcName)s - '\
+               '%(message)s',
+        level=logging.DEBUG)
+
+import wx
+import wx.lib.newevent as wxevent
+
+import fsl.props               as props
+import fsl.data.fslimage       as fslimage
+import fsl.fslview.slicecanvas as slicecanvas
+
+# The OrthoPanel emits a LocationEvent whenever the 'cursor' location
+# changes. It contains three attributes, x, y, and z, corresponding to
+# the current cursort location in the image space.
+LocationEvent, EVT_LOCATION_EVENT = wxevent.NewEvent()
+
+class OrthoPanel(wx.Panel):
+
+    def __init__(self, parent, imageList, *args, **kwargs):
+        """
+        Creates three SliceCanvas objects, each displaying the images
+        in the given image list along a different axis.
+        """
+
+        # if an fslimage.Image is passed in, we turn
+        # a blind eye, and make it a list of length 1
+        if isinstance(imageList, fslimage.Image):
+            imageList = fslimage.ImageList(imageList)
+
+        if not isinstance(imageList, fslimage.ImageList):
+            raise TypeError(
+                'imageList must be a fsl.data.fslimage.ImageList instance')
+
+        self.imageList = imageList
+
+        wx.Panel.__init__(self, parent, *args, **kwargs)
+        self.SetMinSize((300,100))
+
+        self.shape = imageList[0].data.shape
+
+        self.xcanvas = slicecanvas.SliceCanvas(self, imageList, zax=0)
+        self.ycanvas = slicecanvas.SliceCanvas(self, imageList, zax=1,
+                                               context=self.xcanvas.context)
+        self.zcanvas = slicecanvas.SliceCanvas(self, imageList, zax=2,
+                                               context=self.xcanvas.context)
+
+        self.sizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.SetSizer(self.sizer)
+
+        self.sizer.Add(self.xcanvas, flag=wx.EXPAND, proportion=1)
+        self.sizer.Add(self.ycanvas, flag=wx.EXPAND, proportion=1)
+        self.sizer.Add(self.zcanvas, flag=wx.EXPAND, proportion=1)
+
+        self.Layout()
+
+        self.xcanvas.Bind(wx.EVT_LEFT_DOWN, self._setCanvasPosition)
+        self.ycanvas.Bind(wx.EVT_LEFT_DOWN, self._setCanvasPosition)
+        self.zcanvas.Bind(wx.EVT_LEFT_DOWN, self._setCanvasPosition)
+        self.xcanvas.Bind(wx.EVT_MOTION,    self._setCanvasPosition)
+        self.ycanvas.Bind(wx.EVT_MOTION,    self._setCanvasPosition)
+        self.zcanvas.Bind(wx.EVT_MOTION,    self._setCanvasPosition)
+
+        
+    def setLocation(self, x, y, z):
+        """
+        Programmatically set the currently displayed location
+        on each of the canvases. This does not trigger an
+        EVT_LOCATION_EVENT.
+        """
+
+        self.xcanvas.xpos = y
+        self.xcanvas.ypos = z
+        self.xcanvas.zpos = x
+
+        self.ycanvas.xpos = x
+        self.ycanvas.ypos = z
+        self.ycanvas.zpos = y
+        
+        self.zcanvas.xpos = x
+        self.zcanvas.ypos = y
+        self.zcanvas.zpos = z
+
+        self.xcanvas.Refresh()
+        self.ycanvas.Refresh()
+        self.zcanvas.Refresh()
+
+    def setXLocation(self, x):
+        self.setLocation(x, self.ycanvas.zpos, self.zcanvas.zpos)
+
+    def setYLocation(self, y):
+        self.setLocation(self.xcanvas.zpos, y, self.zcanvas.zpos)
+
+    def setZLocation(self, z):
+        self.setLocation(self.xcanvas.zpos, self.ycanvas.zpos, z)
+
+
+    def _setCanvasPosition(self, ev):
+        """
+        Called on mouse movement and left clicks. The currently
+        displayed slices and cursor positions on each of the
+        canvases follow mouse clicks and drags, and an
+        EVT_LOCATION_EVENT is emitted when the cursor position
+        changes.
+        """
+
+        if not ev.LeftIsDown(): return
+
+        mx,my  = ev.GetPositionTuple()
+        source = ev.GetEventObject()
+        w,h = source.GetClientSize()
+
+        my = h - my
+
+        x = self.xcanvas.zpos
+        y = self.ycanvas.zpos
+        z = self.zcanvas.zpos
+
+        if source == self.xcanvas:
+
+            mx = mx * self.shape[1] / float(w)
+            my = my * self.shape[2] / float(h)
+            y,z = mx,my
+
+        elif source == self.ycanvas:
+            mx = mx * self.shape[0] / float(w)
+            my = my * self.shape[2] / float(h)
+            x,z = mx,my
+
+        elif source == self.zcanvas:
+            mx = mx * self.shape[0] / float(w)
+            my = my * self.shape[1] / float(h)
+            x,y = mx,my
+
+        x = int(x)
+        y = int(y)
+        z = int(z)
+
+        if x < 0: x = 0
+        if y < 0: y = 0
+        if z < 0: z = 0
+
+        if x >= self.shape[0]: x = self.shape[0]-1
+        if y >= self.shape[1]: y = self.shape[1]-1
+        if z >= self.shape[2]: z = self.shape[2]-1 
+
+        self.setLocation(x,y,z)
+
+        evt = LocationEvent(x=x,y=y,z=z)
+        wx.PostEvent(self, evt)
+
+
+class OrthoFrame(wx.Frame):
+    """
+    Convenience class for displaying an OrthoPanel in a standalone window.
+    """
+
+    def __init__(self, parent, imageList, title=None):
+        
+        wx.Frame.__init__(self, parent, title=title)
+        self.panel = OrthoPanel(self, imageList)
+        self.Layout()
+
+
+def main():
+    """
+    Test program, displays the image specified on the command line.
+    """
+
+    if len(sys.argv) != 2:
+        print 'usage: orthopanel.py filename'
+        sys.exit(1)
+
+    app       = wx.App()
+    image     = fslimage.Image(sys.argv[1])
+    imageList = fslimage.ImageList([image])
+    
+    frame  = OrthoFrame(None, imageList, title=sys.argv[1])
+    frame.Show()
+
+    app.MainLoop()
+
+    
+if __name__ == '__main__':
+    main()
diff --git a/fsl/utils/slicecanvas.py b/fsl/fslview/slicecanvas.py
similarity index 100%
rename from fsl/utils/slicecanvas.py
rename to fsl/fslview/slicecanvas.py
diff --git a/fsl/utils/imageview.py b/fsl/utils/imageview.py
deleted file mode 100644
index 554860c9767234bd2bf5e401b3fc30b96998a0a5..0000000000000000000000000000000000000000
--- a/fsl/utils/imageview.py
+++ /dev/null
@@ -1,368 +0,0 @@
-#!/usr/bin/env python
-#
-# imgshow.py - A wx/OpenGL widget for displaying and interacting with a
-# collection of 3D images. Displays three canvases, each of which shows
-
-#
-# Author: Paul McCarthy <pauldmccarthy@gmail.com>
-#
-
-import sys
-
-if False:
-    import logging
-    logging.basicConfig(
-        format='%(levelname)8s '\
-               '%(filename)20s '\
-               '%(lineno)4d: '\
-               '%(funcName)s - '\
-               '%(message)s',
-        level=logging.DEBUG)
-
-import wx
-import wx.lib.newevent as wxevent
-
-import fsl.props             as props
-import fsl.data.fslimage     as fslimage
-import fsl.utils.slicecanvas as slicecanvas
-
-LocationEvent, EVT_LOCATION_EVENT = wxevent.NewEvent()
-
-class EditableListBox(wx.Panel):
-    """
-    wx.gizmos.EditableListBox is rubbish.
-    """
-
-    ListSelectEvent, EVT_ELB_SELECT_EVENT = wxevent.NewEvent()
-    ListAddEvent,    EVT_ELB_ADD_EVENT    = wxevent.NewEvent()
-    ListRemoveEvent, EVT_ELB_REMOVE_EVENT = wxevent.NewEvent()
-    ListMoveEvent,   EVT_ELB_MOVE_EVENT   = wxevent.NewEvent()
-
-    def __init__(
-            self,
-            parent,
-            choices,
-            clientData):
-
-        wx.Panel.__init__(self, parent)
-
-        self.listBox = wx.ListBox(
-            self, style=wx.LB_SINGLE | wx.LB_NEEDED_SB)
-
-        for choice,data in zip(choices, clientData):
-            self.listBox.Append(choice, data)
-
-        self.listBox.Bind(wx.EVT_LISTBOX, self.itemSelected)
-
-        self.buttonPanel = wx.Panel(self)
-
-        self.upButton     = wx.Button(self.buttonPanel, label=u'\u25B2')
-        self.downButton   = wx.Button(self.buttonPanel, label=u'\u25BC')
-        self.addButton    = wx.Button(self.buttonPanel, label='+')
-        self.removeButton = wx.Button(self.buttonPanel, label='-')
-
-        self.upButton    .Bind(wx.EVT_BUTTON, self.moveItemUp)
-        self.downButton  .Bind(wx.EVT_BUTTON, self.moveItemDown)
-        self.addButton   .Bind(wx.EVT_BUTTON, self.addItem)
-        self.removeButton.Bind(wx.EVT_BUTTON, self.removeItem)
-
-        self.buttonPanelSizer = wx.BoxSizer(wx.VERTICAL)
-        self.buttonPanel.SetSizer(self.buttonPanelSizer)
-        self.buttonPanelSizer.Add(self.upButton)
-        self.buttonPanelSizer.Add(self.downButton)
-        self.buttonPanelSizer.Add(self.addButton)
-        self.buttonPanelSizer.Add(self.removeButton)
-
-        self.sizer = wx.BoxSizer(wx.HORIZONTAL)
-        self.SetSizer(self.sizer)
-        
-        self.sizer.Add(self.buttonPanel, flag=wx.EXPAND)
-        self.sizer.Add(self.listBox,     flag=wx.EXPAND, proportion=1)
-
-        self.Layout()
-
-        
-    def itemSelected(self, ev):
-        idx    = ev.GetSelection()
-        choice = self.listBox.GetString(idx)
-        data   = self.listBox.GetClientData(idx)
-
-        print 'selected {}'.format(idx)
-        
-        ev = EditableListBox.ListSelectEvent(
-            idx=idx, choice=choice, data=data)
-        wx.PostEvent(self, ev)
-
-        
-    def moveItem(self, oldIdx, newIdx):
-
-        # nothing is selected
-        if oldIdx  == wx.NOT_FOUND: return
-        
-        choice  = self.listBox.GetString(oldIdx)
-        data    = self.listBox.GetClientData(oldIdx)
-        choices = self.listBox.GetItems()
-
-        if oldIdx < 0 or oldIdx >= len(choices): return
-        if newIdx < 0 or newIdx >= len(choices): return
-        
-        self.listBox.Delete(oldIdx)
-        self.listBox.Insert(choice, newIdx, data)
-        self.listBox.SetSelection(newIdx)
-
-        ev = EditableListBox.ListMoveEvent(
-            oldIdx=oldIdx,
-            newIdx=newIdx,
-            choice=choice,
-            data=data)
-        wx.PostEvent(self, ev)
-
-        
-    def moveItemDown(self, ev):
-        oldIdx = self.listBox.GetSelection()
-        newIdx = oldIdx+1
-        self.moveItem(oldIdx, newIdx)
-
-        
-    def moveItemUp(self, ev):
-        oldIdx = self.listBox.GetSelection()
-        newIdx = oldIdx-1
-        self.moveItem(oldIdx, newIdx) 
-
-        
-    def addItem(self, ev):
-        pass
-
-        
-    def removeItem(self, ev):
-        pass
-
-        
-class DisplayControl(wx.Panel):
-    
-    def __init__(self, parent, imageList):
-        
-        wx.Panel.__init__(self, parent)
-        self.imageList = imageList
-
-        imageNames = [img.name for img in imageList]
-        self.listBox = EditableListBox(
-            self, imageNames, imageList)
-
-        self.listBox.Bind(
-            EditableListBox.EVT_ELB_SELECT_EVENT, self.imageSelected)
-        self.listBox.Bind(
-            EditableListBox.EVT_ELB_MOVE_EVENT, self.imageMoved) 
-
-        self.editPanels = []
-        for image in imageList:
-            displayProps = props.buildGUI(self, image.display)
-            self.editPanels.append(displayProps)
-
-
-        self.sizer = wx.BoxSizer(wx.HORIZONTAL)
-        self.SetSizer(self.sizer)
-
-        self.sizer.Add(self.listBox, flag=wx.EXPAND, proportion=1)
-        
-        for i,editPanel in enumerate(self.editPanels):
-            
-            self.sizer.Add(editPanel, flag=wx.EXPAND, proportion=2)
-            editPanel.Show(i == 0)
-            
-        self.Layout()
-
-    def imageMoved(self, ev):
-
-        oldIdx = ev.oldIdx
-        newIdx = ev.newIdx
-
-        image = self.imageList.pop(oldIdx)
-        self.imageList.insert(newIdx, image)
-        self.Refresh()
-
-        
-    def imageSelected(self, ev):
-        """
-        """
-        print 'imageSelected'
-        
-        for i,editPanel in enumerate(self.editPanels):
-            editPanel.Show(i == ev.idx)
-        self.Layout()
-
-
-class ImageView(wx.Panel):
-
-    def __init__(self, parent, imageList, *args, **kwargs):
-        """
-        Creates three SliceCanvas objects, each displaying a
-        different axis of the given image list.
-        """
-        
-        if isinstance(imageList, fslimage.Image):
-            imageList = fslimage.ImageList(imageList)
-
-        if not isinstance(imageList, fslimage.ImageList):
-            raise TypeError(
-                'imageList must be a fsl.data.fslimage.ImageList instance')
-
-        self.imageList = imageList
-
-        wx.Panel.__init__(self, parent, *args, **kwargs)
-        self.SetMinSize((300,100))
-
-        self.shape = imageList[0].data.shape
-
-        self.canvasPanel = wx.Panel(self)
-
-        self.xcanvas = slicecanvas.SliceCanvas(
-            self.canvasPanel, imageList, zax=0)
-        self.ycanvas = slicecanvas.SliceCanvas(
-            self.canvasPanel, imageList, zax=1, context=self.xcanvas.context)
-        self.zcanvas = slicecanvas.SliceCanvas(
-            self.canvasPanel, imageList, zax=2, context=self.xcanvas.context)
-
-        self.controlPanel = DisplayControl(self, imageList)
-
-        self.mainSizer   = wx.BoxSizer(wx.VERTICAL)
-        self.canvasSizer = wx.BoxSizer(wx.HORIZONTAL)
-
-        self.SetSizer(self.mainSizer)
-
-        self.mainSizer.Add(self.canvasPanel,  flag=wx.EXPAND, proportion=1)
-        self.mainSizer.Add(self.controlPanel, flag=wx.EXPAND)
-        
-        self.canvasPanel.SetSizer(self.canvasSizer)
-
-        self.canvasSizer.Add(self.xcanvas, flag=wx.EXPAND, proportion=1)
-        self.canvasSizer.Add(self.ycanvas, flag=wx.EXPAND, proportion=1)
-        self.canvasSizer.Add(self.zcanvas, flag=wx.EXPAND, proportion=1)
-
-        self.canvasPanel.Layout()
-        self.Layout()
-
-        self.xcanvas.Bind(wx.EVT_LEFT_DOWN, self._setCanvasPosition)
-        self.ycanvas.Bind(wx.EVT_LEFT_DOWN, self._setCanvasPosition)
-        self.zcanvas.Bind(wx.EVT_LEFT_DOWN, self._setCanvasPosition)
-        self.xcanvas.Bind(wx.EVT_MOTION,    self._setCanvasPosition)
-        self.ycanvas.Bind(wx.EVT_MOTION,    self._setCanvasPosition)
-        self.zcanvas.Bind(wx.EVT_MOTION,    self._setCanvasPosition)
-
-        
-    def setLocation(self, x, y, z):
-        """
-        Programmatically set the currently displayed location
-        on each of the canvases. This does not trigger an
-        EVT_LOCATION_EVENT.
-        """
-
-        self.xcanvas.xpos = y
-        self.xcanvas.ypos = z
-        self.xcanvas.zpos = x
-
-        self.ycanvas.xpos = x
-        self.ycanvas.ypos = z
-        self.ycanvas.zpos = y
-        
-        self.zcanvas.xpos = x
-        self.zcanvas.ypos = y
-        self.zcanvas.zpos = z
-
-        self.xcanvas.Refresh()
-        self.ycanvas.Refresh()
-        self.zcanvas.Refresh()
-
-    def setXLocation(self, x):
-        self.setLocation(x, self.ycanvas.zpos, self.zcanvas.zpos)
-
-    def setYLocation(self, y):
-        self.setLocation(self.xcanvas.zpos, y, self.zcanvas.zpos)
-
-    def setZLocation(self, z):
-        self.setLocation(self.xcanvas.zpos, self.ycanvas.zpos, z)
-
-
-    def _setCanvasPosition(self, ev):
-        """
-        Called on mouse movement and left clicks. The currently
-        displayed slices and cursor positions on each of the
-        canvases follow mouse clicks and drags.
-        """
-
-        if not ev.LeftIsDown(): return
-
-        mx,my  = ev.GetPositionTuple()
-        source = ev.GetEventObject()
-        w,h = source.GetClientSize()
-
-        my = h - my
-
-        x = self.xcanvas.zpos
-        y = self.ycanvas.zpos
-        z = self.zcanvas.zpos
-
-        if source == self.xcanvas:
-
-            mx = mx * self.shape[1] / float(w)
-            my = my * self.shape[2] / float(h)
-            y,z = mx,my
-
-        elif source == self.ycanvas:
-            mx = mx * self.shape[0] / float(w)
-            my = my * self.shape[2] / float(h)
-            x,z = mx,my
-
-        elif source == self.zcanvas:
-            mx = mx * self.shape[0] / float(w)
-            my = my * self.shape[1] / float(h)
-            x,y = mx,my
-
-        x = int(x)
-        y = int(y)
-        z = int(z)
-
-        if x < 0: x = 0
-        if y < 0: y = 0
-        if z < 0: z = 0
-
-        if x >= self.shape[0]: x = self.shape[0]-1
-        if y >= self.shape[1]: y = self.shape[1]-1
-        if z >= self.shape[2]: z = self.shape[2]-1 
-
-        self.setLocation(x,y,z)
-
-        evt = LocationEvent(x=x,y=y,z=z)
-        wx.PostEvent(self, evt)
-
-
-class ImageFrame(wx.Frame):
-    """
-    Convenience class for displaying a collection of images in a standalone
-    window.
-    """
-
-    def __init__(self, parent, imageList, title=None):
-        wx.Frame.__init__(self, parent, title=title)
-
-        self.panel = ImageView(self, imageList)
-        self.Layout()
-
-
-if __name__ == '__main__':
-
-    if len(sys.argv) < 2:
-        print 'usage: imageview.py filename [filename]'
-        sys.exit(1)
-
-    app       = wx.App()
-    images    = map(fslimage.Image, sys.argv[1:])
-    imageList = fslimage.ImageList(images)
-    
-    frame  = ImageFrame(
-        None,
-        imageList,
-        title=sys.argv[1])
-    frame.Show()
-
-    app.MainLoop()