Index: src/sas/sascalc/dataloader/readers/anton_paar_saxs_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/anton_paar_saxs_reader.py	(revision 80c5d4645fc45af0c2a06ead98749184616799ef)
+++ src/sas/sascalc/dataloader/readers/anton_paar_saxs_reader.py	(revision 80c5d4645fc45af0c2a06ead98749184616799ef)
@@ -0,0 +1,118 @@
+"""
+    CanSAS 2D data reader for reading HDF5 formatted CanSAS files.
+"""
+
+import numpy as np
+import re
+import os
+import sys
+
+from sas.sascalc.dataloader.readers.xml_reader import XMLreader
+from sas.sascalc.dataloader.data_info import plottable_1D, Data1D, Sample, Source
+from sas.sascalc.dataloader.data_info import Process, Aperture, Collimation, TransmissionSpectrum, Detector
+
+
+class Reader(XMLreader):
+    """
+    A class for reading in CanSAS v2.0 data files. The existing iteration opens Mantid generated HDF5 formatted files
+    with file extension .h5/.H5. Any number of data sets may be present within the file and any dimensionality of data
+    may be used. Currently 1D and 2D SAS data sets are supported, but future implementations will include 1D and 2D
+    SESANS data. This class assumes a single data set for each sasentry.
+
+    :Dependencies:
+        The CanSAS HDF5 reader requires h5py v2.5.0 or later.
+    """
+
+    ## Logged warnings or messages
+    logging = None
+    ## List of errors for the current data set
+    errors = None
+    ## Raw file contents to be processed
+    raw_data = None
+    ## Data set being modified
+    current_dataset = None
+    ## For recursion and saving purposes, remember parent objects
+    parent_list = None
+    ## Data type name
+    type_name = "Anton Paar SAXSess"
+    ## Wildcards
+    type = ["Anton Paar SAXSess Files (*.pdh)|*.pdh"]
+    ## List of allowed extensions
+    ext = ['.pdh', '.PDH']
+    ## Flag to bypass extension check
+    allow_all = False
+    ## List of files to return
+    output = None
+
+    def __init__(self):
+        self.current_dataset = Data1D(np.empty(0), np.empty(0),
+                                            np.empty(0), np.empty(0))
+        self.datasets = []
+        self.raw_data = None
+        self.errors = set()
+        self.logging = []
+        self.output = []
+        self.detector = Detector()
+        self.collimation = Collimation()
+        self.aperture = Aperture()
+        self.process = Process()
+        self.source = Source()
+        self.sample = Sample()
+        self.trans_spectrum = TransmissionSpectrum()
+        self.upper = 5
+        self.lower = 5
+
+    def read(self, filename):
+        """
+            This is the general read method that all SasView data_loaders must have.
+
+            :param filename: A path for an XML formatted Anton Paar SAXSess data file.
+            :return: List of Data1D objects or a list of errors.
+            """
+
+        ## Reinitialize the class when loading a new data file to reset all class variables
+        self.__init__()
+        ## Check that the file exists
+        if os.path.isfile(filename):
+            basename = os.path.basename(filename)
+            _, extension = os.path.splitext(basename)
+            # If the file type is not allowed, return empty list
+            if extension in self.ext or self.allow_all:
+                ## Load the data file
+                input_f = open(filename, 'r')
+                buff = input_f.read()
+                self.raw_data = buff.splitlines()
+                self.read_data()
+                xml_intermediate = self.raw_data[self.upper:]
+                xml = ''.join(xml_intermediate)
+                self.set_xml_file(xml)
+        return self.output
+
+    def read_data(self):
+        q_unit = "1/nm"
+        i_unit = "1/um^2"
+        self.current_dataset.title = self.raw_data[0]
+        self.current_dataset.meta_data["Keywords"] = self.raw_data[1]
+        line3 = self.raw_data[2].split()
+        line4 = self.raw_data[3].split()
+        line5 = self.raw_data[4].split()
+        self.data_points = int(line3[0])
+        self.lower = 5
+        self.upper = self.lower + self.data_points
+        self.detector.distance = float(line4[1])
+        self.current_dataset.source.radiation = "x-ray"
+        self.current_dataset.source.name = "Anton Paar SAXSess Instrument"
+        self.current_dataset.source.wavelength = float(line4[4])
+        normal = line4[3]
+        for i in range(self.lower, self.upper):
+            data = self.raw_data[i].split()
+            x_val = [float(data[0])]
+            y_val = [float(data[1])]
+            dy_val = [float(data[2])]
+            self.current_dataset.x = np.append(self.current_dataset.x, x_val)
+            self.current_dataset.y = np.append(self.current_dataset.y, y_val)
+            self.current_dataset.dy = np.append(self.current_dataset.dy, dy_val)
+        self.current_dataset.xaxis("Q (%s)" % (q_unit), q_unit)
+        self.current_dataset.yaxis("Intensity (%s)" % (i_unit), i_unit)
+        self.current_dataset.detector.append(self.detector)
+        self.output.append(self.current_dataset)
Index: src/sas/sascalc/dataloader/readers/cansas_reader_HDF5.py
===================================================================
--- src/sas/sascalc/dataloader/readers/cansas_reader_HDF5.py	(revision ad52d31bb746a70198079b061be06440f3072786)
+++ src/sas/sascalc/dataloader/readers/cansas_reader_HDF5.py	(revision ad52d31bb746a70198079b061be06440f3072786)
@@ -0,0 +1,427 @@
+"""
+    CanSAS 2D data reader for reading HDF5 formatted CanSAS files.
+"""
+
+import h5py
+import numpy as np
+import re
+import os
+import sys
+
+from sas.sascalc.dataloader.data_info import plottable_1D, plottable_2D, Data1D, Data2D, Sample, Source
+from sas.sascalc.dataloader.data_info import Process, Aperture, Collimation, TransmissionSpectrum, Detector
+
+
+class Reader():
+    """
+    A class for reading in CanSAS v2.0 data files. The existing iteration opens Mantid generated HDF5 formatted files
+    with file extension .h5/.H5. Any number of data sets may be present within the file and any dimensionality of data
+    may be used. Currently 1D and 2D SAS data sets are supported, but future implementations will include 1D and 2D
+    SESANS data. This class assumes a single data set for each sasentry.
+
+    :Dependencies:
+        The CanSAS HDF5 reader requires h5py v2.5.0 or later.
+    """
+
+    ## CanSAS version
+    cansas_version = 2.0
+    ## Logged warnings or messages
+    logging = None
+    ## List of errors for the current data set
+    errors = None
+    ## Raw file contents to be processed
+    raw_data = None
+    ## Data set being modified
+    current_dataset = None
+    ## For recursion and saving purposes, remember parent objects
+    parent_list = None
+    ## Data type name
+    type_name = "CanSAS 2.0"
+    ## Wildcards
+    type = ["CanSAS 2.0 HDF5 Files (*.h5)|*.h5"]
+    ## List of allowed extensions
+    ext = ['.h5', '.H5']
+    ## Flag to bypass extension check
+    allow_all = False
+    ## List of files to return
+    output = None
+
+    def __init__(self):
+        """
+        Create the reader object and define initial states for class variables
+        """
+        self.current_dataset = None
+        self.datasets = []
+        self.raw_data = None
+        self.errors = set()
+        self.logging = []
+        self.parent_list = []
+        self.output = []
+        self.detector = Detector()
+        self.collimation = Collimation()
+        self.aperture = Aperture()
+        self.process = Process()
+        self.sample = Sample()
+        self.source = Source()
+        self.trans_spectrum  = TransmissionSpectrum()
+
+    def read(self, filename):
+        """
+        This is the general read method that all SasView data_loaders must have.
+
+        :param filename: A path for an HDF5 formatted CanSAS 2D data file.
+        :return: List of Data1D/2D objects or a list of errors.
+        """
+
+        ## Reinitialize the class when loading a new data file to reset all class variables
+        self.__init__()
+        ## Check that the file exists
+        if os.path.isfile(filename):
+            basename = os.path.basename(filename)
+            _, extension = os.path.splitext(basename)
+            # If the file type is not allowed, return empty list
+            if extension in self.ext or self.allow_all:
+                ## Load the data file
+                self.raw_data = h5py.File(filename, 'r')
+                ## Read in all child elements of top level SASroot
+                self.read_children(self.raw_data)
+                ## Add the last data set to the list of outputs
+                self.add_data_set()
+        ## Return data set(s)
+        return self.output
+
+    def read_children(self, data, parent=u'SASroot'):
+        """
+        A recursive method for stepping through the hierarchical data file.
+
+        :param data: h5py Group object of any kind
+        :param parent: h5py Group parent name
+        :return: None
+        """
+
+        ## Create regex for base sasentry and for parent
+        parent_prog = re.compile(parent)
+
+        ## Loop through each element of the parent and process accordingly
+        for key in data.keys():
+            ## Get all information for the current key
+            value = data.get(key)
+            attr_keys = value.attrs.keys()
+            attr_values = value.attrs.values()
+            class_name = value.attrs.get(u'NX_class')
+            if class_name is not None:
+                class_prog = re.compile(class_name)
+            else:
+                class_prog = re.compile(value.name)
+
+            if isinstance(value, h5py.Group):
+                ##TODO: Rework this for multiple SASdata objects within a single SASentry to allow for both 1D and 2D
+                ##TODO:     data within the same SASentry - One 1D and one 2D data object for all SASdata sets?
+                ## If this is a new sasentry, store the current data set and create a fresh Data1D/2D object
+                if class_prog.match(u'SASentry'):
+                    self.add_data_set(key)
+                ## Recursion step to access data within the group
+                self.read_children(value, class_name)
+                self.add_intermediate(class_name)
+
+            elif isinstance(value, h5py.Dataset):
+                ## If this is a dataset, store the data appropriately
+                data_set = data[key][:]
+
+                for data_point in data_set:
+                    ## Top Level Meta Data
+                    if key == u'definition':
+                        self.current_dataset.meta_data['reader'] = data_point
+                    elif key == u'run':
+                        self.current_dataset.run.append(data_point)
+                    elif key == u'title':
+                        self.current_dataset.title = data_point
+                    elif key == u'SASnote':
+                        self.current_dataset.notes.append(data_point)
+
+                    ## I and Q Data
+                    elif key == u'I':
+                        i_unit = value.attrs.get(u'unit')
+                        if type(self.current_dataset) is Data2D:
+                            self.current_dataset.data = np.append(self.current_dataset.data, data_point)
+                            self.current_dataset.zaxis("Intensity (%s)" % (i_unit), i_unit)
+                        else:
+                            self.current_dataset.y = np.append(self.current_dataset.y, data_point)
+                            self.current_dataset.yaxis("Intensity (%s)" % (i_unit), i_unit)
+                    elif key == u'Idev':
+                        if type(self.current_dataset) is Data2D:
+                            self.current_dataset.err_data = np.append(self.current_dataset.err_data, data_point)
+                        else:
+                            self.current_dataset.dy = np.append(self.current_dataset.dy, data_point)
+                    elif key == u'Q':
+                        q_unit = value.attrs.get(u'unit')
+                        self.current_dataset.xaxis("Q (%s)" % (q_unit), q_unit)
+                        if type(self.current_dataset) is Data2D:
+                            self.current_dataset.q = np.append(self.current_dataset.q, data_point)
+                        else:
+                            self.current_dataset.x = np.append(self.current_dataset.x, data_point)
+                    elif key == u'Qy':
+                        q_unit = value.attrs.get(u'unit')
+                        self.current_dataset.yaxis("Q (%s)" % (q_unit), q_unit)
+                        self.current_dataset.qy_data = np.append(self.current_dataset.qy_data, data_point)
+                    elif key == u'Qydev':
+                        self.current_dataset.dqy_data = np.append(self.current_dataset.dqy_data, data_point)
+                    elif key == u'Qx':
+                        q_unit = value.attrs.get(u'unit')
+                        self.current_dataset.xaxis("Q (%s)" % (q_unit), q_unit)
+                        self.current_dataset.qx_data = np.append(self.current_dataset.qx_data, data_point)
+                    elif key == u'Qxdev':
+                        self.current_dataset.dqx_data = np.append(self.current_dataset.dqx_data, data_point)
+                    elif key == u'Mask':
+                        self.current_dataset.mask = np.append(self.current_dataset.mask, data_point)
+
+                    ## Sample Information
+                    elif key == u'Title' and parent == u'SASsample':
+                        self.sample.name = data_point
+                    elif key == u'thickness' and parent == u'SASsample':
+                        self.sample.thickness = data_point
+                    elif key == u'temperature' and parent == u'SASsample':
+                        self.sample.temperature = data_point
+
+                    ## Instrumental Information
+                    elif key == u'name' and parent == u'SASinstrument':
+                        self.current_dataset.instrument = data_point
+                    elif key == u'name' and parent == u'SASdetector':
+                        self.detector.name = data_point
+                    elif key == u'SDD' and parent == u'SASdetector':
+                        self.detector.distance = data_point
+                        self.detector.distance_unit = value.attrs.get(u'unit')
+                    elif key == u'SSD' and parent == u'SAScollimation':
+                        self.collimation.length = data_point
+                        self.collimation.length_unit = value.attrs.get(u'unit')
+                    elif key == u'name' and parent == u'SAScollimation':
+                        self.collimation.name = data_point
+
+                    ## Process Information
+                    elif key == u'name' and parent == u'SASprocess':
+                        self.process.name = data_point
+                    elif key == u'Title' and parent == u'SASprocess':
+                        self.process.name = data_point
+                    elif key == u'description' and parent == u'SASprocess':
+                        self.process.description = data_point
+                    elif key == u'date' and parent == u'SASprocess':
+                        self.process.date = data_point
+                    elif parent == u'SASprocess':
+                        self.process.notes.append(data_point)
+
+                    ## Transmission Spectrum
+                    elif key == u'T' and parent == u'SAStransmission_spectrum':
+                        self.trans_spectrum.transmission.append(data_point)
+                    elif key == u'Tdev' and parent == u'SAStransmission_spectrum':
+                        self.trans_spectrum.transmission_deviation.append(data_point)
+                    elif key == u'lambda' and parent == u'SAStransmission_spectrum':
+                        self.trans_spectrum.wavelength.append(data_point)
+
+                    ## Other Information
+                    elif key == u'wavelength' and parent == u'SASdata':
+                        self.source.wavelength = data_point
+                        self.source.wavelength.unit = value.attrs.get(u'unit')
+                    elif key == u'radiation' and parent == u'SASsource':
+                        self.source.radiation = data_point
+                    elif key == u'transmission' and parent == u'SASdata':
+                        self.sample.transmission = data_point
+
+                    ## Everything else goes in meta_data
+                    else:
+                        new_key = self._create_unique_key(self.current_dataset.meta_data, key)
+                        self.current_dataset.meta_data[new_key] = data_point
+
+            else:
+                ## I don't know if this reachable code
+                self.errors.add("ShouldNeverHappenException")
+
+    def add_intermediate(self, parent):
+        """
+        This method stores any intermediate objects within the final data set after fully reading the set.
+
+        :param parent: The NXclass name for the h5py Group object that just finished being processed
+        :return:
+        """
+
+        if parent == u'SASprocess':
+            self.current_dataset.process.append(self.process)
+            self.process = Process()
+        elif parent == u'SASdetector':
+            self.current_dataset.detector.append(self.detector)
+            self.detector = Detector()
+        elif parent == u'SAStransmission_spectrum':
+            self.current_dataset.trans_spectrum.append(self.trans_spectrum)
+            self.trans_spectrum = TransmissionSpectrum()
+        elif parent == u'SASsource':
+            self.current_dataset.source = self.source
+            self.source = Source()
+        elif parent == u'SASsample':
+            self.current_dataset.sample = self.sample
+            self.sample = Sample()
+        elif parent == u'SAScollimation':
+            self.current_dataset.collimation.append(self.collimation)
+            self.collimation = Collimation()
+        elif parent == u'SASaperture':
+            self.collimation.aperture.append(self.aperture)
+            self.aperture = Aperture()
+
+    def final_data_cleanup(self):
+        """
+        Does some final cleanup and formatting on self.current_dataset
+        """
+
+        ## Type cast data arrays to float64 and find min/max as appropriate
+        if type(self.current_dataset) is Data2D:
+            self.current_dataset.data = np.delete(self.current_dataset.data, [0])
+            self.current_dataset.data = self.current_dataset.data.astype(np.float64)
+            self.current_dataset.err_data = np.delete(self.current_dataset.err_data, [0])
+            self.current_dataset.err_data = self.current_dataset.err_data.astype(np.float64)
+            self.current_dataset.mask = np.delete(self.current_dataset.mask, [0])
+            if self.current_dataset.qx_data is not None:
+                self.current_dataset.qx_data = np.delete(self.current_dataset.qx_data, [0])
+                self.current_dataset.xmin = np.min(self.current_dataset.qx_data)
+                self.current_dataset.xmax = np.max(self.current_dataset.qx_data)
+                self.current_dataset.qx_data = self.current_dataset.qx_data.astype(np.float64)
+            if self.current_dataset.dqx_data is not None:
+                self.current_dataset.dqx_data = np.delete(self.current_dataset.dqx_data, [0])
+                self.current_dataset.dqx_data = self.current_dataset.dqx_data.astype(np.float64)
+            if self.current_dataset.qy_data is not None:
+                self.current_dataset.qy_data = np.delete(self.current_dataset.qy_data, [0])
+                self.current_dataset.ymin = np.min(self.current_dataset.qy_data)
+                self.current_dataset.ymax = np.max(self.current_dataset.qy_data)
+                self.current_dataset.qy_data = self.current_dataset.qy_data.astype(np.float64)
+            if self.current_dataset.dqy_data is not None:
+                self.current_dataset.dqy_data = np.delete(self.current_dataset.dqy_data, [0])
+                self.current_dataset.dqy_data = self.current_dataset.dqy_data.astype(np.float64)
+            if self.current_dataset.q_data is not None:
+                self.current_dataset.q_data = np.delete(self.current_dataset.q_data, [0])
+                self.current_dataset.q_data = self.current_dataset.q_data.astype(np.float64)
+            zeros = np.ones(self.current_dataset.data.size, dtype=bool)
+            try:
+                for i in range (0, self.current_dataset.mask.size - 1):
+                    zeros[i] = self.current_dataset.mask[i]
+            except:
+                self.errors.add(sys.exc_value)
+            self.current_dataset.mask = zeros
+
+            ## Calculate the actual Q matrix
+            try:
+                if self.current_dataset.q_data.size <= 1:
+                    self.current_dataset.q_data = np.sqrt(self.current_dataset.qx_data * self.current_dataset.qx_data +
+                            self.current_dataset.qy_data * self.current_dataset.qy_data)
+            except:
+                self.current_dataset.q_data = None
+
+        elif type(self.current_dataset) is Data1D:
+            if self.current_dataset.x is not None:
+                self.current_dataset.x = np.delete(self.current_dataset.x, [0])
+                self.current_dataset.x = self.current_dataset.x.astype(np.float64)
+                self.current_dataset.xmin = np.min(self.current_dataset.x)
+                self.current_dataset.xmax = np.max(self.current_dataset.x)
+            if self.current_dataset.y is not None:
+                self.current_dataset.y = np.delete(self.current_dataset.y, [0])
+                self.current_dataset.y = self.current_dataset.y.astype(np.float64)
+                self.current_dataset.ymin = np.min(self.current_dataset.y)
+                self.current_dataset.ymax = np.max(self.current_dataset.y)
+            if self.current_dataset.dx is not None:
+                self.current_dataset.dx = np.delete(self.current_dataset.dx, [0])
+                self.current_dataset.dx = self.current_dataset.dx.astype(np.float64)
+            if self.current_dataset.dxl is not None:
+                self.current_dataset.dxl = np.delete(self.current_dataset.dxl, [0])
+                self.current_dataset.dxl = self.current_dataset.dxl.astype(np.float64)
+            if self.current_dataset.dxw is not None:
+                self.current_dataset.dxw = np.delete(self.current_dataset.dxw, [0])
+                self.current_dataset.dxw = self.current_dataset.dxw.astype(np.float64)
+            if self.current_dataset.dy is not None:
+                self.current_dataset.dy = np.delete(self.current_dataset.dy, [0])
+                self.current_dataset.dy =self.current_dataset.dy.astype(np.float64)
+
+        if len(self.current_dataset.trans_spectrum) is not 0:
+            spectrum_list = []
+            for spectrum in self.current_dataset.trans_spectrum:
+                spectrum.transmission = np.delete(spectrum.transmission, [0])
+                spectrum.transmission = spectrum.transmission.astype(np.float64)
+                spectrum.transmission_deviation = np.delete(spectrum.transmission_deviation, [0])
+                spectrum.transmission_deviation = spectrum.transmission_deviation.astype(np.float64)
+                spectrum.wavelength = np.delete(spectrum.wavelength, [0])
+                spectrum.wavelength = spectrum.wavelength.astype(np.float64)
+                spectrum_list.append(spectrum)
+            self.current_dataset.trans_spectrum = spectrum_list
+
+        else:
+            self.errors.add("ShouldNeverHappenException")
+
+        ## Append intermediate objects to data
+        self.current_dataset.sample = self.sample
+        self.current_dataset.source = self.source
+        self.current_dataset.collimation.append(self.collimation)
+
+        ## Append errors to dataset and reset class errors
+        self.current_dataset.errors = self.errors
+        self.errors.clear()
+
+    def add_data_set(self, key=""):
+        """
+        Adds the current_dataset to the list of outputs after preforming final processing on the data and then calls a
+        private method to generate a new data set.
+
+        :param key: NeXus group name for current tree level
+        :return: None
+        """
+        if self.current_dataset is not None:
+            self.final_data_cleanup()
+            self.output.append(self.current_dataset)
+        self._initialize_new_data_set(key)
+
+    def _initialize_new_data_set(self, key=""):
+        """
+        A private class method to generate a new 1D or 2D data object based on the type of data within the set.
+        Outside methods should call add_data_set() to be sure any existing data is stored properly.
+
+        :param key: NeXus group name for current tree level
+        :return: None
+        """
+        entry = self._find_intermediate(key, "sasentry*")
+        data = entry.get("sasdata")
+        if data.get("Qx") is not None:
+            self.current_dataset = Data2D()
+        else:
+            x = np.array(0)
+            y = np.array(0)
+            self.current_dataset = Data1D(x, y)
+        self.current_dataset.filename = self.raw_data.filename
+
+    def _find_intermediate(self, key="", basename=""):
+        """
+        A private class used to find an entry by either using a direct key or knowing the approximate basename.
+
+        :param key: Exact keyname of an entry
+        :param basename: Approximate name of an entry
+        :return:
+        """
+        entry = []
+        if key is not "":
+            entry = self.raw_data.get(key)
+        else:
+            key_prog = re.compile(basename)
+            for key in self.raw_data.keys():
+                if (key_prog.match(key)):
+                    entry = self.raw_data.get(key)
+                    break
+        return entry
+
+    def _create_unique_key(self, dictionary, name, numb=0):
+        """
+        Create a unique key value for any dictionary to prevent overwriting
+        Recurses until a unique key value is found.
+
+        :param dictionary: A dictionary with any number of entries
+        :param name: The index of the item to be added to dictionary
+        :param numb: The number to be appended to the name, starts at 0
+        """
+        if dictionary.get(name) is not None:
+            numb += 1
+            name = name.split("_")[0]
+            name += "_{0}".format(numb)
+            name = self._create_unique_key(dictionary, name, numb)
+        return name
Index: src/sas/sascalc/dataloader/readers/defaults.json
===================================================================
--- src/sas/sascalc/dataloader/readers/defaults.json	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/dataloader/readers/defaults.json	(revision 80c5d4645fc45af0c2a06ead98749184616799ef)
@@ -9,4 +9,8 @@
             "-extension":".ses",
             "-reader":"sesans_reader"
+         },
+         {
+            "-extension":".h5",
+            "-reader":"cansas_reader_HDF5"
          },
          {
@@ -37,4 +41,8 @@
             "-extension":".nxs",
             "-reader":"nexus_reader"
+         },
+         {
+            "-extension":".pdh",
+            "-reader":"anton_paar_saxs_reader"
          }
       ]
Index: src/sas/sasgui/guiframe/data_panel.py
===================================================================
--- src/sas/sasgui/guiframe/data_panel.py	(revision d85c194ea2f78cb2b9b3de9c969e1b8ebea6b6d2)
+++ src/sas/sasgui/guiframe/data_panel.py	(revision 1941e6a4dc7d7e117ddc9faa4c5eaf341725564c)
@@ -13,4 +13,5 @@
 import wx
 from wx.build import build_options
+
 # Check version
 toks = str(wx.__version__).split('.')
@@ -291,16 +292,23 @@
         Layout widgets related to buttons
         """
+        #Load Data Button
         self.bt_add = wx.Button(self, wx.NewId(), "Load Data", 
                                 size=(BUTTON_WIDTH, -1))
         self.bt_add.SetToolTipString("Load data files")
         wx.EVT_BUTTON(self, self.bt_add.GetId(), self._load_data)
+        
+        #Delete Data Button
         self.bt_remove = wx.Button(self, wx.NewId(), "Delete Data",
          size=(BUTTON_WIDTH, -1))
         self.bt_remove.SetToolTipString("Delete data from the application")
         wx.EVT_BUTTON(self, self.bt_remove.GetId(), self.on_remove)
+        
+        #Send data to perspective button
         self.bt_import = wx.Button(self, wx.NewId(), "Send To",
                                     size=(BUTTON_WIDTH, -1))
         self.bt_import.SetToolTipString("Send Data set to active perspective")
         wx.EVT_BUTTON(self, self.bt_import.GetId(), self.on_import)
+        
+        #Choose perspective to be send data to combo box
         self.perspective_cbox = wx.ComboBox(self, -1,
                                 style=wx.CB_READONLY)
@@ -310,4 +318,5 @@
                         self._on_perspective_selection)
     
+        #Append data to current Graph Button
         self.bt_append_plot = wx.Button(self, wx.NewId(), "Append Plot To",
                                         size=(BUTTON_WIDTH, -1))
@@ -316,4 +325,5 @@
         wx.EVT_BUTTON(self, self.bt_append_plot.GetId(), self.on_append_plot)
         
+        #Create a new graph and send data to that new graph button
         self.bt_plot = wx.Button(self, wx.NewId(), "New Plot", 
                                  size=(BUTTON_WIDTH, -1))
@@ -321,4 +331,5 @@
         wx.EVT_BUTTON(self, self.bt_plot.GetId(), self.on_plot)
         
+        #Freeze current theory button - becomes a data set and stays on graph
         self.bt_freeze = wx.Button(self, wx.NewId(), "Freeze Theory", 
                                    size=(BUTTON_WIDTH, -1))
@@ -329,4 +340,5 @@
         wx.EVT_BUTTON(self, self.bt_freeze.GetId(), self.on_freeze)
        
+        #select plot to send to combo box (blank if no data)
         if sys.platform == 'darwin':
             self.cb_plotpanel = wx.ComboBox(self, -1, 
@@ -337,4 +349,10 @@
         wx.EVT_COMBOBOX(self.cb_plotpanel, -1, self._on_plot_selection)
         self.cb_plotpanel.Disable()
+        
+        #Help button
+        self.bt_help = wx.Button(self, wx.NewId(), "HELP",
+                                 size=(BUTTON_WIDTH, -1))
+        self.bt_help.SetToolTipString("Help for the Data Explorer.")
+        wx.EVT_BUTTON(self,self.bt_help.GetId(), self.on_help)
 
         self.sizer3.AddMany([(self.bt_add),
@@ -356,6 +374,6 @@
                              ((10, 10)),
                              (self.sizer4),
-                             ((10, 40)),
-                             ((10, 40))])
+                             ((10, 10)),
+                             (self.bt_help, 0, wx.EXPAND|wx.RIGHT, 5)])
 
         self.sizer3.AddGrowableCol(1, 1)
@@ -1069,4 +1087,27 @@
         return self.frame 
     
+    def on_help(self, event):
+        """
+        Bring up the data manager Documentation whenever
+        the HELP button is clicked.
+
+        Calls DocumentationWindow with the path of the location within the
+        documentation tree (after /doc/ ....".  Note that when using old
+        versions of Wx (before 2.9) and thus not the release version of
+        installers, the help comes up at the top level of the file as
+        webbrowser does not pass anything past the # to the browser when it is
+        running "file:///...."
+
+    :param evt: Triggers on clicking the help button
+    """
+
+        #import documentation window here to avoid circular imports
+        #if put at top of file with rest of imports.
+        from documentation_window import DocumentationWindow
+
+        _TreeLocation = "user/sasgui/guiframe/data_explorer_help.html"
+        _doc_viewer = DocumentationWindow(self, -1, _TreeLocation, "",
+                                          "Data Explorer Help")
+
     def on_close(self, event):
         """
Index: src/sas/sasgui/guiframe/report_dialog.py
===================================================================
--- src/sas/sasgui/guiframe/report_dialog.py	(revision d85c194ea2f78cb2b9b3de9c969e1b8ebea6b6d2)
+++ src/sas/sasgui/guiframe/report_dialog.py	(revision 6dd6e32012f292ecf1f21bdca4ad872eb94b706c)
@@ -15,5 +15,6 @@
     FONT_VARIANT = 0
     ISPDF = True
-elif sys.platform == "darwin":
+# For OSX and everything else
+else:
     _STATICBOX_WIDTH = 480
     PANEL_WIDTH = 530
Index: src/sas/sasgui/perspectives/calculator/model_editor.py
===================================================================
--- src/sas/sasgui/perspectives/calculator/model_editor.py	(revision bb841ef2942a9e860e2d1f70e9a0fc36695812af)
+++ src/sas/sasgui/perspectives/calculator/model_editor.py	(revision 95016615bc8960b6775d667f752de694493a3270)
@@ -578,7 +578,7 @@
                     else:
                         out_f.write(line + "\n")
-                elif line.count("P1 = make_class"):
+                elif line.count("P1 = find_model"):
                     out_f.write(line % (name1) + "\n")
-                elif line.count("P2 = make_class"):
+                elif line.count("P2 = find_model"):
                     out_f.write(line % (name2) + "\n")
 
@@ -719,7 +719,9 @@
         Do the layout for parameter related widgets
         """
-        param_txt = wx.StaticText(self, -1, 'Fit Parameters (if any): ')
-
-        param_tip = "#Set the parameters and initial values.\n"
+        param_txt = wx.StaticText(self, -1, 'Fit Parameters NOT requiring' + \
+                                  ' polydispersity (if any): ')
+
+        param_tip = "#Set the parameters NOT requiring polydispersity " + \
+        "and their initial values.\n"
         param_tip += "#Example:\n"
         param_tip += "A = 1\nB = 1"
@@ -736,7 +738,9 @@
         
         # Parameters with polydispersity
-        pd_param_txt = wx.StaticText(self, -1, 'Fit Parameters requiring polydispersity (if any): ')
-
-        pd_param_tip = "#Set the parameters and initial values.\n"
+        pd_param_txt = wx.StaticText(self, -1, 'Fit Parameters requiring ' + \
+                                     'polydispersity (if any): ')
+
+        pd_param_tip = "#Set the parameters requiring polydispersity and " + \
+        "their initial values.\n"
         pd_param_tip += "#Example:\n"
         pd_param_tip += "C = 2\nD = 2"
@@ -803,10 +807,7 @@
         self.bt_close.SetToolTipString("Close this panel.")
 
-        self.button_sizer.AddMany([(self.bt_apply, 0,
-                                    wx.LEFT, EDITOR_WIDTH * 0.8),
-                                   (self.bt_help, 0,
-                                    wx.LEFT,15),
-                                   (self.bt_close, 0,
-                                    wx.LEFT | wx.BOTTOM, 15)])
+        self.button_sizer.AddMany([(self.bt_apply, 0,0),
+                                   (self.bt_help, 0, wx.LEFT | wx.BOTTOM,15),
+                                   (self.bt_close, 0, wx.LEFT | wx.RIGHT, 15)])
 
     def _do_layout(self):
@@ -835,5 +836,5 @@
                                   wx.ALL | wx.EXPAND, 5),
                                  (self.msg_sizer, 0, wx.EXPAND | wx.ALL, 5),
-                                 (self.button_sizer, 0, wx.EXPAND | wx.ALL, 5)])
+                                 (self.button_sizer, 0, wx.ALIGN_RIGHT)])
         self.SetSizer(self.main_sizer)
         self.SetAutoLayout(True)
@@ -1237,9 +1238,8 @@
 ##import scipy?
 #class Model(Model1DPlugin):
-#    name = ""
+#    name = basename without extension of __file__
 #    def __init__(self):
 #        Model1DPlugin.__init__(self, name=self.name)
 #        #set name same as file name
-#        self.name = self.get_fname()
 #        #self.params here
 #        self.description = "%s"
@@ -1303,9 +1303,4 @@
 """
 TEST_TEMPLATE = """
-    def get_fname(self):
-        path = sys._getframe().f_code.co_filename
-        basename  = os.path.basename(path)
-        name, _ = os.path.splitext(basename)
-        return name
 ######################################################################
 ## THIS IS FOR TEST. DO NOT MODIFY THE FOLLOWING LINES!!!!!!!!!!!!!!!!
@@ -1330,28 +1325,22 @@
 import sys
 import copy
+import collections
 
 import numpy
 
 from sas.sascalc.fit.pluginmodel import Model1DPlugin
-from sasmodels.sasview_model import make_class
-from sasmodels.core import load_model_info
-# User can change the name of the model (only with single functional model)
-#P1_model:
-#from %s import Model as P1
-
-#P2_model:
-#from %s import Model as P2
+from sasmodels.sasview_model import find_model
 
 class Model(Model1DPlugin):
-    name = ""
-    def __init__(self):
+    name = os.path.splitext(os.path.basename(__file__))[0]
+    is_multiplicity_model = False
+    def __init__(self, multiplicity=1):
         Model1DPlugin.__init__(self, name='')
-        P1 = make_class('%s')
-        P2 = make_class('%s')
+        P1 = find_model('%s')
+        P2 = find_model('%s')
         p_model1 = P1()
         p_model2 = P2()
         ## Setting  model name model description
         self.description = '%s'
-        self.name = self.get_fname()
         if self.name.rstrip().lstrip() == '':
             self.name = self._get_name(p_model1.name, p_model2.name)
@@ -1362,5 +1351,5 @@
 
         ## Define parameters
-        self.params = {}
+        self.params = collections.OrderedDict()
 
         ## Parameter details [units, min, max]
@@ -1391,5 +1380,7 @@
         #list of parameter that can be fitted
         self._set_fixed_params()
+
         ## parameters with orientation
+        self.orientation_params = []
         for item in self.p_model1.orientation_params:
             new_item = "p1_" + item
@@ -1402,4 +1393,5 @@
                 self.orientation_params.append(new_item)
         ## magnetic params
+        self.magnetic_params = []
         for item in self.p_model1.magnetic_params:
             new_item = "p1_" + item
@@ -1459,4 +1451,5 @@
 
     def _set_dispersion(self):
+        self.dispersion = collections.OrderedDict()
         ##set dispersion only from p_model
         for name , value in self.p_model1.dispersion.iteritems():
@@ -1574,4 +1567,5 @@
 
     def _set_fixed_params(self):
+        self.fixed = []
         for item in self.p_model1.fixed:
             new_item = "p1" + item
@@ -1624,10 +1618,4 @@
         self.description += description
 
-    def get_fname(self):
-        path = sys._getframe().f_code.co_filename
-        basename  = os.path.basename(path)
-        name, _ = os.path.splitext(basename)
-        return name
-
 if __name__ == "__main__":
     m1= Model()
Index: src/sas/sasgui/perspectives/calculator/pyconsole.py
===================================================================
--- src/sas/sasgui/perspectives/calculator/pyconsole.py	(revision 26d6e0456318cb4a88efcb2e4a432527408e4a7d)
+++ src/sas/sasgui/perspectives/calculator/pyconsole.py	(revision 7673ecd9d5adcefdfa1fc6a6d1ff0649f8e63bc8)
@@ -27,14 +27,11 @@
     """
     # try running the model
-    from sasmodels.core import load_model, call_kernel
-    model = load_model(path)
-
+    from sasmodels.sasview_model import load_custom_model
+    Model = load_custom_model(path)
+    model = Model()
     q =  np.array([0.01, 0.1])
-    kernel = model.make_kernel([q])
-    Iq = call_kernel(kernel, {})
-
+    Iq = model.evalDistribution(q)
     qx, qy =  np.array([0.01, 0.01]), np.array([0.1, 0.1])
-    kernel = model.make_kernel([qx, qy])
-    Iqxy = call_kernel(kernel, {})
+    Iqxy = model.evalDistribution([qx, qy])
 
     result = """
@@ -56,5 +53,5 @@
     except Exception:
         import traceback
-        result, errmsg = None, traceback.format_exc(limit=2)
+        result, errmsg = None, traceback.format_exc()
 
     parts = ["Running model '%s'..." % os.path.basename(fname)]
Index: src/sas/sasgui/perspectives/fitting/basepage.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/basepage.py	(revision cb4ef58d8aabd8393aae14823d3fa98d1ba0c1a2)
+++ src/sas/sasgui/perspectives/fitting/basepage.py	(revision 6ed67dbdbcce387c7e93ee58083ecf6397042b8c)
@@ -12,6 +12,10 @@
 import json
 import logging
+import traceback
+
 from collections import defaultdict
 from wx.lib.scrolledpanel import ScrolledPanel
+
+import sasmodels.sasview_model
 from sas.sasgui.guiframe.panel_base import PanelBase
 from sas.sasgui.guiframe.utils import format_number, check_float, IdList
@@ -198,6 +202,6 @@
         self.state_change = False
         ## save customized array
-        self.values = []
-        self.weights = []
+        self.values = {}   # type: Dict[str, List[float, ...]]
+        self.weights = {}   # type: Dict[str, List[float, ...]]
         ## retrieve saved state
         self.number_saved_state = 0
@@ -852,7 +856,7 @@
                     angles.append(angle)
                     weights.append(weight)
-                except:
+                except Exception:
                     # Skip non-data lines
-                    logging.error(sys.exc_info()[1])
+                    logging.error(traceback.format_exc())
             return numpy.array(angles), numpy.array(weights)
         except:
@@ -1393,6 +1397,6 @@
                 self.model._persistency_dict[key] = \
                                  [state.values, state.weights]
-            except:
-                logging.error(sys.exc_info()[1])
+            except Exception:
+                logging.error(traceback.format_exc())
             selection = self._find_polyfunc_selection(disp_model)
             for list in self.fittable_param:
@@ -1410,6 +1414,6 @@
                             list[5].Disable()
                             list[6].Disable()
-                        except:
-                            logging.error(sys.exc_info()[1])
+                        except Exception:
+                            logging.error(traceback.format_exc())
             # For array, disable all fixed params
             if selection == 1:
@@ -1419,6 +1423,6 @@
                         try:
                             item[2].Disable()
-                        except:
-                            logging.error(sys.exc_info()[1])
+                        except Exception:
+                            logging.error(traceback.format_exc())
 
         # Make sure the check box updated when all checked
@@ -1507,6 +1511,6 @@
                 is_modified = self._check_value_enter(self.parameters,
                                                       is_modified)
-            except:
-                logging.error(sys.exc_info()[1])
+            except Exception:
+                logging.error(traceback.format_exc())
 
             # Here we should check whether the boundaries have been modified.
@@ -1651,6 +1655,6 @@
         try:
             self.save_current_state()
-        except:
-            logging.error(sys.exc_info()[1])
+        except Exception:
+            logging.error(traceback.format_exc())
 
         return flag
@@ -1895,9 +1899,5 @@
             if mod_cat == custom_model:
                 for model in self.model_list_box[mod_cat]:
-                    if 'sasmodels.sasview_model.' in str(model):
-                        str_m = model.id
-                    else:
-                        str_m = str(model).split(".")[0]
-                    #self.model_box.Append(str_m)
+                    str_m = model.id if hasattr(model, 'id') else model.name
                     m_list.append(self.model_dict[str_m])
             else:
@@ -1910,6 +1910,6 @@
                     #    wx.PostEvent(self.parent.parent,
                     #                 StatusEvent(status=msg, info="error"))
-        except:
-            msg = "%s\n" % (sys.exc_info()[1])
+        except Exception:
+            msg = traceback.format_exc()
             wx.PostEvent(self._manager.parent,
                          StatusEvent(status=msg, info="error"))
@@ -2474,6 +2474,6 @@
                 try:
                     self.model.set_dispersion(p, disp_model)
-                except:
-                    logging.error(sys.exc_info()[1])
+                except Exception:
+                    logging.error(traceback.format_exc())
 
         ## save state into
@@ -2588,5 +2588,6 @@
             self._draw_model()
             self.Refresh()
-        except:
+        except Exception:
+            logging.error(traceback.format_exc())
             # Error msg
             msg = "Error occurred:"
@@ -2680,13 +2681,14 @@
         # Try to delete values and weight of the names array dic if exists
         try:
-            del self.values[name]
-            del self.weights[name]
-            # delete all other dic
-            del self.state.values[name]
-            del self.state.weights[name]
-            del self.model._persistency_dict[name.split('.')[0]]
-            del self.state.model._persistency_dict[name.split('.')[0]]
-        except:
-            logging.error(sys.exc_info()[1])
+            if name in self.values:
+                del self.values[name]
+                del self.weights[name]
+                # delete all other dic
+                del self.state.values[name]
+                del self.state.weights[name]
+                del self.model._persistency_dict[name.split('.')[0]]
+                del self.state.model._persistency_dict[name.split('.')[0]]
+        except Exception:
+            logging.error(traceback.format_exc())
 
     def _lay_out(self):
@@ -2832,7 +2834,7 @@
                         graphs.append(item2.figure)
                         canvases.append(item2.canvas)
-            except:
+            except Exception:
                 # Not for control panels
-                logging.error(sys.exc_info()[1])
+                logging.error(traceback.format_exc())
         # Make sure the resduals plot goes to the last
         if res_item != None:
@@ -3167,6 +3169,6 @@
                 if item[7].__class__.__name__ == 'ComboBox':
                     disfunc = str(item[7].GetValue())
-            except:
-                logging.error(sys.exc_info()[1])
+            except Exception:
+                logging.error(traceback.format_exc())
 
             # 2D
@@ -3174,5 +3176,5 @@
                 try:
                     check = item[0].GetValue()
-                except:
+                except Exception:
                     check = None
                 name = item[1]
@@ -3202,6 +3204,6 @@
                     for weight in self.weights[name]:
                         disfunc += ' ' + str(weight)
-            except:
-                logging.error(sys.exc_info()[1])
+            except Exception:
+                logging.error(traceback.format_exc())
             content += name + ',' + str(check) + ',' + value + disfunc + ':'
 
@@ -3403,6 +3405,6 @@
                                                        weights=pd_weights)
                             is_array = True
-                except:
-                    logging.error(sys.exc_info()[1])
+                except Exception:
+                    logging.error(traceback.format_exc())
                 if not is_array:
                     self._disp_obj_dict[name] = disp_model
@@ -3418,6 +3420,6 @@
                                              self.state.weights]
 
-            except:
-                logging.error(sys.exc_info()[1])
+            except Exception:
+                logging.error(traceback.format_exc())
                 print "Error in BasePage._paste_poly_help: %s" % \
                                         sys.exc_info()[1]
Index: src/sas/sasgui/perspectives/fitting/fitting.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/fitting.py	(revision 934ce6493af6e904cf20f2500ef522b42192adfd)
+++ src/sas/sasgui/perspectives/fitting/fitting.py	(revision 7673ecd9d5adcefdfa1fc6a6d1ff0649f8e63bc8)
@@ -273,5 +273,6 @@
                         wx.PostEvent(self.parent, evt)
                         break
-        except:
+        except Exception:
+            import traceback; traceback.print_exc()
             msg = 'Delete Error: \nCould not delete the file; Check if in use.'
             wx.MessageBox(msg, 'Error')
@@ -579,5 +580,5 @@
                 _, theory_state = item
                 self.fit_panel.set_model_state(theory_state)
-            except:
+            except Exception:
                 msg = "Fitting: cannot deal with the theory received"
                 evt = StatusEvent(status=msg, info="error")
Index: src/sas/sasgui/perspectives/fitting/models.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/models.py	(revision cb4ef58d8aabd8393aae14823d3fa98d1ba0c1a2)
+++ src/sas/sasgui/perspectives/fitting/models.py	(revision 7673ecd9d5adcefdfa1fc6a6d1ff0649f8e63bc8)
@@ -2,5 +2,5 @@
     Utilities to manage models
 """
-import imp
+import traceback
 import os
 import sys
@@ -8,4 +8,5 @@
 # Time is needed by the log method
 import time
+import datetime
 import logging
 import py_compile
@@ -20,4 +21,6 @@
 
 PLUGIN_DIR = 'plugin_models'
+PLUGIN_LOG = os.path.join(os.path.expanduser("~"), '.sasview', PLUGIN_DIR,
+                          "plugins.log")
 
 def get_model_python_path():
@@ -28,11 +31,12 @@
 
 
-def log(message):
+def plugin_log(message):
     """
     Log a message in a file located in the user's home directory
     """
-    dir = os.path.join(os.path.expanduser("~"), '.sasview', PLUGIN_DIR)
-    out = open(os.path.join(dir, "plugins.log"), 'a')
-    out.write("%10g:  %s\n" % (time.clock(), message))
+    out = open(PLUGIN_LOG, 'a')
+    now = time.time()
+    stamp = datetime.datetime.fromtimestamp(now).strftime('%Y-%m-%d %H:%M:%S')
+    out.write("%s: %s\n" % (stamp, message))
     out.close()
 
@@ -51,9 +55,9 @@
     if not issubclass(model, Model1DPlugin):
         msg = "Plugin %s must be of type Model1DPlugin \n" % str(name)
-        log(msg)
+        plugin_log(msg)
         return None
     if model.__name__ != "Model":
         msg = "Plugin %s class name must be Model \n" % str(name)
-        log(msg)
+        plugin_log(msg)
         return None
     try:
@@ -63,5 +67,5 @@
                                                              str(sys.exc_type),
                                                              sys.exc_info()[1])
-        log(msg)
+        plugin_log(msg)
         return None
 
@@ -72,9 +76,9 @@
             msg = "Plugin %s: error writing function \n\t :%s %s\n " % \
                     (str(name), str(sys.exc_type), sys.exc_info()[1])
-            log(msg)
+            plugin_log(msg)
             return None
     else:
         msg = "Plugin  %s needs a method called function \n" % str(name)
-        log(msg)
+        plugin_log(msg)
         return None
     return model
@@ -132,8 +136,8 @@
     """
     def __nonzero__(self):
-        type, value, traceback = sys.exc_info()
+        type, value, tb = sys.exc_info()
         if type is not None and issubclass(type, py_compile.PyCompileError):
             print "Problem with", repr(value)
-            raise type, value, traceback
+            raise type, value, tb
         return 1
 
@@ -155,57 +159,33 @@
 
 def _findModels(dir):
+    """
+    Find custom models
+    """
     # List of plugin objects
-    plugins = {}
     dir = find_plugins_dir()
     # Go through files in plug-in directory
-    #always recompile the folder plugin
     if not os.path.isdir(dir):
-        msg = "SasView couldn't locate Model plugin folder."
-        msg += """ "%s" does not exist""" % dir
+        msg = "SasView couldn't locate Model plugin folder %r." % dir
         logging.warning(msg)
-        return plugins
-    else:
-        log("looking for models in: %s" % str(dir))
-        compile_file(dir)
-        logging.info("plugin model dir: %s" % str(dir))
-    try:
-        list = os.listdir(dir)
-        for item in list:
-            toks = os.path.splitext(os.path.basename(item))
-            if toks[1] == '.py' and not toks[0] == '__init__':
-                name = toks[0]
-                path = [os.path.abspath(dir)]
-                file = None
-                try:
-                    (file, path, info) = imp.find_module(name, path)
-                    module = imp.load_module(name, file, item, info)
-                    if hasattr(module, "Model"):
-                        try:
-                            if _check_plugin(module.Model, name) != None:
-                                plugins[name] = module.Model
-                        except:
-                            msg = "Error accessing Model"
-                            msg += "in %s\n  %s %s\n" % (name,
-                                                         str(sys.exc_type),
-                                                         sys.exc_info()[1])
-                            log(msg)
-                    else:
-                        filename = os.path.join(dir, item)
-                        plugins[name] = load_custom_model(filename)
-
-                except:
-                    msg = "Error accessing Model"
-                    msg += " in %s\n  %s %s \n" % (name,
-                                                   str(sys.exc_type),
-                                                   sys.exc_info()[1])
-                    log(msg)
-                finally:
-
-                    if not file == None:
-                        file.close()
-    except:
-        # Don't deal with bad plug-in imports. Just skip.
-        msg = "Could not import model plugin: %s" % sys.exc_info()[1]
-        log(msg)
+        return {}
+
+    plugin_log("looking for models in: %s" % str(dir))
+    #compile_file(dir)  #always recompile the folder plugin
+    logging.info("plugin model dir: %s" % str(dir))
+
+    plugins = {}
+    for filename in os.listdir(dir):
+        name, ext = os.path.splitext(filename)
+        if ext == '.py' and not name == '__init__':
+            path = os.path.abspath(os.path.join(dir, filename))
+            try:
+                model = load_custom_model(path)
+            except Exception:
+                msg = traceback.format_exc()
+                msg += "\nwhile accessing model in %r" % path
+                plugin_log(msg)
+                logging.warning("Failed to load plugin %r. See %s for details"
+                                % (path, PLUGIN_LOG))
+            plugins[model.name] = model
 
     return plugins
Index: src/sas/sasgui/perspectives/fitting/pagestate.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/pagestate.py	(revision c10d9d6c8667422d3db329ac32d0528a6cce3fc6)
+++ src/sas/sasgui/perspectives/fitting/pagestate.py	(revision 7673ecd9d5adcefdfa1fc6a6d1ff0649f8e63bc8)
@@ -19,5 +19,5 @@
 import logging
 import numpy
-import string
+import traceback
 
 import xml.dom.minidom
@@ -473,6 +473,6 @@
             try:
                 value = content[1]
-            except:
-                logging.error(sys.exc_value)
+            except Exception:
+                logging.error(traceback.format_exc())
             if name.count("State created"):
                 repo_time = "" + value
@@ -515,6 +515,6 @@
                         title = content[2] + " [" + repo_time + "]"
                         title_name = HEADER % title
-                except:
-                    logging.error(sys.exc_value)
+                except Exception:
+                    logging.error(traceback.format_exc())
             if name == "model name ":
                 try:
@@ -530,6 +530,6 @@
                     q_name = ("Q Range:    " + q_range)
                     q_range = CENTRE % q_name
-                except:
-                    logging.error(sys.exc_value)
+                except Exception:
+                    logging.error(traceback.format_exc())
         paramval = ""
         for lines in param_string.split(":"):
@@ -864,6 +864,6 @@
                             attribute = getattr(self, item[1])
                             attribute[name] = com_name
-                        except:
-                            logging.error(sys.exc_value)
+                        except Exception:
+                            logging.error(traceback.format_exc())
 
                 # get self.values and self.weights dic. if exists
@@ -880,7 +880,7 @@
                                 val = float(line)
                                 value_list.append(val)
-                            except:
+                            except Exception:
                                 # pass if line is empty (it happens)
-                                logging.error(sys.exc_value)
+                                logging.error(traceback.format_exc())
                         dic[name] = numpy.array(value_list)
                     setattr(self, item[1], dic)
@@ -1293,5 +1293,5 @@
                     if len(note_value) > 0:
                         data_info.notes.append(note_value)
-            except:
+            except Exception:
                 err_mess = "cansas_reader.read: error processing entry notes\n  %s" % sys.exc_value
                 self.errors.append(err_mess)
@@ -1315,5 +1315,5 @@
                     if len(detail_value) > 0:
                         data_info.sample.details.append(detail_value)
-            except:
+            except Exception:
                 err_mess = "cansas_reader.read: error processing sample details\n  %s" % sys.exc_value
                 self.errors.append(err_mess)
