Index: src/sas/sasgui/perspectives/file_converter/converter_panel.py
===================================================================
--- src/sas/sasgui/perspectives/file_converter/converter_panel.py	(revision d6bf0640d935e9a1724ba92501e293aec9b535cc)
+++ src/sas/sasgui/perspectives/file_converter/converter_panel.py	(revision ba65aff97e4c9511e2e199a78137312a6adc403d)
@@ -23,5 +23,5 @@
 from sas.sasgui.perspectives.file_converter.cansas_writer import CansasWriter
 from sas.sascalc.dataloader.readers.red2d_reader import Reader as Red2DWriter
-from sas.sasgui.perspectives.file_converter.bsl_loader import BSLLoader as OTOKOLoader
+from sas.sasgui.perspectives.file_converter.otoko_loader import OTOKOLoader
 from sas.sascalc.file_converter.bsl_loader import BSLLoader
 from sas.sascalc.dataloader.data_info import Detector
@@ -45,4 +45,7 @@
 
 class ConverterPanel(ScrolledPanel, PanelBase):
+    """
+    This class provides the File Converter GUI
+    """
 
     def __init__(self, parent, base=None, *args, **kwargs):
@@ -56,4 +59,5 @@
         self.meta_frames = []
 
+        # GUI inputs
         self.q_input = None
         self.iq_input = None
@@ -61,6 +65,8 @@
         self.radiation_input = None
         self.metadata_section = None
+
         self.data_type = "ascii"
 
+        # Metadata values
         self.title = None
         self.run = None
@@ -80,10 +86,20 @@
 
     def convert_to_cansas(self, frame_data, filename, single_file):
-        reader = CansasWriter()
+        """
+        Saves an array of Data1D objects to a single CanSAS file with multiple
+        <SasData> elements, or to multiple CanSAS files, each with one
+        <SasData> element.
+
+        :param frame_data: The Data1D object to save
+        :param filename: Where to save the CanSAS file
+        :param single_file: If true, array is saved as a single file, if false,
+        each item in the array is saved to it's own file
+        """
+        writer = CansasWriter()
         entry_attrs = None
         if self.run_name is not None:
             entry_attrs = { 'name': self.run_name }
         if single_file:
-            reader.write(filename, frame_data,
+            writer.write(filename, frame_data,
                 sasentry_attrs=entry_attrs)
         else:
@@ -92,9 +108,20 @@
             name = filename.replace(ext, '')
             for i in range(len(frame_data)):
-                f_name = "{}{}{}".format(name, i+1, ext)
-                reader.write(f_name, [frame_data[i]],
+                # TODO: Change i to actual frame number, not consecutive numbers
+                # (maybe use info in params from frame select dialog, ie
+                # increment. Alternatively, change frame_data to a dictionary
+                # with the frame number as key and use frame_data.iteritems()
+                # (would need to update CansasWriter to deal with this)
+                f_name = name + str(i) + ext
+                writer.write(f_name, [frame_data[i]],
                     sasentry_attrs=entry_attrs)
 
     def extract_ascii_data(self, filename):
+        """
+        Extracts data from a single-column ASCII file
+
+        :param filename: The file to load data from
+        :return: A numpy array containing the extracted data
+        """
         data = np.loadtxt(filename, dtype=str)
 
@@ -122,4 +149,10 @@
 
     def extract_otoko_data(self, filename):
+        """
+        Extracts data from a 1D OTOKO file
+
+        :param filename: The OTOKO file to load the data from
+        :return: A numpy array containing the extracted data
+        """
         loader = OTOKOLoader(self.q_input.GetPath(),
             self.iq_input.GetPath())
@@ -139,4 +172,11 @@
 
     def ask_frame_range(self, n_frames):
+        """
+        Display a dialog asking the user to input the range of frames they
+        would like to export
+
+        :param n_frames: How many frames the loaded data file has
+        :return: A dictionary containing the parameters input by the user
+        """
         valid_input = False
         is_bsl = (self.data_type == 'bsl')
@@ -179,4 +219,5 @@
 
     def on_convert(self, event):
+        """Called when the Convert button is clicked"""
         if not self.validate_inputs():
             return
@@ -191,4 +232,5 @@
                 qdata, iqdata = self.extract_otoko_data(self.q_input.GetPath())
             else: # self.data_type == 'bsl'
+                # TODO: Refactor this into an extract_bsl_data method
                 loader = BSLLoader(self.iq_input.GetPath())
                 frames = [0]
@@ -243,5 +285,6 @@
         # Standard file has 3 frames: SAS, calibration and WAS
         if n_frames > 3:
-            # File has multiple frames
+            # File has multiple frames - ask the user which ones they want to
+            # export
             params = self.ask_frame_range(n_frames)
             frames = params['frames']
@@ -254,4 +297,5 @@
         output_path = self.output.GetPath()
 
+        # Prepare the metadata for writing to a file
         if self.run is None:
             self.run = []
@@ -293,4 +337,7 @@
 
     def on_help(self, event):
+        """
+        Show the File Converter documentation
+        """
         tree_location = ("user/sasgui/perspectives/file_converter/"
             "file_converter_help.html")
@@ -314,4 +361,7 @@
 
     def show_detector_window(self, event):
+        """
+        Show the window for inputting :class:`~sas.sascalc.dataloader.data_info.Detector~` metadata
+        """
         if self.meta_frames != []:
             for frame in self.meta_frames:
@@ -325,4 +375,7 @@
 
     def show_sample_window(self, event):
+        """
+        Show the window for inputting :class:`~sas.sascalc.dataloader.data_info.Sample~` metadata
+        """
         if self.meta_frames != []:
             for frame in self.meta_frames:
@@ -336,4 +389,7 @@
 
     def show_source_window(self, event):
+        """
+        Show the window for inputting :class:`~sas.sascalc.dataloader.data_info.Source~` metadata
+        """
         if self.meta_frames != []:
             for frame in self.meta_frames:
@@ -347,4 +403,8 @@
 
     def on_collapsible_pane(self, event):
+        """
+        Resize the scrollable area to fit the metadata pane when it's
+        collapsed or expanded
+        """
         self.Freeze()
         self.SetupScrolling()
@@ -353,4 +413,8 @@
 
     def datatype_changed(self, event):
+        """
+        Update the UI and self.data_type when a data type radio button is
+        pressed
+        """
         event.Skip()
         dtype = event.GetEventObject().GetName()
@@ -527,4 +591,5 @@
 
 class ConverterWindow(widget.CHILD_FRAME):
+    """Displays ConverterPanel"""
 
     def __init__(self, parent=None, title='File Converter', base=None,
