Index: setup.py
===================================================================
--- setup.py	(revision 7fb471d81b5d11daed3df86a4e8427e6a832cf7a)
+++ setup.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -380,4 +380,10 @@
     "src", "sas", "qtgui", "Perspectives", "Fitting", "UI")
 packages.extend(["sas.qtgui.Perspectives.Fitting", "sas.qtgui.Perspectives.Fitting.UI"])
+
+package_dir["sas.qtgui.Perspectives.Inversion"] = os.path.join(
+    "src", "sas", "qtgui", "Perspectives", "Inversion")
+package_dir["sas.qtgui.Perspectives.Inversion.UI"] = os.path.join(
+    "src", "sas", "qtgui", "Perspectives", "Inversion", "UI")
+packages.extend(["sas.qtgui.Perspectives.Inversion", "sas.qtgui.Perspectives.Inversion.UI"])
 
 ## Plotting
Index: src/sas/qtgui/Perspectives/Inversion/DMaxExplorerWidget.py
===================================================================
--- src/sas/qtgui/Perspectives/Inversion/DMaxExplorerWidget.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
+++ src/sas/qtgui/Perspectives/Inversion/DMaxExplorerWidget.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -0,0 +1,163 @@
+# -*- coding: utf-8 -*-
+"""
+Dialog panel to explore the P(r) inversion results for a range
+of D_max value. User picks a number of points and a range of
+distances, then can toggle between inversion outputs and see
+their distribution as a function of D_max.
+"""
+
+# global
+import sys
+import os
+import logging
+import numpy as np
+from PyQt5 import QtCore
+from PyQt5 import QtGui
+from PyQt5 import QtWidgets
+
+from twisted.internet import threads
+
+# sas-global
+from sas.qtgui.Plotting.PlotterData import Data1D
+from sas.qtgui.Plotting.Plotter import PlotterWidget
+import sas.qtgui.Utilities.GuiUtils as GuiUtils
+
+# local
+from .UI.dmax import Ui_DmaxExplorer
+# from InvariantDetails import DetailsDialog
+# from InvariantUtils import WIDGETS
+
+logger = logging.getLogger(__name__)
+
+def enum(*sequential, **named):
+    enums = dict(list(zip(sequential, list(range(len(sequential))))), **named)
+    return type('Enum', (), enums)
+
+W = enum( 'NPTS',           #0
+          'DMIN',               #1
+          'DMAX',               #2
+          'VARIABLE',         #3
+)
+
+class DmaxWindow(QtWidgets.QDialog, Ui_DmaxExplorer):
+    # The controller which is responsible for managing signal slots connections
+    # for the gui and providing an interface to the data model.
+    name = "Dmax Explorer"  # For displaying in the combo box
+
+    def __init__(self, pr_state, nfunc, parent=None):
+        super(DmaxWindow, self).__init__()
+        self.setupUi(self)
+
+        self.setWindowTitle("Dâââ Explorer")
+
+        self.pr_state = pr_state
+        self.nfunc = nfunc
+        self.communicator = GuiUtils.Communicate()
+
+        self.plot = PlotterWidget(self, self)
+        self.hasPlot = None
+        self.verticalLayout.insertWidget(0, self.plot)
+
+        # Let's choose the Standard Item Model.
+        self.model = QtGui.QStandardItemModel(self)
+        self.mapper = None
+
+        # # Connect buttons to slots.
+        # # Needs to be done early so default values propagate properly.
+        self.setupSlots()
+
+        # Set up the model.
+        self.setupModel()
+
+        # # Set up the mapper
+        self.setupMapper()
+
+    def setupSlots(self):
+        self.closeButton.clicked.connect(self.close)
+
+        self.model.itemChanged.connect(self.modelChanged)
+
+    def setupModel(self):
+        self.model.setItem(W.NPTS, QtGui.QStandardItem(str(self.nfunc)))
+        self.model.setItem(W.DMIN,
+                           QtGui.QStandardItem(
+                               str(0.9*self.pr_state.d_max)))
+        self.model.setItem(W.DMAX,
+                           QtGui.QStandardItem(
+                               str(1.1*self.pr_state.d_max)))
+        self.model.setItem(W.VARIABLE,
+                           QtGui.QStandardItem( "ÏÂ²/dof"))
+
+    def setupMapper(self):
+        self.mapper = QtWidgets.QDataWidgetMapper(self)
+        self.mapper.setOrientation(QtCore.Qt.Vertical)
+        self.mapper.setModel(self.model)
+
+        self.mapper.addMapping(self.Npts, W.NPTS)
+        self.mapper.addMapping(self.minDist, W.DMIN)
+        self.mapper.addMapping(self.maxDist, W.DMAX)
+        self.mapper.addMapping(self.dependentVariable, W.VARIABLE)
+
+        self.mapper.toFirst()
+
+    def modelChanged(self, item):
+        if not self.mapper:
+            return
+
+        iq0 = []
+        rg = []
+        pos = []
+        pos_err = []
+        osc = []
+        bck = []
+        chi2 = []
+
+        xs = np.linspace(float(self.model.item(W.DMIN).text()),
+                         float(self.model.item(W.DMAX).text()),
+                         float(self.model.item(W.NPTS).text()))
+
+        original = self.pr_state.d_max
+        for x in xs:
+            self.pr_state.d_max = x
+            try:
+                out, cov = self.pr_state.invert(self.pr_state.nfunc)
+
+                iq0.append(self.pr_state.iq0(out))
+                rg.append(self.pr_state.rg(out))
+                pos.append(self.pr_state.get_positive(out))
+                pos_err.append(self.pr_state.get_pos_err(out, cov))
+                osc.append(self.pr_state.oscillations(out))
+                bck.append(self.pr_state.background)
+                chi2.append(self.pr_state.chi2)
+            except:
+                # This inversion failed, skip this D_max value
+                msg = "ExploreDialog: inversion failed "
+                msg += "for D_max=%s\n%s" % (str(x), sys.exc_info()[1])
+                print(msg)
+                # logger.error(msg)
+
+        #Return the invertor to its original state
+        self.pr_state.d_max = original
+        self.pr_state.invert(self.nfunc)
+
+        plotter = str(self.model.item(W.VARIABLE).text())
+        if plotter == "ÏÂ²/dof":
+            ys = chi2
+        elif plotter == "I(Q=0)":
+            ys = iq0
+        elif plotter == "Rg":
+            ys = rg
+        elif plotter == "Oscillation parameter":
+            ys = osc
+        elif plotter == "Background":
+            ys = bck
+        elif plotter == "Positive Fraction":
+            ys = pos
+        else:
+            ys = pos_err
+
+        data = Data1D(xs, ys)
+        if self.hasPlot:
+            self.plot.removePlot(None)
+        self.hasPlot = True
+        self.plot.plot(data=data, marker="-")
Index: src/sas/qtgui/Perspectives/Inversion/InversionLogic.py
===================================================================
--- src/sas/qtgui/Perspectives/Inversion/InversionLogic.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
+++ src/sas/qtgui/Perspectives/Inversion/InversionLogic.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -0,0 +1,286 @@
+import math
+import pylab
+import numpy as np
+
+from sas.qtgui.Plotting.PlotterData import Data1D
+
+PR_FIT_LABEL = r"$P_{fit}(r)$"
+PR_LOADED_LABEL = r"$P_{loaded}(r)$"
+IQ_DATA_LABEL = r"$I_{obs}(q)$"
+IQ_FIT_LABEL = r"$I_{fit}(q)$"
+IQ_SMEARED_LABEL = r"$I_{smeared}(q)$"
+GROUP_ID_IQ_DATA = r"$I_{obs}(q)$"
+GROUP_ID_PR_FIT = r"$P_{fit}(r)$"
+
+
+class InversionLogic(object):
+    """
+    All the data-related logic. This class deals exclusively with Data1D/2D
+    No QStandardModelIndex here.
+    """
+
+    # TODO: Add way to change this value
+    _pr_n_pts = 51
+
+    def __init__(self, data=None):
+        self._data = data
+        self.data_is_loaded = False
+        if data is not None:
+            self.data_is_loaded = True
+
+    @property
+    def data(self):
+        return self._data
+
+    @data.setter
+    def data(self, value):
+        """ data setter """
+        self._data = value
+        self.data_is_loaded = True
+
+    def isLoadedData(self):
+        """ accessor """
+        return self.data_is_loaded
+
+    def new1DPlot(self, out, pr, q=None):
+        """
+        Create a new 1D data instance based on fitting results
+        """
+
+        qtemp = pr.x
+        if q is not None:
+            qtemp = q
+
+        # Make a plot
+        maxq = max(qtemp)
+
+        minq = min(qtemp)
+
+        # Check for user min/max
+        if pr.q_min is not None and maxq >= pr.q_min >= minq:
+            minq = pr.q_min
+        if pr.q_max is not None and maxq >= pr.q_max >= minq:
+            maxq = pr.q_max
+
+        x = pylab.arange(minq, maxq, maxq / 301.0)
+        y = np.zeros(len(x))
+        err = np.zeros(len(x))
+        for i in range(len(x)):
+            value = pr.iq(out, x[i])
+            y[i] = value
+            try:
+                err[i] = math.sqrt(math.fabs(value))
+            except:
+                err[i] = 1.0
+                print(("Error getting error", value, x[i]))
+
+        new_plot = Data1D(x, y)
+        new_plot.name = IQ_FIT_LABEL
+        new_plot.xaxis("\\rm{Q}", 'A^{-1}')
+        new_plot.yaxis("\\rm{Intensity} ", "cm^{-1}")
+        title = "I(q)"
+        new_plot.title = title
+
+        # If we have a group ID, use it
+        if 'plot_group_id' in pr.info:
+            new_plot.group_id = pr.info["plot_group_id"]
+        new_plot.id = IQ_FIT_LABEL
+
+        # If we have used slit smearing, plot the smeared I(q) too
+        if pr.slit_width > 0 or pr.slit_height > 0:
+            x = pylab.arange(minq, maxq, maxq / 301.0)
+            y = np.zeros(len(x))
+            err = np.zeros(len(x))
+            for i in range(len(x)):
+                value = pr.iq_smeared(pr.out, x[i])
+                y[i] = value
+                try:
+                    err[i] = math.sqrt(math.fabs(value))
+                except:
+                    err[i] = 1.0
+                    print(("Error getting error", value, x[i]))
+
+            new_plot = Data1D(x, y)
+            new_plot.name = IQ_SMEARED_LABEL
+            new_plot.xaxis("\\rm{Q}", 'A^{-1}')
+            new_plot.yaxis("\\rm{Intensity} ", "cm^{-1}")
+            # If we have a group ID, use it
+            if 'plot_group_id' in pr.info:
+                new_plot.group_id = pr.info["plot_group_id"]
+            new_plot.id = IQ_SMEARED_LABEL
+            new_plot.title = title
+
+        return new_plot
+
+    def update1DPlot(self, plot, out, pr, q=None):
+        """
+        Create a new 1D data instance based on fitting results
+        """
+
+        qtemp = pr.x
+        if q is not None:
+            qtemp = q
+
+        # Make a plot
+        maxq = max(qtemp)
+
+        minq = min(qtemp)
+
+        # Check for user min/max
+        if pr.q_min is not None and maxq >= pr.q_min >= minq:
+            minq = pr.q_min
+        if pr.q_max is not None and maxq >= pr.q_max >= minq:
+            maxq = pr.q_max
+
+        x = pylab.arange(minq, maxq, maxq / 301.0)
+        y = np.zeros(len(x))
+        err = np.zeros(len(x))
+        for i in range(len(x)):
+            value = pr.iq(out, x[i])
+            y[i] = value
+            try:
+                err[i] = math.sqrt(math.fabs(value))
+            except:
+                err[i] = 1.0
+                print(("Error getting error", value, x[i]))
+
+        plot.x = x
+        plot.y = y
+
+        # If we have used slit smearing, plot the smeared I(q) too
+        if pr.slit_width > 0 or pr.slit_height > 0:
+            x = pylab.arange(minq, maxq, maxq / 301.0)
+            y = np.zeros(len(x))
+            err = np.zeros(len(x))
+            for i in range(len(x)):
+                value = pr.iq_smeared(pr.out, x[i])
+                y[i] = value
+                try:
+                    err[i] = math.sqrt(math.fabs(value))
+                except:
+                    err[i] = 1.0
+                    print(("Error getting error", value, x[i]))
+
+            plot.x = x
+            plot.y = y
+
+        return plot
+
+    def newPRPlot(self, out, pr, cov=None):
+        """
+        """
+        # Show P(r)
+        x = pylab.arange(0.0, pr.d_max, pr.d_max / self._pr_n_pts)
+
+        y = np.zeros(len(x))
+        dy = np.zeros(len(x))
+
+        total = 0.0
+        pmax = 0.0
+        cov2 = np.ascontiguousarray(cov)
+
+        for i in range(len(x)):
+            if cov2 is None:
+                value = pr.pr(out, x[i])
+            else:
+                (value, dy[i]) = pr.pr_err(out, cov2, x[i])
+            total += value * pr.d_max / len(x)
+
+            # keep track of the maximum P(r) value
+            if value > pmax:
+                pmax = value
+
+            y[i] = value
+
+        # if self._normalize_output == True:
+        #     y = y / total
+        #     dy = dy / total
+        # elif self._scale_output_unity == True:
+        #     y = y / pmax
+        #     dy = dy / pmax
+
+        if cov2 is None:
+            new_plot = Data1D(x, y)
+        else:
+            new_plot = Data1D(x, y, dy=dy)
+        new_plot.name = PR_FIT_LABEL
+        new_plot.xaxis("\\rm{r}", 'A')
+        new_plot.yaxis("\\rm{P(r)} ", "cm^{-3}")
+        new_plot.title = "P(r) fit"
+        new_plot.id = PR_FIT_LABEL
+        # Make sure that the plot is linear
+        new_plot.xtransform = "x"
+        new_plot.ytransform = "y"
+        new_plot.group_id = GROUP_ID_PR_FIT
+
+        return new_plot
+
+    def updatePRPlot(self, plot, out, pr, cov=None):
+        x = pylab.arange(0.0, pr.d_max, pr.d_max / self._pr_n_pts)
+
+        y = np.zeros(len(x))
+        dy = np.zeros(len(x))
+
+        total = 0.0
+        pmax = 0.0
+        cov2 = np.ascontiguousarray(cov)
+
+        for i in range(len(x)):
+            if cov2 is None:
+                value = pr.pr(out, x[i])
+            else:
+                (value, dy[i]) = pr.pr_err(out, cov2, x[i])
+            total += value * pr.d_max / len(x)
+
+            # keep track of the maximum P(r) value
+            if value > pmax:
+                pmax = value
+
+            y[i] = value
+
+        # if self._normalize_output == True:
+        #     y = y / total
+        #     dy = dy / total
+        # elif self._scale_output_unity == True:
+        #     y = y / pmax
+        #     dy = dy / pmax
+        plot.x = x
+        plot.y = y
+
+        if cov2 is not None:
+            plot.dy = dy
+
+        return plot
+
+    def computeDataRange(self):
+        """
+        Wrapper for calculating the data range based on local dataset
+        """
+        return self.computeRangeFromData(self.data)
+
+    def computeRangeFromData(self, data):
+        """
+        Compute the minimum and the maximum range of the data
+        return the npts contains in data
+        """
+        qmin, qmax = None, None
+        if isinstance(data, Data1D):
+            try:
+                qmin = min(data.x)
+                qmax = max(data.x)
+            except (ValueError, TypeError):
+                msg = "Unable to find min/max/length of \n data named %s" % \
+                            self.data.filename
+                raise ValueError(msg)
+
+        else:
+            qmin = 0
+            try:
+                x = max(np.fabs(data.xmin), np.fabs(data.xmax))
+                y = max(np.fabs(data.ymin), np.fabs(data.ymax))
+            except (ValueError, TypeError):
+                msg = "Unable to find min/max of \n data named %s" % \
+                            self.data.filename
+                raise ValueError(msg)
+            qmax = np.sqrt(x * x + y * y)
+        return qmin, qmax
Index: src/sas/qtgui/Perspectives/Inversion/InversionPerspective.py
===================================================================
--- src/sas/qtgui/Perspectives/Inversion/InversionPerspective.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
+++ src/sas/qtgui/Perspectives/Inversion/InversionPerspective.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -0,0 +1,603 @@
+import sys
+import logging
+import pylab
+import numpy as np
+
+from PyQt5 import QtGui, QtCore, QtWidgets
+
+# sas-global
+import sas.qtgui.Utilities.GuiUtils as GuiUtils
+
+# pr inversion GUI elements
+from .InversionUtils import WIDGETS
+from .UI.TabbedInversionUI import Ui_PrInversion
+from .InversionLogic import InversionLogic
+
+# pr inversion calculation elements
+from sas.sascalc.dataloader.data_info import Data1D
+from sas.sascalc.pr.invertor import Invertor
+
+def is_float(value):
+    """Converts text input values to floats. Empty strings throw ValueError"""
+    try:
+        return float(value)
+    except ValueError:
+        return 0.0
+
+
+# TODO: Remove data
+# TODO: Modify plot references, don't just send new
+# TODO: Update help with batch capabilities
+# TODO: Easy way to scroll through results - no tabs in window(?) - 'spreadsheet'
+# TODO: Method to export results in some meaningful way
+class InversionWindow(QtWidgets.QTabWidget, Ui_PrInversion):
+    """
+    The main window for the P(r) Inversion perspective.
+    """
+
+    name = "Inversion"
+
+    def __init__(self, parent=None, data=None):
+        super(InversionWindow, self).__init__()
+        self.setupUi(self)
+
+        self.setWindowTitle("P(r) Inversion Perspective")
+
+        self._manager = parent
+        self._model_item = QtGui.QStandardItem()
+        #self._helpView = QtWebKit.QWebView()
+
+        self.communicate = GuiUtils.Communicate()
+
+        self.logic = InversionLogic()
+
+        # The window should not close
+        self._allow_close = False
+
+        # current QStandardItem showing on the panel
+        self._data = None
+        # current Data1D as referenced by self._data
+        self._data_set = None
+
+        # p(r) calculator
+        self._calculator = Invertor()
+        self._last_calculator = None
+        self.calc_thread = None
+        self.estimation_thread = None
+
+        # Current data object in view
+        self._data_index = 0
+        # list mapping data to p(r) calculation
+        self._data_list = {}
+        if not isinstance(data, list):
+            data_list = [data]
+        if data is not None:
+            for datum in data_list:
+                self._data_list[datum] = self._calculator.clone()
+
+        # plots for current data
+        self.pr_plot = None
+        self.data_plot = None
+        # plot references for all data in perspective
+        self.pr_plot_list = {}
+        self.data_plot_list = {}
+
+        self.model = QtGui.QStandardItemModel(self)
+        self.mapper = QtWidgets.QDataWidgetMapper(self)
+        # Link user interactions with methods
+        self.setupLinks()
+        # Set values
+        self.setupModel()
+        # Set up the Widget Map
+        self.setupMapper()
+        # Set base window state
+        self.setupWindow()
+
+    ######################################################################
+    # Base Perspective Class Definitions
+
+    def communicator(self):
+        return self.communicate
+
+    def allowBatch(self):
+        return True
+
+    def setClosable(self, value=True):
+        """
+        Allow outsiders close this widget
+        """
+        assert isinstance(value, bool)
+        self._allow_close = value
+
+    def closeEvent(self, event):
+        """
+        Overwrite QDialog close method to allow for custom widget close
+        """
+        if self._allow_close:
+            # reset the closability flag
+            self.setClosable(value=False)
+            event.accept()
+        else:
+            event.ignore()
+            # Maybe we should just minimize
+            self.setWindowState(QtCore.Qt.WindowMinimized)
+
+    ######################################################################
+    # Initialization routines
+
+    def setupLinks(self):
+        """Connect the use controls to their appropriate methods"""
+        self.dataList.currentIndexChanged.connect(self.displayChange)
+        self.calculateAllButton.clicked.connect(self.startThreadAll)
+        self.calculateThisButton.clicked.connect(self.startThread)
+        self.removeButton.clicked.connect(self.removeData)
+        self.helpButton.clicked.connect(self.help)
+        self.estimateBgd.toggled.connect(self.toggleBgd)
+        self.manualBgd.toggled.connect(self.toggleBgd)
+        self.regConstantSuggestionButton.clicked.connect(self.acceptAlpha)
+        self.noOfTermsSuggestionButton.clicked.connect(self.acceptNoTerms)
+        self.explorerButton.clicked.connect(self.openExplorerWindow)
+        self.backgroundInput.textChanged.connect(
+            lambda: self._calculator.set_est_bck(int(is_float(
+                str(self.backgroundInput.text())))))
+        self.minQInput.textChanged.connect(
+            lambda: self._calculator.set_qmin(is_float(
+                str(self.minQInput.text()))))
+        self.regularizationConstantInput.textChanged.connect(
+            lambda: self._calculator.set_alpha(is_float(
+                str(self.regularizationConstantInput.text()))))
+        self.maxDistanceInput.textChanged.connect(
+            lambda: self._calculator.set_dmax(is_float(
+                str(self.maxDistanceInput.text()))))
+        self.maxQInput.textChanged.connect(
+            lambda: self._calculator.set_qmax(is_float(
+                str(self.maxQInput.text()))))
+        self.slitHeightInput.textChanged.connect(
+            lambda: self._calculator.set_slit_height(is_float(
+                str(self.slitHeightInput.text()))))
+        self.slitWidthInput.textChanged.connect(
+            lambda: self._calculator.set_slit_width(is_float(
+                str(self.slitHeightInput.text()))))
+        self.model.itemChanged.connect(self.model_changed)
+
+    def setupMapper(self):
+        # Set up the mapper.
+        self.mapper.setOrientation(QtCore.Qt.Vertical)
+        self.mapper.setModel(self.model)
+
+        # Filename
+        self.mapper.addMapping(self.dataList, WIDGETS.W_FILENAME)
+        # Background
+        self.mapper.addMapping(self.backgroundInput, WIDGETS.W_BACKGROUND_INPUT)
+        self.mapper.addMapping(self.estimateBgd, WIDGETS.W_ESTIMATE)
+        self.mapper.addMapping(self.manualBgd, WIDGETS.W_MANUAL_INPUT)
+
+        # Qmin/Qmax
+        self.mapper.addMapping(self.minQInput, WIDGETS.W_QMIN)
+        self.mapper.addMapping(self.maxQInput, WIDGETS.W_QMAX)
+
+        # Slit Parameter items
+        self.mapper.addMapping(self.slitWidthInput, WIDGETS.W_SLIT_WIDTH)
+        self.mapper.addMapping(self.slitHeightInput, WIDGETS.W_SLIT_HEIGHT)
+
+        # Parameter Items
+        self.mapper.addMapping(self.regularizationConstantInput,
+                               WIDGETS.W_REGULARIZATION)
+        self.mapper.addMapping(self.regConstantSuggestionButton,
+                               WIDGETS.W_REGULARIZATION_SUGGEST)
+        self.mapper.addMapping(self.explorerButton, WIDGETS.W_EXPLORE)
+        self.mapper.addMapping(self.maxDistanceInput, WIDGETS.W_MAX_DIST)
+        self.mapper.addMapping(self.noOfTermsInput, WIDGETS.W_NO_TERMS)
+        self.mapper.addMapping(self.noOfTermsSuggestionButton,
+                               WIDGETS.W_NO_TERMS_SUGGEST)
+
+        # Output
+        self.mapper.addMapping(self.rgValue, WIDGETS.W_RG)
+        self.mapper.addMapping(self.iQ0Value, WIDGETS.W_I_ZERO)
+        self.mapper.addMapping(self.backgroundValue, WIDGETS.W_BACKGROUND_OUTPUT)
+        self.mapper.addMapping(self.computationTimeValue, WIDGETS.W_COMP_TIME)
+        self.mapper.addMapping(self.chiDofValue, WIDGETS.W_CHI_SQUARED)
+        self.mapper.addMapping(self.oscillationValue, WIDGETS.W_OSCILLATION)
+        self.mapper.addMapping(self.posFractionValue, WIDGETS.W_POS_FRACTION)
+        self.mapper.addMapping(self.sigmaPosFractionValue,
+                               WIDGETS.W_SIGMA_POS_FRACTION)
+
+        # Main Buttons
+        self.mapper.addMapping(self.removeButton, WIDGETS.W_REMOVE)
+        self.mapper.addMapping(self.calculateAllButton, WIDGETS.W_CALCULATE_ALL)
+        self.mapper.addMapping(self.calculateThisButton,
+                               WIDGETS.W_CALCULATE_VISIBLE)
+        self.mapper.addMapping(self.helpButton, WIDGETS.W_HELP)
+
+        self.mapper.toFirst()
+
+    def setupModel(self):
+        """
+        Update boxes with initial values
+        """
+        item = QtGui.QStandardItem("")
+        self.model.setItem(WIDGETS.W_FILENAME, item)
+        item = QtGui.QStandardItem('0.0')
+        self.model.setItem(WIDGETS.W_BACKGROUND_INPUT, item)
+        item = QtGui.QStandardItem("")
+        self.model.setItem(WIDGETS.W_QMIN, item)
+        item = QtGui.QStandardItem("")
+        self.model.setItem(WIDGETS.W_QMAX, item)
+        item = QtGui.QStandardItem("")
+        self.model.setItem(WIDGETS.W_SLIT_WIDTH, item)
+        item = QtGui.QStandardItem("")
+        self.model.setItem(WIDGETS.W_SLIT_HEIGHT, item)
+        item = QtGui.QStandardItem("10")
+        self.model.setItem(WIDGETS.W_NO_TERMS, item)
+        item = QtGui.QStandardItem("0.0001")
+        self.model.setItem(WIDGETS.W_REGULARIZATION, item)
+        item = QtGui.QStandardItem("140.0")
+        self.model.setItem(WIDGETS.W_MAX_DIST, item)
+        item = QtGui.QStandardItem("")
+        self.model.setItem(WIDGETS.W_RG, item)
+        item = QtGui.QStandardItem("")
+        self.model.setItem(WIDGETS.W_I_ZERO, item)
+        item = QtGui.QStandardItem("")
+        self.model.setItem(WIDGETS.W_BACKGROUND_OUTPUT, item)
+        item = QtGui.QStandardItem("")
+        self.model.setItem(WIDGETS.W_COMP_TIME, item)
+        item = QtGui.QStandardItem("")
+        self.model.setItem(WIDGETS.W_CHI_SQUARED, item)
+        item = QtGui.QStandardItem("")
+        self.model.setItem(WIDGETS.W_OSCILLATION, item)
+        item = QtGui.QStandardItem("")
+        self.model.setItem(WIDGETS.W_POS_FRACTION, item)
+        item = QtGui.QStandardItem("")
+        self.model.setItem(WIDGETS.W_SIGMA_POS_FRACTION, item)
+
+    def setupWindow(self):
+        """Initialize base window state on init"""
+        self.setTabPosition(0)
+        self.enableButtons()
+        self.estimateBgd.setChecked(True)
+
+    ######################################################################
+    # Methods for updating GUI
+
+    def enableButtons(self):
+        """
+        Enable buttons when data is present, else disable them
+        """
+        self.removeButton.setEnabled(self.logic.data_is_loaded)
+        self.explorerButton.setEnabled(self.logic.data_is_loaded)
+        self.calculateAllButton.setEnabled(self.logic.data_is_loaded)
+        self.calculateThisButton.setEnabled(self.logic.data_is_loaded)
+
+    def populateDataComboBox(self, filename, data_ref):
+        """
+        Append a new file name to the data combobox
+        :param filename: data filename
+        :param data_ref: QStandardItem reference for data set to be added
+        """
+        qt_item = QtCore.QString.fromUtf8(filename)
+        ref = QtCore.QVariant(data_ref)
+        self.dataList.addItem(qt_item, ref)
+
+    def acceptNoTerms(self):
+        """Send estimated no of terms to input"""
+        self.model.setItem(WIDGETS.W_NO_TERMS, QtGui.QStandardItem(
+            self.noOfTermsSuggestionButton.text()))
+
+    def acceptAlpha(self):
+        """Send estimated alpha to input"""
+        self.model.setItem(WIDGETS.W_REGULARIZATION, QtGui.QStandardItem(
+            self.regConstantSuggestionButton.text()))
+
+    def displayChange(self):
+        variant_ref = self.dataList.itemData(self.dataList.currentIndex())
+        self.setCurrentData(variant_ref.toPyObject())
+
+    def removeData(self):
+        """Remove the existing data reference from the P(r) Persepective"""
+        self._data_list.pop(self._data)
+        self.pr_plot_list.pop(self._data)
+        self.data_plot_list.pop(self._data)
+        self.dataList.removeItem(self.dataList.currentIndex())
+        self.dataList.setCurrentIndex(0)
+
+    ######################################################################
+    # GUI Interaction Events
+
+    def update_calculator(self):
+        """Update all p(r) params"""
+        self._calculator.set_x(self._data_set.x)
+        self._calculator.set_y(self._data_set.y)
+        self._calculator.set_err(self._data_set.dy)
+
+    def model_changed(self):
+        """Update the values when user makes changes"""
+        if not self.mapper:
+            msg = "Unable to update P{r}. The connection between the main GUI "
+            msg += "and P(r) was severed. Attempting to restart P(r)."
+            logging.warning(msg)
+            self.setClosable(True)
+            self.close()
+            InversionWindow.__init__(self.parent(), list(self._data_list.keys()))
+            exit(0)
+        # TODO: Only send plot first time - otherwise, update in complete
+        if self.pr_plot is not None:
+            title = self.pr_plot.name
+            GuiUtils.updateModelItemWithPlot(
+                self._data, QtCore.QVariant(self.pr_plot), title)
+        if self.data_plot is not None:
+            title = self.data_plot.name
+            GuiUtils.updateModelItemWithPlot(
+                self._data, QtCore.QVariant(self.data_plot), title)
+        self.mapper.toFirst()
+
+    def help(self):
+        """
+        Open the P(r) Inversion help browser
+        """
+        tree_location = (GuiUtils.HELP_DIRECTORY_LOCATION +
+                         "user/sasgui/perspectives/pr/pr_help.html")
+
+        # Actual file anchor will depend on the combo box index
+        # Note that we can be clusmy here, since bad current_fitter_id
+        # will just make the page displayed from the top
+        #self._helpView.load(QtCore.QUrl(tree_location))
+        #self._helpView.show()
+
+    def toggleBgd(self):
+        """
+        Toggle the background between manual and estimated
+        """
+        sender = self.sender()
+        if sender is self.estimateBgd:
+            self.backgroundInput.setEnabled(False)
+        else:
+            self.backgroundInput.setEnabled(True)
+
+    def openExplorerWindow(self):
+        """
+        Open the Explorer window to see correlations between params and results
+        """
+        # TODO: Link Invertor() and DmaxWindow so window updates when recalculated
+        from .DMaxExplorerWidget import DmaxWindow
+        self.dmaxWindow = DmaxWindow(self._calculator, self.getNFunc(), self)
+        self.dmaxWindow.show()
+
+    ######################################################################
+    # Response Actions
+
+    def setData(self, data_item=None, is_batch=False):
+        """
+        Assign new data set(s) to the P(r) perspective
+        Obtain a QStandardItem object and parse it to get Data1D/2D
+        Pass it over to the calculator
+        """
+        assert data_item is not None
+
+        if not isinstance(data_item, list):
+            msg = "Incorrect type passed to the P(r) Perspective"
+            raise AttributeError
+
+        for data in data_item:
+            # Create initial internal mappings
+            self._data_list[data] = self._calculator.clone()
+            self._data_set = GuiUtils.dataFromItem(data)
+            self.data_plot_list[data] = self.data_plot
+            self.pr_plot_list[data] = self.pr_plot
+            ref_var = QtCore.QVariant(data)
+            self.populateDataComboBox(self._data_set.filename, ref_var)
+            self.setCurrentData(data)
+
+            # Estimate initial values from data
+            self.performEstimate()
+            self.logic = InversionLogic(self._data_set)
+
+            # Estimate q range
+            qmin, qmax = self.logic.computeDataRange()
+            self.model.setItem(WIDGETS.W_QMIN, QtGui.QStandardItem(
+                "{:.4g}".format(qmin)))
+            self.model.setItem(WIDGETS.W_QMAX, QtGui.QStandardItem(
+                "{:.4g}".format(qmax)))
+
+        self.enableButtons()
+
+    def getNFunc(self):
+        """Get the n_func value from the GUI object"""
+        return int(UI.TabbedInversionUI._fromUtf8(self.noOfTermsInput.text()))
+
+    def setCurrentData(self, data_ref):
+        """Get the current data and display as necessary"""
+
+        if not isinstance(data_ref, QtGui.QStandardItem):
+            msg = "Incorrect type passed to the P(r) Perspective"
+            raise AttributeError
+
+        # Data references
+        self._data = data_ref
+        self._data_set = GuiUtils.dataFromItem(data_ref)
+        self._calculator = self._data_list[data_ref]
+        self.pr_plot = self.pr_plot_list[data_ref]
+        self.data_plot = self.data_plot_list[data_ref]
+
+    ######################################################################
+    # Thread Creators
+
+    # TODO: Move to individual class(?)
+
+    def startThreadAll(self):
+        for data_ref, pr in list(self._data_list.items()):
+            self._data_set = GuiUtils.dataFromItem(data_ref)
+            self._calculator = pr
+            self.startThread()
+
+    def startThread(self):
+        """
+            Start a calculation thread
+        """
+        from .Thread import CalcPr
+
+        # Set data before running the calculations
+        self.update_calculator()
+
+        # If a thread is already started, stop it
+        if self.calc_thread is not None and self.calc_thread.isrunning():
+            self.calc_thread.stop()
+        pr = self._calculator.clone()
+        nfunc = self.getNFunc()
+        self.calc_thread = CalcPr(pr, nfunc,
+                                  error_func=self._threadError,
+                                  completefn=self._completed, updatefn=None)
+        self.calc_thread.queue()
+        self.calc_thread.ready(2.5)
+
+    def performEstimateNT(self):
+        """
+            Perform parameter estimation
+        """
+        from .Thread import EstimateNT
+
+        # If a thread is already started, stop it
+        if (self.estimation_thread is not None and
+                self.estimation_thread.isrunning()):
+            self.estimation_thread.stop()
+        pr = self._calculator.clone()
+        # Skip the slit settings for the estimation
+        # It slows down the application and it doesn't change the estimates
+        pr.slit_height = 0.0
+        pr.slit_width = 0.0
+        nfunc = self.getNFunc()
+        self.estimation_thread = EstimateNT(pr, nfunc,
+                                            error_func=self._threadError,
+                                            completefn=self._estimateNTCompleted,
+                                            updatefn=None)
+        self.estimation_thread.queue()
+        self.estimation_thread.ready(2.5)
+
+    def performEstimate(self):
+        """
+            Perform parameter estimation
+        """
+        from .Thread import EstimatePr
+
+        self.startThread()
+
+        # If a thread is already started, stop it
+        if (self.estimation_thread is not None and
+                self.estimation_thread.isrunning()):
+            self.estimation_thread.stop()
+        pr = self._calculator.clone()
+        nfunc = self.getNFunc()
+        self.estimation_thread = EstimatePr(pr, nfunc,
+                                            error_func=self._threadError,
+                                            completefn=self._estimateCompleted,
+                                            updatefn=None)
+        self.estimation_thread.queue()
+        self.estimation_thread.ready(2.5)
+
+    ######################################################################
+    # Thread Complete
+
+    def _estimateCompleted(self, alpha, message, elapsed):
+        """
+        Parameter estimation completed,
+        display the results to the user
+
+        :param alpha: estimated best alpha
+        :param elapsed: computation time
+        """
+        # Save useful info
+        self.model.setItem(WIDGETS.W_COMP_TIME,
+                           QtGui.QStandardItem(str(elapsed)))
+        self.regConstantSuggestionButton.setText(QtCore.QString(str(alpha)))
+        self.regConstantSuggestionButton.setEnabled(True)
+        if message:
+            logging.info(message)
+        self.performEstimateNT()
+
+    def _estimateNTCompleted(self, nterms, alpha, message, elapsed):
+        """
+        Parameter estimation completed,
+        display the results to the user
+
+        :param alpha: estimated best alpha
+        :param nterms: estimated number of terms
+        :param elapsed: computation time
+
+        """
+        # Save useful info
+        self.noOfTermsSuggestionButton.setText(QtCore.QString(
+            "{:n}".format(nterms)))
+        self.noOfTermsSuggestionButton.setEnabled(True)
+        self.regConstantSuggestionButton.setText(QtCore.QString(
+            "{:.3g}".format(alpha)))
+        self.regConstantSuggestionButton.setEnabled(True)
+        self.model.setItem(WIDGETS.W_COMP_TIME,
+                           QtGui.QStandardItem(str(elapsed)))
+        self.PrTabWidget.setCurrentIndex(0)
+        if message:
+            logging.info(message)
+
+    def _completed(self, out, cov, pr, elapsed):
+        """
+        Method called with the results when the inversion is done
+
+        :param out: output coefficient for the base functions
+        :param cov: covariance matrix
+        :param pr: Invertor instance
+        :param elapsed: time spent computing
+
+        """
+        # Save useful info
+        cov = np.ascontiguousarray(cov)
+        pr.cov = cov
+        pr.out = out
+        pr.elapsed = elapsed
+
+        # Show result on control panel
+
+        # TODO: Connect self._calculator to GUI - two-to-one connection possible?
+        self.model.setItem(WIDGETS.W_RG, QtGui.QStandardItem(str(pr.rg(out))))
+        self.model.setItem(WIDGETS.W_I_ZERO,
+                           QtGui.QStandardItem(str(pr.iq0(out))))
+        self.model.setItem(WIDGETS.W_BACKGROUND_INPUT,
+                           QtGui.QStandardItem("{:.3f}".format(pr.est_bck)))
+        self.model.setItem(WIDGETS.W_BACKGROUND_OUTPUT,
+                           QtGui.QStandardItem(str(pr.background)))
+        self.model.setItem(WIDGETS.W_CHI_SQUARED,
+                           QtGui.QStandardItem(str(pr.chi2[0])))
+        self.model.setItem(WIDGETS.W_COMP_TIME,
+                           QtGui.QStandardItem(str(elapsed)))
+        self.model.setItem(WIDGETS.W_OSCILLATION,
+                           QtGui.QStandardItem(str(pr.oscillations(out))))
+        self.model.setItem(WIDGETS.W_POS_FRACTION,
+                           QtGui.QStandardItem(str(pr.get_positive(out))))
+        self.model.setItem(WIDGETS.W_SIGMA_POS_FRACTION,
+                           QtGui.QStandardItem(str(pr.get_pos_err(out, cov))))
+
+        # Display results tab
+        self.PrTabWidget.setCurrentIndex(1)
+        # Save Pr invertor
+        self._calculator = pr
+        # Append data to data list
+        self._data_list[self._data] = self._calculator.clone()
+
+        # Create new P(r) and fit plots
+        if self.pr_plot is None:
+            self.pr_plot = self.logic.newPRPlot(out, self._calculator, cov)
+            self.pr_plot_list[self._data] = self.pr_plot
+        else:
+            # FIXME: this should update the existing plot, not create a new one
+            self.pr_plot = self.logic.newPRPlot(out, self._calculator, cov)
+            self.pr_plot_list[self._data] = self.pr_plot
+        if self.data_plot is None:
+            self.data_plot = self.logic.new1DPlot(out, self._calculator)
+            self.data_plot_list[self._data] = self.data_plot
+        else:
+            # FIXME: this should update the existing plot, not create a new one
+            self.data_plot = self.logic.new1DPlot(out, self._calculator)
+            self.data_plot_list[self._data] = self.data_plot
+
+    def _threadError(self, error):
+        """
+            Call-back method for calculation errors
+        """
+        logging.warning(error)
Index: src/sas/qtgui/Perspectives/Inversion/InversionUtils.py
===================================================================
--- src/sas/qtgui/Perspectives/Inversion/InversionUtils.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
+++ src/sas/qtgui/Perspectives/Inversion/InversionUtils.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -0,0 +1,33 @@
+def enum(*sequential, **named):
+    enums = dict(list(zip(sequential, list(range(len(sequential))))), **named)
+    return type('Enum', (), enums)
+
+WIDGETS = enum( 'W_FILENAME',               #0
+                'W_BACKGROUND_INPUT',             #1
+                'W_ESTIMATE',               #2
+                'W_MANUAL_INPUT',           #3
+                'W_QMIN',                   #4
+                'W_QMAX',                   #5
+                'W_SLIT_HEIGHT',            #6
+                'W_SLIT_WIDTH',             #7
+                'W_NO_TERMS',               #8
+                'W_NO_TERMS_SUGGEST',       #9
+                'W_REGULARIZATION',         #10
+                'W_REGULARIZATION_SUGGEST', #11
+                'W_MAX_DIST',               #12
+                'W_EXPLORE',                #13
+                # results
+                'W_RG',
+                'W_I_ZERO',
+                'W_BACKGROUND_OUTPUT',
+                'W_COMP_TIME',
+                'W_CHI_SQUARED',
+                'W_OSCILLATION',
+                'W_POS_FRACTION',
+                'W_SIGMA_POS_FRACTION',
+                # bottom buttons
+                'W_REMOVE',
+                'W_CALCULATE_ALL',
+                'W_CALCULATE_VISIBLE',
+                'W_HELP'
+)
Index: src/sas/qtgui/Perspectives/Inversion/Thread.py
===================================================================
--- src/sas/qtgui/Perspectives/Inversion/Thread.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
+++ src/sas/qtgui/Perspectives/Inversion/Thread.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -0,0 +1,104 @@
+import sys
+import time
+from sas.sascalc.data_util.calcthread import CalcThread
+
+
+class CalcPr(CalcThread):
+    """
+    Compute P(r)
+    """
+
+    def __init__(self, pr, nfunc=5, error_func=None, completefn=None,
+                 updatefn=None, yieldtime=0.01, worktime=0.01):
+        """
+        """
+        CalcThread.__init__(self, completefn, updatefn, yieldtime, worktime)
+        self.pr = pr
+        self.nfunc = nfunc
+        self.error_func = error_func
+        self.starttime = 0
+
+    def compute(self):
+        """
+        Perform P(r) inversion
+        """
+        try:
+            self.starttime = time.time()
+            out, cov = self.pr.invert(self.nfunc)
+            elapsed = time.time() - self.starttime
+            self.complete(out=out, cov=cov, pr=self.pr, elapsed=elapsed)
+        except KeyboardInterrupt:
+            # Thread was interrupted, just proceed
+            pass
+        except:
+            if self.error_func is not None:
+                self.error_func("CalcPr.compute: %s" % sys.exc_info()[1])
+
+
+class EstimatePr(CalcThread):
+    """
+    Estimate P(r)
+    """
+
+    def __init__(self, pr, nfunc=5, error_func=None, completefn=None,
+                 updatefn=None, yieldtime=0.01, worktime=0.01):
+        CalcThread.__init__(self, completefn, updatefn, yieldtime, worktime)
+        self.pr = pr
+        self.nfunc = nfunc
+        self.error_func = error_func
+        self.starttime = 0
+
+    def compute(self):
+        """
+        Calculates the estimate
+        """
+        try:
+            alpha, message, elapsed = self.pr.estimate_alpha(self.nfunc)
+            self.isquit()
+            self.complete(alpha=alpha, message=message, elapsed=elapsed)
+        except KeyboardInterrupt:
+            # Thread was interrupted, just proceed
+            pass
+        except:
+            if self.error_func is not None:
+                self.error_func("EstimatePr.compute: %s" % sys.exc_info()[1])
+
+
+class EstimateNT(CalcThread):
+    """
+    """
+    def __init__(self, pr, nfunc=5, error_func=None, completefn=None,
+                 updatefn=None, yieldtime=0.01, worktime=0.01):
+        CalcThread.__init__(self, completefn, updatefn, yieldtime, worktime)
+        self.pr = pr
+        self.nfunc = nfunc
+        self.error_func = error_func
+        self.starttime = 0
+        self._time_for_sleep = 0
+        self._sleep_delay = 1.0
+
+    def isquit(self):
+        """
+        """
+        CalcThread.isquit(self)
+        if time.time() > self._time_for_sleep + self._sleep_delay:
+            time.sleep(.2)
+            self._time_for_sleep = time.time()
+
+    def compute(self):
+        """
+        Calculates the estimate
+        """
+        try:
+            t_0 = time.time()
+            self._time_for_sleep = t_0
+            nterms, alpha, message = self.pr.estimate_numterms(self.isquit)
+            t_1 = time.time() - t_0
+            self.isquit()
+            self.complete(nterms=nterms, alpha=alpha, message=message, elapsed=t_1)
+        except KeyboardInterrupt:
+            # Thread was interrupted, just proceed
+            pass
+        except:
+            if self.error_func is not None:
+                self.error_func("EstimatePr2.compute: %s" % sys.exc_info()[1])
Index: src/sas/qtgui/Perspectives/Inversion/UI/DMaxExplorer.ui
===================================================================
--- src/sas/qtgui/Perspectives/Inversion/UI/DMaxExplorer.ui	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
+++ src/sas/qtgui/Perspectives/Inversion/UI/DMaxExplorer.ui	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>DmaxExplorer</class>
+ <widget class="QDialog" name="DmaxExplorer">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>609</width>
+    <height>524</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Dialog</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout_2">
+   <item row="0" column="0">
+    <layout class="QVBoxLayout" name="verticalLayout">
+     <item>
+      <spacer name="verticalSpacer">
+       <property name="orientation">
+        <enum>Qt::Vertical</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>40</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <layout class="QGridLayout" name="gridLayout">
+       <item row="1" column="2">
+        <widget class="QLabel" name="label_4">
+         <property name="text">
+          <string>Max Distance [â«]</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="3">
+        <widget class="QLineEdit" name="maxDist"/>
+       </item>
+       <item row="0" column="2">
+        <widget class="QLabel" name="label_2">
+         <property name="text">
+          <string>Npts</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="3">
+        <widget class="QLineEdit" name="Npts"/>
+       </item>
+       <item row="0" column="0">
+        <widget class="QLabel" name="label">
+         <property name="text">
+          <string>Dependent Variable:</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="1">
+        <widget class="QLineEdit" name="minDist"/>
+       </item>
+       <item row="0" column="1">
+        <widget class="QComboBox" name="dependentVariable">
+         <item>
+          <property name="text">
+           <string>1-Ï positive fraction</string>
+          </property>
+         </item>
+         <item>
+          <property name="text">
+           <string>ÏÂ²/dof</string>
+          </property>
+         </item>
+         <item>
+          <property name="text">
+           <string>I(Q=0)</string>
+          </property>
+         </item>
+         <item>
+          <property name="text">
+           <string>Rg</string>
+          </property>
+         </item>
+         <item>
+          <property name="text">
+           <string>Oscillation parameter</string>
+          </property>
+         </item>
+         <item>
+          <property name="text">
+           <string>Background</string>
+          </property>
+         </item>
+         <item>
+          <property name="text">
+           <string>Positive Fraction</string>
+          </property>
+         </item>
+        </widget>
+       </item>
+       <item row="1" column="0">
+        <widget class="QLabel" name="label_3">
+         <property name="text">
+          <string>Min Distance [â«]</string>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="0" colspan="4">
+        <widget class="QPushButton" name="closeButton">
+         <property name="text">
+          <string>Close</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
Index: src/sas/qtgui/Perspectives/Inversion/UI/TabbedInversionUI.ui
===================================================================
--- src/sas/qtgui/Perspectives/Inversion/UI/TabbedInversionUI.ui	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
+++ src/sas/qtgui/Perspectives/Inversion/UI/TabbedInversionUI.ui	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -0,0 +1,589 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PrInversion</class>
+ <widget class="QDialog" name="PrInversion">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>458</width>
+    <height>679</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>458</width>
+    <height>679</height>
+   </size>
+  </property>
+  <property name="maximumSize">
+   <size>
+    <width>16777215</width>
+    <height>16777215</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>P(r) Inversion</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout_7">
+   <item row="4" column="0">
+    <layout class="QHBoxLayout" name="horizontalLayout_8">
+     <property name="sizeConstraint">
+      <enum>QLayout::SetDefaultConstraint</enum>
+     </property>
+     <item>
+      <widget class="QPushButton" name="removeButton">
+       <property name="text">
+        <string>Remove</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="calculateThisButton">
+       <property name="text">
+        <string>Calculate</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="calculateAllButton">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string>Calculate All</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeType">
+        <enum>QSizePolicy::Expanding</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="helpButton">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string>Help</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item row="3" column="0">
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>2</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item row="1" column="0" rowspan="2">
+    <widget class="QTabWidget" name="PrTabWidget">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>0</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="currentIndex">
+      <number>0</number>
+     </property>
+     <widget class="QWidget" name="tabMain">
+      <attribute name="title">
+       <string>P(r) Parameters</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_6" stretch="0,1,1,0,0">
+       <item>
+        <widget class="QGroupBox" name="dataSourceGroupBox">
+         <property name="title">
+          <string>I(q) data source</string>
+         </property>
+         <layout class="QGridLayout" name="gridLayout_2">
+          <item row="1" column="0">
+           <layout class="QHBoxLayout" name="horizontalLayout_3" stretch="2,1,0,2,2">
+            <item>
+             <widget class="QLabel" name="label_5">
+              <property name="text">
+               <string>Background Level:</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QLineEdit" name="backgroundInput">
+              <property name="enabled">
+               <bool>false</bool>
+              </property>
+              <property name="text">
+               <string>0.0</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QLabel" name="label_10">
+              <property name="text">
+               <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Ã
+&lt;span style=&quot; vertical-align:super;&quot;&gt;-1&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QRadioButton" name="estimateBgd">
+              <property name="text">
+               <string>Estimate</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QRadioButton" name="manualBgd">
+              <property name="text">
+               <string>Manual Input</string>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </item>
+          <item row="0" column="0">
+           <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,3">
+            <item>
+             <widget class="QLabel" name="label">
+              <property name="text">
+               <string>Data File Name:</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QComboBox" name="dataList"/>
+            </item>
+           </layout>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item>
+        <widget class="QGroupBox" name="qRangeGroupBox">
+         <property name="title">
+          <string>Total Q range</string>
+         </property>
+         <layout class="QGridLayout" name="gridLayout_3">
+          <item row="0" column="0">
+           <layout class="QHBoxLayout" name="horizontalLayout_4">
+            <item>
+             <widget class="QLabel" name="label_7">
+              <property name="text">
+               <string>Min:</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QLineEdit" name="minQInput">
+              <property name="enabled">
+               <bool>true</bool>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QLabel" name="label_11">
+              <property name="text">
+               <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Ã
+&lt;span style=&quot; vertical-align:super;&quot;&gt;-1&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QLabel" name="label_8">
+              <property name="text">
+               <string>Max:</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QLineEdit" name="maxQInput">
+              <property name="enabled">
+               <bool>true</bool>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QLabel" name="label_9">
+              <property name="text">
+               <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Ã
+&lt;span style=&quot; vertical-align:super;&quot;&gt;-1&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item>
+        <widget class="QGroupBox" name="slitParamsGroupBox">
+         <property name="title">
+          <string>Slit Parameters</string>
+         </property>
+         <layout class="QGridLayout" name="gridLayout">
+          <item row="0" column="0">
+           <layout class="QHBoxLayout" name="horizontalLayout">
+            <item>
+             <widget class="QLabel" name="label_3">
+              <property name="text">
+               <string>Height</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QLineEdit" name="slitHeightInput">
+              <property name="enabled">
+               <bool>true</bool>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QLabel" name="label_6">
+              <property name="text">
+               <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Ã
+&lt;span style=&quot; vertical-align:super;&quot;&gt;-1&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QLabel" name="label_4">
+              <property name="text">
+               <string>Width</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QLineEdit" name="slitWidthInput">
+              <property name="enabled">
+               <bool>true</bool>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QLabel" name="label_2">
+              <property name="text">
+               <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Ã
+&lt;span style=&quot; vertical-align:super;&quot;&gt;-1&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item>
+        <widget class="QGroupBox" name="paramGroupBox">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="toolTip">
+          <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;pre style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&gt;&lt;span style=&quot; font-family:'Courier New'; font-size:9pt; color:#000000;&quot;&gt;P(r) is found by fitting a set of base functions to I(Q). The minimization involves a regularization term to ensure a smooth P(r). The regularization constant gives the size of that term. The suggested value is the value above which the output P(r) will have only one peak.&lt;/span&gt;&lt;/pre&gt;&lt;/body&gt;&lt;/html&gt;</string>
+         </property>
+         <property name="title">
+          <string>Parameters</string>
+         </property>
+         <layout class="QGridLayout" name="gridLayout_8">
+          <item row="1" column="0">
+           <widget class="QLabel" name="label_13">
+            <property name="text">
+             <string>Regularization constant</string>
+            </property>
+           </widget>
+          </item>
+          <item row="0" column="2">
+           <widget class="QPushButton" name="noOfTermsSuggestionButton">
+            <property name="enabled">
+             <bool>false</bool>
+            </property>
+            <property name="text">
+             <string/>
+            </property>
+           </widget>
+          </item>
+          <item row="2" column="2">
+           <widget class="QPushButton" name="explorerButton">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+              <horstretch>0</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="toolTip">
+             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Open the D&lt;span style=&quot; vertical-align:sub;&quot;&gt;max&lt;/span&gt; explorer window.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+            </property>
+            <property name="layoutDirection">
+             <enum>Qt::LeftToRight</enum>
+            </property>
+            <property name="text">
+             <string>Explore</string>
+            </property>
+           </widget>
+          </item>
+          <item row="0" column="1">
+           <widget class="QLineEdit" name="noOfTermsInput"/>
+          </item>
+          <item row="2" column="1">
+           <widget class="QLineEdit" name="maxDistanceInput"/>
+          </item>
+          <item row="0" column="0">
+           <widget class="QLabel" name="label_12">
+            <property name="text">
+             <string>Number of Terms</string>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="2">
+           <widget class="QPushButton" name="regConstantSuggestionButton">
+            <property name="enabled">
+             <bool>false</bool>
+            </property>
+            <property name="text">
+             <string/>
+            </property>
+           </widget>
+          </item>
+          <item row="2" column="0">
+           <widget class="QLabel" name="label_14">
+            <property name="text">
+             <string>Max distance [Ã
+]</string>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="1">
+           <widget class="QLineEdit" name="regularizationConstantInput"/>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item>
+        <widget class="QGroupBox" name="outputsGroupBox">
+         <property name="title">
+          <string>Outputs</string>
+         </property>
+         <layout class="QGridLayout" name="gridLayout_4">
+          <item row="0" column="1">
+           <widget class="QLabel" name="label_23">
+            <property name="text">
+             <string>Calculated Value</string>
+            </property>
+            <property name="alignment">
+             <set>Qt::AlignCenter</set>
+            </property>
+           </widget>
+          </item>
+          <item row="0" column="2">
+           <widget class="QLabel" name="label_28">
+            <property name="text">
+             <string>Units</string>
+            </property>
+           </widget>
+          </item>
+          <item row="6" column="1">
+           <widget class="QLineEdit" name="oscillationValue">
+            <property name="enabled">
+             <bool>false</bool>
+            </property>
+           </widget>
+          </item>
+          <item row="3" column="0">
+           <widget class="QLabel" name="label_17">
+            <property name="text">
+             <string>Background</string>
+            </property>
+           </widget>
+          </item>
+          <item row="7" column="1">
+           <widget class="QLineEdit" name="posFractionValue">
+            <property name="enabled">
+             <bool>false</bool>
+            </property>
+           </widget>
+          </item>
+          <item row="2" column="2">
+           <widget class="QLabel" name="label_25">
+            <property name="text">
+             <string>Ã
+-1</string>
+            </property>
+           </widget>
+          </item>
+          <item row="3" column="1">
+           <widget class="QLineEdit" name="backgroundValue">
+            <property name="enabled">
+             <bool>false</bool>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="0">
+           <widget class="QLabel" name="label_15">
+            <property name="text">
+             <string>Rg</string>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="2">
+           <widget class="QLabel" name="label_24">
+            <property name="text">
+             <string>Ã
+</string>
+            </property>
+           </widget>
+          </item>
+          <item row="6" column="0">
+           <widget class="QLabel" name="label_20">
+            <property name="text">
+             <string>Oscillations</string>
+            </property>
+           </widget>
+          </item>
+          <item row="4" column="0">
+           <widget class="QLabel" name="label_18">
+            <property name="text">
+             <string>Computation Time</string>
+            </property>
+           </widget>
+          </item>
+          <item row="4" column="2">
+           <widget class="QLabel" name="label_27">
+            <property name="text">
+             <string>secs</string>
+            </property>
+           </widget>
+          </item>
+          <item row="2" column="0">
+           <widget class="QLabel" name="label_16">
+            <property name="text">
+             <string>I(Q=0)</string>
+            </property>
+           </widget>
+          </item>
+          <item row="3" column="2">
+           <widget class="QLabel" name="label_26">
+            <property name="text">
+             <string>Ã
+-1</string>
+            </property>
+           </widget>
+          </item>
+          <item row="0" column="0">
+           <widget class="QLabel" name="label_22">
+            <property name="text">
+             <string>Parameter</string>
+            </property>
+           </widget>
+          </item>
+          <item row="8" column="0">
+           <widget class="QLabel" name="label_29">
+            <property name="text">
+             <string>1-sigma positive fraction</string>
+            </property>
+           </widget>
+          </item>
+          <item row="2" column="1">
+           <widget class="QLineEdit" name="iQ0Value">
+            <property name="enabled">
+             <bool>false</bool>
+            </property>
+           </widget>
+          </item>
+          <item row="5" column="0">
+           <widget class="QLabel" name="label_19">
+            <property name="text">
+             <string>Chi^2/dof</string>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="1">
+           <widget class="QLineEdit" name="rgValue">
+            <property name="enabled">
+             <bool>false</bool>
+            </property>
+           </widget>
+          </item>
+          <item row="5" column="1">
+           <widget class="QLineEdit" name="chiDofValue">
+            <property name="enabled">
+             <bool>false</bool>
+            </property>
+           </widget>
+          </item>
+          <item row="4" column="1">
+           <widget class="QLineEdit" name="computationTimeValue">
+            <property name="enabled">
+             <bool>false</bool>
+            </property>
+           </widget>
+          </item>
+          <item row="7" column="0">
+           <widget class="QLabel" name="label_21">
+            <property name="text">
+             <string>Positive Fraction</string>
+            </property>
+           </widget>
+          </item>
+          <item row="8" column="1">
+           <widget class="QLineEdit" name="sigmaPosFractionValue">
+            <property name="enabled">
+             <bool>false</bool>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
Index: src/sas/qtgui/Perspectives/__init__.py
===================================================================
--- src/sas/qtgui/Perspectives/__init__.py	(revision b3e8629bf18b74f4214d6552e620fcbc4e73a28c)
+++ src/sas/qtgui/Perspectives/__init__.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -4,7 +4,9 @@
 from .Fitting.FittingPerspective import FittingWindow
 from .Invariant.InvariantPerspective import InvariantWindow
+from .Inversion.InversionPerspective import InversionWindow
 
 PERSPECTIVES = {
     FittingWindow.name: FittingWindow,
     InvariantWindow.name: InvariantWindow,
+    InversionWindow.name: InversionWindow
 }
Index: src/sas/sascalc/fit/models.py
===================================================================
--- src/sas/sascalc/fit/models.py	(revision b963b2066d6a76fe5073000c5a596a6fd0130ac4)
+++ src/sas/sascalc/fit/models.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -139,5 +139,5 @@
         if type is not None and issubclass(type, py_compile.PyCompileError):
             print("Problem with", repr(value))
-            raise type, value, tb
+            raise (type, value, tb)
         return 1
 
Index: src/sas/sasgui/guiframe/data_processor.py
===================================================================
--- src/sas/sasgui/guiframe/data_processor.py	(revision a1b8fee73528559155793dd74cda07d0c32e9d72)
+++ src/sas/sasgui/guiframe/data_processor.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -909,5 +909,5 @@
                     msg = "Edit axis doesn't understand this selection.\n"
                     msg += "Please select only one column"
-                    raise ValueError, msg
+                    raise (ValueError, msg)
             for (_, cell_col) in grid.selected_cells:
                 if cell_col != col:
@@ -915,5 +915,5 @@
                     msg += "this operation.\n"
                     msg += "Please select elements of the same col.\n"
-                    raise ValueError, msg
+                    raise (ValueError, msg)
 
             # Finally check the highlighted cell if any cells missing
@@ -922,5 +922,5 @@
             msg = "No item selected.\n"
             msg += "Please select only one column or one cell"
-            raise ValueError, msg
+            raise (ValueError, msg)
         return grid.selected_cells
 
@@ -1326,5 +1326,5 @@
             if sentence.strip() == "":
                 msg = "Select column values for x axis"
-                raise ValueError, msg
+                raise (ValueError, msg)
         except:
             msg = "X axis value error."
@@ -1345,5 +1345,5 @@
             if sentence.strip() == "":
                 msg = "select value for y axis"
-                raise ValueError, msg
+                raise (ValueError, msg)
         except:
             msg = "Y axis value error."
@@ -1631,5 +1631,5 @@
 
         # We need to grab a WxMenu handle here, otherwise the next one to grab
-        # the handle will be treated as the Edit Menu handle when checking in
+        # the handle will be treated as the Edi)t Menu handle when checking in
         # on_menu_open event handler and thus raise an exception when it hits an 
         # unitialized object.  Alternative is to comment out that whole section
Index: src/sas/sasgui/guiframe/gui_manager.py
===================================================================
--- src/sas/sasgui/guiframe/gui_manager.py	(revision b963b2066d6a76fe5073000c5a596a6fd0130ac4)
+++ src/sas/sasgui/guiframe/gui_manager.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -2519,5 +2519,5 @@
             wx.PostEvent(self, StatusEvent(status=msg,
                                            info="error"))
-            raise ValueError, msg
+            raise (ValueError, msg)
         # text = str(data)
         text = data.__str__()
Index: src/sas/sasgui/guiframe/local_perspectives/plotting/Plotter1D.py
===================================================================
--- src/sas/sasgui/guiframe/local_perspectives/plotting/Plotter1D.py	(revision 7432acb919f4013119ca918a73350297e0d4c93f)
+++ src/sas/sasgui/guiframe/local_perspectives/plotting/Plotter1D.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -475,5 +475,5 @@
             try:
                 self._onEVT_FUNC_PROPERTY()
-            except Exception, exc:
+            except (Exception, exc):
                 wx.PostEvent(self.parent,
                              StatusEvent(status="Plotting Error: %s" % str(exc), info="error"))
@@ -492,5 +492,5 @@
                     # MAC: forcing to plot 2D avg
                     self.canvas._onDrawIdle()
-            except Exception, exc:
+            except (Exception, exc):
                 wx.PostEvent(self.parent, StatusEvent(status=\
                     "Plotting Error: %s" % str(exc), info="error"))
Index: src/sas/sasgui/guiframe/local_perspectives/plotting/SectorSlicer.py
===================================================================
--- src/sas/sasgui/guiframe/local_perspectives/plotting/SectorSlicer.py	(revision cee5c78929349ae10afc88cb8b95e0c907f1826a)
+++ src/sas/sasgui/guiframe/local_perspectives/plotting/SectorSlicer.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -232,5 +232,5 @@
             msg = "Phi left and phi right are different"
             msg += " %f, %f" % (self.left_line.phi, self.right_line.phi)
-            raise ValueError, msg
+            raise (ValueError, msg)
         params["Phi [deg]"] = self.main_line.theta * 180 / numpy.pi
         params["Delta_Phi [deg]"] = numpy.fabs(self.left_line.phi * 180 / numpy.pi)
Index: src/sas/sasgui/guiframe/local_perspectives/plotting/binder.py
===================================================================
--- src/sas/sasgui/guiframe/local_perspectives/plotting/binder.py	(revision 463e7ffce130ae386f93cd18b87a1120743e88b5)
+++ src/sas/sasgui/guiframe/local_perspectives/plotting/binder.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -187,6 +187,6 @@
         # Check that the trigger is valid
         if trigger not in self._actions:
-            raise ValueError, "%s invalid --- valid triggers are %s" \
-                % (trigger, ", ".join(self.events))
+            raise (ValueError, "%s invalid --- valid triggers are %s" \
+                % (trigger, ", ".join(self.events)))
         # Register the trigger callback
         self._actions[trigger][artist] = action
@@ -203,5 +203,5 @@
         """
         if action not in self.events:
-            raise ValueError, "Trigger expects " + ", ".join(self.events)
+            raise (ValueError, "Trigger expects " + ", ".join(self.events))
         # Tag the event with modifiers
         for mod in ('alt', 'control', 'shift', 'meta'):
Index: src/sas/sasgui/guiframe/local_perspectives/plotting/boxSlicer.py
===================================================================
--- src/sas/sasgui/guiframe/local_perspectives/plotting/boxSlicer.py	(revision cee5c78929349ae10afc88cb8b95e0c907f1826a)
+++ src/sas/sasgui/guiframe/local_perspectives/plotting/boxSlicer.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -136,5 +136,5 @@
             if new_slab is None:
                 msg = "post data:cannot average , averager is empty"
-                raise ValueError, msg
+                raise (ValueError, msg)
             self.averager = new_slab
         if self.direction == "X":
@@ -152,5 +152,5 @@
         else:
             msg = "post data:no Box Average direction was supplied"
-            raise ValueError, msg
+            raise (ValueError, msg)
         # # Average data2D given Qx or Qy
         box = self.averager(x_min=x_min, x_max=x_max, y_min=y_min, y_max=y_max,
Index: src/sas/sasgui/guiframe/local_perspectives/plotting/plotting.py
===================================================================
--- src/sas/sasgui/guiframe/local_perspectives/plotting/plotting.py	(revision 2d9526dfda3bdfe3c9159ab7d9db8399b4a00848)
+++ src/sas/sasgui/guiframe/local_perspectives/plotting/plotting.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -200,5 +200,5 @@
 
         msg = "1D Panel of group ID %s could not be created" % str(group_id)
-        raise ValueError, msg
+        raise (ValueError, msg)
 
     def create_2d_panel(self, data, group_id):
@@ -218,5 +218,5 @@
             return new_panel
         msg = "2D Panel of group ID %s could not be created" % str(group_id)
-        raise ValueError, msg
+        raise (ValueError, msg)
 
     def update_panel(self, data, panel):
@@ -238,5 +238,5 @@
             msg += " to panel %s\n" % str(panel.window_caption)
             msg += "Please edit %s's units, labels" % str(data.name)
-            raise ValueError, msg
+            raise (ValueError, msg)
         else:
             if panel.group_id not in data.list_group_id:
Index: src/sas/sasgui/guiframe/local_perspectives/plotting/sector_mask.py
===================================================================
--- src/sas/sasgui/guiframe/local_perspectives/plotting/sector_mask.py	(revision 7432acb919f4013119ca918a73350297e0d4c93f)
+++ src/sas/sasgui/guiframe/local_perspectives/plotting/sector_mask.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -175,5 +175,5 @@
             msg += "different %f, %f" % (self.left_line.phi,
                                          self.right_line.phi)
-            raise ValueError, msg
+            raise (ValueError, msg)
         params["Phi"] = self.main_line.theta
         params["Delta_Phi"] = math.fabs(self.left_line.phi)
Index: src/sas/sasgui/guiframe/plugin_base.py
===================================================================
--- src/sas/sasgui/guiframe/plugin_base.py	(revision 7432acb919f4013119ca918a73350297e0d4c93f)
+++ src/sas/sasgui/guiframe/plugin_base.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -277,5 +277,5 @@
         """
         msg = "%s plugin: does not support import theory" % str(self.sub_menu)
-        raise ValueError, msg
+        raise (ValueError, msg)
 
     def on_set_state_helper(self, event):
Index: src/sas/sasgui/guiframe/proxy.py
===================================================================
--- src/sas/sasgui/guiframe/proxy.py	(revision a1b8fee73528559155793dd74cda07d0c32e9d72)
+++ src/sas/sasgui/guiframe/proxy.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -127,5 +127,5 @@
             logger.debug("Trying Direct connection to %s..."%self.url)
             response = urllib2.urlopen(req, timeout=self.timeout)
-        except Exception, e:
+        except (Exception, e):
             logger.debug("Failed!")
             logger.debug(e)
@@ -134,5 +134,5 @@
                 self._set_proxy()
                 response = urllib2.urlopen(req, timeout=self.timeout)
-            except Exception, e:
+            except (Exception, e):
                 logger.debug("Failed!")
                 logger.debug(e)
@@ -144,5 +144,5 @@
                         self._set_proxy(proxy)
                         response = urllib2.urlopen(req, timeout=self.timeout)
-                    except Exception, e:
+                    except (Exception, e):
                         logger.debug("Failed!")
                         logger.debug(e)
Index: src/sas/sasgui/perspectives/calculator/__init__.py
===================================================================
--- src/sas/sasgui/perspectives/calculator/__init__.py	(revision 5a405bda2da663894b6b32ab94768a91d60525a4)
+++ src/sas/sasgui/perspectives/calculator/__init__.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -2,5 +2,5 @@
 import os
 from distutils.filelist import findall
-from calculator import *
+from .calculator import *
 N_DIR = 12
 def get_data_path(media):
Index: src/sas/sasgui/perspectives/calculator/collimation_editor.py
===================================================================
--- src/sas/sasgui/perspectives/calculator/collimation_editor.py	(revision 959eb0181ad06828a748f04512f953aad5b31659)
+++ src/sas/sasgui/perspectives/calculator/collimation_editor.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -6,5 +6,5 @@
 from sas.sascalc.dataloader.loader import Loader
 from sas.sascalc.dataloader.data_info import Aperture, Collimation
-from aperture_editor import ApertureDialog
+from .aperture_editor import ApertureDialog
 
 from sas.sasgui.guiframe.utils import check_float
Index: src/sas/sasgui/perspectives/calculator/data_editor.py
===================================================================
--- src/sas/sasgui/perspectives/calculator/data_editor.py	(revision 235f514ba93b57fa63e44309f182acef0b98bb55)
+++ src/sas/sasgui/perspectives/calculator/data_editor.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -7,7 +7,7 @@
 from sas.sascalc.dataloader.loader import Loader
 from sas.sascalc.dataloader.data_info import Data2D
-from detector_editor import DetectorDialog
-from collimation_editor import CollimationDialog
-from console import ConsoleDialog
+from .detector_editor import DetectorDialog
+from .collimation_editor import CollimationDialog
+from .console import ConsoleDialog
 
 from sas.sasgui.guiframe.events import StatusEvent
@@ -397,5 +397,5 @@
         if data is None:
             return
-        from sample_editor import SampleDialog
+        from .sample_editor import SampleDialog
         dlg = SampleDialog(parent=self, sample=data.sample)
         dlg.set_manager(self)
@@ -409,5 +409,5 @@
         if data is None:
             return
-        from source_editor import SourceDialog
+        from .source_editor import SourceDialog
         dlg = SourceDialog(parent=self, source=data.source)
         dlg.set_manager(self)
@@ -527,5 +527,5 @@
         try:
             #Load data
-            from load_thread import DataReader
+            from .load_thread import DataReader
             ## If a thread is already started, stop it
             if self.reader is not None and self.reader.isrunning():
@@ -536,5 +536,5 @@
             self.reader.queue()
         except:
-            msg = "Data Editor: %s" % (sys.exc_value)
+            msg = "Data Editor: %s" % (sys.exc_info()[1])
             load_error(msg)
             return
Index: src/sas/sasgui/perspectives/calculator/data_operator.py
===================================================================
--- src/sas/sasgui/perspectives/calculator/data_operator.py	(revision 7432acb919f4013119ca918a73350297e0d4c93f)
+++ src/sas/sasgui/perspectives/calculator/data_operator.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -202,10 +202,10 @@
         else:
             text = name
-        state_list = self.get_datalist().values()
+        state_list = list(self.get_datalist().values())
         name_list = []
         for state in state_list:
             if state.data is None:
                 theory_list = state.get_theory()
-                theory, _ = theory_list.values()[0]
+                theory, _ = list(theory_list.values())[0]
                 d_name = str(theory.name)
             else:
@@ -397,5 +397,5 @@
             self._set_textctrl_color(self.data1_cbox, 'pink')
             self._set_textctrl_color(self.data2_cbox, 'pink')
-            msg = "DataOperation: %s" % sys.exc_value
+            msg = "DataOperation: %s" % sys.exc_info()[1]
             self.send_warnings(msg, 'error')
             self.output = None
@@ -411,5 +411,5 @@
         operator = self.operator_cbox.GetClientData(pos)
         try:
-            exec "output = data1 %s data2" % operator
+            exec("output = data1 %s data2" % operator)
         except:
             raise
@@ -532,5 +532,5 @@
         self.data2_cbox.SetClientData(pos3, val)
         dnames = []
-        ids = self._data.keys()
+        ids = list(self._data.keys())
         for id in ids:
             if id is not None:
@@ -539,9 +539,9 @@
                 else:
                     theory_list = self._data[id].get_theory()
-                    theory, _ = theory_list.values()[0]
+                    theory, _ = list(theory_list.values())[0]
                     dnames.append(theory.name)
         ind = np.argsort(dnames)
         if len(ind) > 0:
-            val_list = np.array(self._data.values())[ind]
+            val_list = np.array(list(self._data.values()))[ind]
             for datastate in val_list:
                 data = datastate.data
@@ -558,5 +558,5 @@
                 try:
                     theory_list = datastate.get_theory()
-                    for theory, _ in theory_list.values():
+                    for theory, _ in list(theory_list.values()):
                         th_name = theory.name
                         posth1 = self.data1_cbox.Append(str(th_name))
@@ -588,5 +588,5 @@
         self.send_warnings('')
         self.data_namectr.SetBackgroundColour('white')
-        state_list = self.get_datalist().values()
+        state_list = list(self.get_datalist().values())
         name = self.data_namectr.GetValue().strip()
         name_list = []
@@ -594,5 +594,5 @@
             if state.data is None:
                 theory_list = state.get_theory()
-                theory, _ = theory_list.values()[0]
+                theory, _ = list(theory_list.values())[0]
                 d_name = str(theory.name)
             else:
@@ -896,6 +896,6 @@
         list = []
         list = self.graph.returnPlottable()
-        if len(list.keys()) > 0:
-            first_item = list.keys()[0]
+        if len(list(list.keys())) > 0:
+            first_item = list(list.keys())[0]
             if first_item.x != []:
                 from sas.sasgui.plottools.PropertyDialog import Properties
Index: src/sas/sasgui/perspectives/calculator/density_panel.py
===================================================================
--- src/sas/sasgui/perspectives/calculator/density_panel.py	(revision 7432acb919f4013119ca918a73350297e0d4c93f)
+++ src/sas/sasgui/perspectives/calculator/density_panel.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -364,5 +364,5 @@
         except:
             if self.base is not None:
-                msg = "Density/Volume Calculator: %s" % (sys.exc_value)
+                msg = "Density/Volume Calculator: %s" % (sys.exc_info()[1])
                 wx.PostEvent(self.base, StatusEvent(status=msg))
         if event is not None:
Index: src/sas/sasgui/perspectives/calculator/detector_editor.py
===================================================================
--- src/sas/sasgui/perspectives/calculator/detector_editor.py	(revision a1b8fee73528559155793dd74cda07d0c32e9d72)
+++ src/sas/sasgui/perspectives/calculator/detector_editor.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -1,3 +1,3 @@
-from __future__ import print_function
+
 
 import wx
@@ -35,5 +35,5 @@
             self.set_values()
         except:
-            print("error", sys.exc_value)
+            print("error", sys.exc_info()[1])
 
     def _define_structure(self):
Index: src/sas/sasgui/perspectives/calculator/gen_scatter_panel.py
===================================================================
--- src/sas/sasgui/perspectives/calculator/gen_scatter_panel.py	(revision a1b8fee73528559155793dd74cda07d0c32e9d72)
+++ src/sas/sasgui/perspectives/calculator/gen_scatter_panel.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -3,5 +3,5 @@
 This module relies on guiframe manager.
 """
-from __future__ import print_function
+
 
 import wx
@@ -228,5 +228,5 @@
         sizer.Add(unit_title, (iy, ix), (1, 1), \
                             wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-        key_list = params.keys()
+        key_list = list(params.keys())
         key_list.sort()
         for param in key_list:
@@ -560,5 +560,5 @@
             if self.parent.parent is None:
                 return
-            msg = "Generic SAS Calculator: %s" % (sys.exc_value)
+            msg = "Generic SAS Calculator: %s" % (sys.exc_info()[1])
             wx.PostEvent(self.parent.parent,
                           StatusEvent(status=msg, type='stop'))
@@ -761,5 +761,5 @@
         # II. Plot selective points in color
         other_color = np.ones(len(pix_symbol), dtype='bool')
-        for key in color_dic.keys():
+        for key in list(color_dic.keys()):
             chosen_color = pix_symbol == key
             if np.any(chosen_color):
@@ -775,5 +775,5 @@
                 # Get atom names not in the list
                 a_names = [symb  for symb in pix_symbol \
-                           if symb not in color_dic.keys()]
+                           if symb not in list(color_dic.keys())]
                 a_name = a_names[0]
                 for name in a_names:
@@ -899,5 +899,5 @@
 
         except:
-            msg = "%s." % sys.exc_value
+            msg = "%s." % sys.exc_info()[1]
             status_type = 'stop'
             self._status_info(msg, status_type)
@@ -1285,5 +1285,5 @@
             else:
                sld_sets[list[0]] = None
-        for key in sld_sets.keys():
+        for key in list(sld_sets.keys()):
             key_low = key.lower()
             if key_low.count('mx') > 0:
@@ -1336,5 +1336,5 @@
                     return
 
-            for key in sets.keys():
+            for key in list(sets.keys()):
                 setattr(omfdata, key, sets[key])
 
@@ -1345,5 +1345,5 @@
             self.sld_data.filename = "Default SLD Profile"
         except:
-            msg = "OMF Panel: %s" % sys.exc_value
+            msg = "OMF Panel: %s" % sys.exc_info()[1]
             infor = 'Error'
             #logger.error(msg)
@@ -1375,5 +1375,5 @@
         step_list = self._get_step_key_list(omfdata)
         for ctr_list in self.nodes:
-            for key in nodes_list.keys():
+            for key in list(nodes_list.keys()):
                 if ctr_list[0] == key:
                     ctr_list[1].SetValue(format_number(nodes_list[key], True))
@@ -1381,5 +1381,5 @@
                     break
         for ctr_list in self.stepsize:
-            for key in step_list.keys():
+            for key in list(step_list.keys()):
                 if ctr_list[0] == key:
                     ctr_list[1].SetValue(format_number(step_list[key], True))
@@ -1442,5 +1442,5 @@
         sld_key_list = self._get_slds_key_list(omfdata)
         # Dic is not sorted
-        key_list = [key for key in sld_key_list.keys()]
+        key_list = [key for key in list(sld_key_list.keys())]
         # Sort here
         key_list.sort()
@@ -1485,5 +1485,5 @@
         ix = 0
         iy = -1
-        for key, value in key_list.iteritems():
+        for key, value in key_list.items():
             iy += 1
             ix = 0
@@ -1521,5 +1521,5 @@
         iy = -1
         #key_list.sort()
-        for key, value in key_list.iteritems():
+        for key, value in key_list.items():
             iy += 1
             ix = 0
@@ -1641,5 +1641,5 @@
         sld_list = self._get_slds_key_list(sld_data)
         for ctr_list in self.slds:
-            for key in sld_list.keys():
+            for key in list(sld_list.keys()):
                 if ctr_list[0] == key:
                     min_val = np.min(sld_list[key])
Index: src/sas/sasgui/perspectives/calculator/image_viewer.py
===================================================================
--- src/sas/sasgui/perspectives/calculator/image_viewer.py	(revision 412e9e8b8f844bae61656ffb2a2d0c1c53e6e8de)
+++ src/sas/sasgui/perspectives/calculator/image_viewer.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -1,3 +1,3 @@
-from __future__ import print_function
+
 
 import os
Index: src/sas/sasgui/perspectives/calculator/kiessig_calculator_panel.py
===================================================================
--- src/sas/sasgui/perspectives/calculator/kiessig_calculator_panel.py	(revision 7432acb919f4013119ca918a73350297e0d4c93f)
+++ src/sas/sasgui/perspectives/calculator/kiessig_calculator_panel.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -14,6 +14,6 @@
 from sas.sasgui.guiframe.panel_base import PanelBase
 from sas.sascalc.calculator.kiessig_calculator import KiessigThicknessCalculator
-from calculator_widgets import OutputTextCtrl
-from calculator_widgets import InputTextCtrl
+from .calculator_widgets import OutputTextCtrl
+from .calculator_widgets import InputTextCtrl
 from sas.sasgui.perspectives.calculator import calculator_widgets as widget
 from sas.sasgui.guiframe.documentation_window import DocumentationWindow
Index: src/sas/sasgui/perspectives/calculator/model_editor.py
===================================================================
--- src/sas/sasgui/perspectives/calculator/model_editor.py	(revision 23359ccb520c33d89444d0732d752a3bc0226916)
+++ src/sas/sasgui/perspectives/calculator/model_editor.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -23,5 +23,5 @@
 #copyright 2009, University of Tennessee
 ################################################################################
-from __future__ import print_function
+
 
 import wx
@@ -790,5 +790,5 @@
             if item.count("_") < 1:
                 try:
-                    exec "float(math.%s)" % item
+                    exec("float(math.%s)" % item)
                     self.math_combo.Append(str(item))
                 except Exception:
Index: src/sas/sasgui/perspectives/calculator/resolution_calculator_panel.py
===================================================================
--- src/sas/sasgui/perspectives/calculator/resolution_calculator_panel.py	(revision 01cda57b9e14ce69f0153005140f08837b90f28e)
+++ src/sas/sasgui/perspectives/calculator/resolution_calculator_panel.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -148,5 +148,5 @@
         # Custom sorting
         source_list = []
-        for key, _ in self.source_mass.iteritems():
+        for key, _ in self.source_mass.items():
             name_source = str(key)
             source_list.append(name_source)
@@ -1082,5 +1082,5 @@
                 msg = "The numbers must be one or two (separated by ',')..."
                 self._status_info(msg, 'stop')
-                raise RuntimeError, msg
+                raise RuntimeError(msg)
 
         return new_string
@@ -1100,5 +1100,5 @@
                 new_string.append(value)
             except:
-                logger.error(sys.exc_value)
+                logger.error(sys.exc_info()[1])
 
         return new_string
@@ -1142,5 +1142,5 @@
                         return out
                 except:
-                    logger.error(sys.exc_value)
+                    logger.error(sys.exc_info()[1])
 
     def _on_xy_coordinate(self, event=None):
@@ -1269,5 +1269,5 @@
             try:
                 basename = os.path.basename(path)
-                if basename not in self.spectrum_dic.keys():
+                if basename not in list(self.spectrum_dic.keys()):
                     self.spectrum_cb.Append(basename)
                 self.spectrum_dic[basename] = self._read_file(path)
@@ -1320,5 +1320,5 @@
                 except:
                     # Skip non-data lines
-                    logger.error(sys.exc_value)
+                    logger.error(sys.exc_info()[1])
 
             return [wavelength, intensity]
Index: src/sas/sasgui/perspectives/calculator/sld_panel.py
===================================================================
--- src/sas/sasgui/perspectives/calculator/sld_panel.py	(revision fe09e9bb0ad6caa9284d13a5c7dfdfc420d0c28b)
+++ src/sas/sasgui/perspectives/calculator/sld_panel.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -366,5 +366,5 @@
         if len(myformula.atoms) != 1:
             return
-        element = myformula.atoms.keys()[0]
+        element = list(myformula.atoms.keys())[0]
         energy = xray_energy(element.K_alpha)
 
@@ -450,5 +450,5 @@
         if len(element_formula.atoms) != 1:
             return
-        element = element_formula.atoms.keys()[0]
+        element = list(element_formula.atoms.keys())[0]
         energy = xray_energy(element.K_alpha)
         atom = molecule_formula.atoms
@@ -507,5 +507,5 @@
         except:
             if self.base is not None:
-                msg = "SLD Calculator: %s" % (sys.exc_value)
+                msg = "SLD Calculator: %s" % (sys.exc_info()[1])
                 wx.PostEvent(self.base, StatusEvent(status=msg))
         if event is not None:
Index: src/sas/sasgui/perspectives/calculator/slit_length_calculator_panel.py
===================================================================
--- src/sas/sasgui/perspectives/calculator/slit_length_calculator_panel.py	(revision 7432acb919f4013119ca918a73350297e0d4c93f)
+++ src/sas/sasgui/perspectives/calculator/slit_length_calculator_panel.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -17,6 +17,6 @@
 from sas.sasgui.guiframe.events import StatusEvent
 from sas.sascalc.calculator.slit_length_calculator import SlitlengthCalculator
-from calculator_widgets import OutputTextCtrl
-from calculator_widgets import InterActiveOutputTextCtrl
+from .calculator_widgets import OutputTextCtrl
+from .calculator_widgets import InterActiveOutputTextCtrl
 from sas.sasgui.perspectives.calculator import calculator_widgets as widget
 from sas.sasgui.guiframe.documentation_window import DocumentationWindow
@@ -210,5 +210,5 @@
         try:
             #Load data
-            from load_thread import DataReader
+            from .load_thread import DataReader
             ## If a thread is already started, stop it
             if self.reader is not None and self.reader.isrunning():
@@ -226,5 +226,5 @@
             if self.parent.parent is None:
                 return
-            msg = "Slit Length Calculator: %s" % (sys.exc_value)
+            msg = "Slit Length Calculator: %s" % (sys.exc_info()[1])
             wx.PostEvent(self.parent.parent,
                           StatusEvent(status=msg, type='stop'))
@@ -262,5 +262,5 @@
             if x == [] or  x is None or y == [] or y is None:
                 msg = "The current data is empty please check x and y"
-                raise ValueError, msg
+                raise ValueError(msg)
             slit_length_calculator = SlitlengthCalculator()
             slit_length_calculator.set_data(x=x, y=y)
@@ -269,5 +269,5 @@
             if self.parent.parent is None:
                 return
-            msg = "Slit Size Calculator: %s" % (sys.exc_value)
+            msg = "Slit Size Calculator: %s" % (sys.exc_info()[1])
             wx.PostEvent(self.parent.parent,
                           StatusEvent(status=msg, type='stop'))
Index: src/sas/sasgui/perspectives/corfunc/__init__.py
===================================================================
--- src/sas/sasgui/perspectives/corfunc/__init__.py	(revision c23f303448cfceb9e5a58e5d7da46258cdb3cbd8)
+++ src/sas/sasgui/perspectives/corfunc/__init__.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -1,2 +1,2 @@
 PLUGIN_ID = "Corfunc Plug-In 0.1"
-from corfunc import *
+from .corfunc import *
Index: src/sas/sasgui/perspectives/corfunc/corfunc.py
===================================================================
--- src/sas/sasgui/perspectives/corfunc/corfunc.py	(revision 9b90bf84a52cb67272ccbed5bac69156a6ca6bc3)
+++ src/sas/sasgui/perspectives/corfunc/corfunc.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -18,5 +18,5 @@
 from sas.sascalc.dataloader.loader import Loader
 import sas.sascalc.dataloader
-from plot_labels import *
+from .plot_labels import *
 
 logger = logging.getLogger(__name__)
@@ -149,5 +149,5 @@
                     self.corfunc_panel.set_data(data)
                 except:
-                    msg = "Corfunc set_data: " + str(sys.exc_value)
+                    msg = "Corfunc set_data: " + str(sys.exc_info()[1])
                     wx.PostEvent(self.parent, StatusEvent(status=msg,
                         info='error'))
Index: src/sas/sasgui/perspectives/corfunc/corfunc_panel.py
===================================================================
--- src/sas/sasgui/perspectives/corfunc/corfunc_panel.py	(revision 2a399cab29be825175279ecb215dd7ac22191496)
+++ src/sas/sasgui/perspectives/corfunc/corfunc_panel.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -17,5 +17,5 @@
 from sas.sascalc.corfunc.corfunc_calculator import CorfuncCalculator
 from sas.sasgui.guiframe.documentation_window import DocumentationWindow
-from plot_labels import *
+from .plot_labels import *
 
 OUTPUT_STRINGS = {
@@ -393,8 +393,8 @@
         if params is None:
             # Reset outputs
-            for output in self._extrapolation_outputs.values():
+            for output in list(self._extrapolation_outputs.values()):
                 output.SetValue('-')
             return
-        for key, value in params.iteritems():
+        for key, value in params.items():
             output = self._extrapolation_outputs[key]
             rounded = self._round_sig_figs(value, 6)
@@ -411,5 +411,5 @@
         if params is None:
             if not reset: error = True
-            for output in self._output_boxes.values():
+            for output in list(self._output_boxes.values()):
                 output.SetValue('-')
         else:
@@ -417,5 +417,5 @@
                 # Not all parameters were calculated
                 error = True
-            for key, value in params.iteritems():
+            for key, value in params.items():
                 rounded = self._round_sig_figs(value, 6)
                 self._output_boxes[key].SetValue(rounded)
@@ -656,5 +656,5 @@
         self._extrapolation_outputs['K'] = k_output
 
-        sigma_label = wx.StaticText(self, -1, u'\u03C3: ')
+        sigma_label = wx.StaticText(self, -1, '\u03C3: ')
         params_sizer.Add(sigma_label, (2, 2), (1, 1), wx.LEFT | wx.EXPAND, 15)
 
@@ -713,5 +713,5 @@
         self._output_boxes = dict()
         i = 0
-        for key, value in OUTPUT_STRINGS.iteritems():
+        for key, value in OUTPUT_STRINGS.items():
             # Create a label and a text box for each poperty
             label = wx.StaticText(self, -1, value)
Index: src/sas/sasgui/perspectives/corfunc/corfunc_state.py
===================================================================
--- src/sas/sasgui/perspectives/corfunc/corfunc_state.py	(revision 1fa4f7368e26981352615cbfb337a581ed97031f)
+++ src/sas/sasgui/perspectives/corfunc/corfunc_state.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -74,5 +74,5 @@
         if self.outputs != {} and self.outputs is not None:
             state += "\nOutputs:\n"
-            for key, value in self.outputs.iteritems():
+            for key, value in self.outputs.items():
                 name = output_list[key][1]
                 state += "{}: {}\n".format(name, str(value))
@@ -158,5 +158,5 @@
         state = new_doc.createElement("state")
         top_element.appendChild(state)
-        for name, value in self.saved_state.iteritems():
+        for name, value in self.saved_state.items():
             element = new_doc.createElement(name)
             element.appendChild(new_doc.createTextNode(str(value)))
@@ -181,5 +181,5 @@
             output = new_doc.createElement("output")
             top_element.appendChild(output)
-            for key, value in self.outputs.iteritems():
+            for key, value in self.outputs.items():
                 element = new_doc.createElement(key)
                 element.appendChild(new_doc.createTextNode(str(value)))
@@ -216,5 +216,5 @@
                 except:
                     msg = ("CorfuncState.fromXML: Could not read timestamp",
-                        "\n{}").format(sys.exc_value)
+                        "\n{}").format(sys.exc_info()[1])
                     logger.error(msg)
 
@@ -222,5 +222,5 @@
             entry = get_content('ns:state', node)
             if entry is not None:
-                for item in DEFAULT_STATE.iterkeys():
+                for item in DEFAULT_STATE.keys():
                     input_field = get_content("ns:{}".format(item), entry)
                     if input_field is not None:
@@ -283,5 +283,5 @@
             root, ext = os.path.splitext(basename)
             if not ext.lower() in self.ext:
-                raise IOError, "{} is not a supported file type".format(ext)
+                raise IOError("{} is not a supported file type".format(ext))
             tree = etree.parse(path, parser=etree.ETCompatXMLParser())
             root = tree.getroot()
@@ -299,5 +299,5 @@
             # File not found
             msg = "{} is not a valid file path or doesn't exist".format(path)
-            raise IOError, msg
+            raise IOError(msg)
 
         if len(output) == 0:
@@ -323,5 +323,5 @@
             msg = ("The CanSAS writer expects a Data1D instance. {} was "
                 "provided").format(datainfo.__class__.__name__)
-            raise RuntimeError, msg
+            raise RuntimeError(msg)
         if datainfo.title is None or datainfo.title == '':
             datainfo.title = datainfo.name
@@ -360,5 +360,5 @@
         except:
             msg = "XML document does not contain CorfuncState information\n{}"
-            msg.format(sys.exc_value)
+            msg.format(sys.exc_info()[1])
             logger.info(msg)
         return state
Index: src/sas/sasgui/perspectives/file_converter/__init__.py
===================================================================
--- src/sas/sasgui/perspectives/file_converter/__init__.py	(revision 77d92cd296d0af12557c509f0c48df88ef5f59e9)
+++ src/sas/sasgui/perspectives/file_converter/__init__.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -1,2 +1,2 @@
 PLUGIN_ID = "File-Converter Plug-In 1.0"
-from file_converter import *
+from .file_converter import *
Index: src/sas/sasgui/perspectives/file_converter/converter_panel.py
===================================================================
--- src/sas/sasgui/perspectives/file_converter/converter_panel.py	(revision a26f67f1457e7e39b79d4c3800c03bcaa208dff7)
+++ src/sas/sasgui/perspectives/file_converter/converter_panel.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -112,5 +112,5 @@
             [group_path, group_name] = os.path.split(filepath)
             ext = "." + group_name.split('.')[-1] # File extension
-            for frame_number, frame_data in frame_data.iteritems():
+            for frame_number, frame_data in frame_data.items():
                 # Append frame number to base filename
                 filename = group_name.replace(ext, str(frame_number)+ext)
@@ -155,5 +155,5 @@
             # If lines end with comma or semi-colon, trim the last character
             if end_char == ',' or end_char == ';':
-                data = map(lambda s: s[0:-1], data)
+                data = [s[0:-1] for s in data]
             else:
                 msg = ("Error reading {}: Lines must end with a digit, comma "
@@ -275,5 +275,5 @@
             else:
                 return { 'frames': [], 'inc': None, 'file': single_file }
-        frames = range(first_frame, last_frame + 1, increment)
+        frames = list(range(first_frame, last_frame + 1, increment))
         return { 'frames': frames, 'inc': increment, 'file': single_file }
 
@@ -335,13 +335,13 @@
         if single_file:
             # Only need to set metadata on first Data1D object
-            frame_data = frame_data.values() # Don't need to know frame numbers
+            frame_data = list(frame_data.values()) # Don't need to know frame numbers
             frame_data[0].filename = output_path.split('\\')[-1]
-            for key, value in metadata.iteritems():
+            for key, value in metadata.items():
                 setattr(frame_data[0], key, value)
         else:
             # Need to set metadata for all Data1D objects
-            for datainfo in frame_data.values():
+            for datainfo in list(frame_data.values()):
                 datainfo.filename = output_path.split('\\')[-1]
-                for key, value in metadata.iteritems():
+                for key, value in metadata.items():
                     setattr(datainfo, key, value)
 
@@ -355,5 +355,5 @@
     def convert_2d_data(self, dataset):
         metadata = self.get_metadata()
-        for key, value in metadata.iteritems():
+        for key, value in metadata.items():
             setattr(dataset[0], key, value)
 
Index: src/sas/sasgui/perspectives/file_converter/converter_widgets.py
===================================================================
--- src/sas/sasgui/perspectives/file_converter/converter_widgets.py	(revision 0e11ec7528fae0e8cd8f2e311b10e73c4b19c77b)
+++ src/sas/sasgui/perspectives/file_converter/converter_widgets.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -54,5 +54,5 @@
         v = Vector()
         if not self.Validate(): return v
-        for direction, control in self._inputs.iteritems():
+        for direction, control in self._inputs.items():
             try:
                 value = float(control.GetValue())
@@ -86,5 +86,5 @@
         all_valid = True
         invalid_ctrl = None
-        for control in self._inputs.values():
+        for control in list(self._inputs.values()):
             if control.GetValue() == '': continue
             control.SetBackgroundColour(wx.WHITE)
Index: src/sas/sasgui/perspectives/fitting/__init__.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/__init__.py	(revision 12d3e0e31a7f9e8d72d50e8ad904316b003a6bea)
+++ src/sas/sasgui/perspectives/fitting/__init__.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -1,5 +1,5 @@
 PLUGIN_ID = "Fitting plug-in 1.0"
 import os
-from fitting import *
+from .fitting import *
 from distutils.filelist import findall
 def get_data_path(media):
Index: src/sas/sasgui/perspectives/fitting/basepage.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/basepage.py	(revision 3bd677bda9477bbd07235d9fba448f09ae1b3932)
+++ src/sas/sasgui/perspectives/fitting/basepage.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -2,5 +2,5 @@
 Base Page for fitting
 """
-from __future__ import print_function
+
 
 import sys
@@ -12,5 +12,5 @@
 import logging
 import traceback
-from Queue import Queue
+from queue import Queue
 from threading import Thread
 from collections import defaultdict
@@ -223,5 +223,5 @@
         self.popUpMenu = wx.Menu()
 
-        wx_id = self._ids.next()
+        wx_id = next(self._ids)
         self._keep = wx.MenuItem(self.popUpMenu, wx_id, "Add bookmark",
                                  " Keep the panel status to recall it later")
@@ -623,5 +623,5 @@
         if self.model is not None:
             self.m_name = self.model.name
-        if name in self.saved_states.keys():
+        if name in list(self.saved_states.keys()):
             previous_state = self.saved_states[name]
             # reset state of checkbox,textcrtl  and  regular parameters value
@@ -918,5 +918,5 @@
 
         if len(self._disp_obj_dict) > 0:
-            for k, v in self._disp_obj_dict.iteritems():
+            for k, v in self._disp_obj_dict.items():
                 self.state.disp_obj_dict[k] = v.type
 
@@ -985,5 +985,5 @@
 
             if len(self.disp_cb_dict) > 0:
-                for k, v in self.disp_cb_dict.iteritems():
+                for k, v in self.disp_cb_dict.items():
                     if v is None:
                         self.state.disp_cb_dict[k] = v
@@ -994,5 +994,5 @@
                             self.state.disp_cb_dict[k] = None
             if len(self._disp_obj_dict) > 0:
-                for k, v in self._disp_obj_dict.iteritems():
+                for k, v in self._disp_obj_dict.items():
                     self.state.disp_obj_dict[k] = v.type
 
@@ -1096,5 +1096,5 @@
             if name == "ArrayDispersion":
 
-                for item in self.disp_cb_dict.keys():
+                for item in list(self.disp_cb_dict.keys()):
 
                     if hasattr(self.disp_cb_dict[item], "SetValue"):
@@ -1167,5 +1167,5 @@
         :return: combo_box_position
         """
-        for key, value in self.master_category_dict.iteritems():
+        for key, value in self.master_category_dict.items():
             formfactor = state.formfactorcombobox.split(":")
             if isinstance(formfactor, list):
@@ -1219,5 +1219,5 @@
         # select the current model
         state._convert_to_sasmodels()
-        state.categorycombobox = unicode(state.categorycombobox)
+        state.categorycombobox = str(state.categorycombobox)
         if state.categorycombobox in self.categorybox.Items:
             category_pos = self.categorybox.Items.index(
@@ -1244,5 +1244,5 @@
         structfactor_pos = 0
         if state.structurecombobox is not None:
-            state.structurecombobox = unicode(state.structurecombobox)
+            state.structurecombobox = str(state.structurecombobox)
             for ind_struct in range(self.structurebox.GetCount()):
                 if (self.structurebox.GetString(ind_struct)
@@ -1354,5 +1354,5 @@
         self.weights = copy.deepcopy(state.weights)
 
-        for key, disp_type in state.disp_obj_dict.iteritems():
+        for key, disp_type in state.disp_obj_dict.items():
             # disp_model = disp
             disp_model = POLYDISPERSITY_MODELS[disp_type]()
@@ -1415,8 +1415,8 @@
         """
         ids = iter(self._id_pool)  # Reusing ids for context menu
-        for name, _ in self.state.saved_states.iteritems():
+        for name, _ in self.state.saved_states.items():
             self.number_saved_state += 1
             # Add item in the context menu
-            wx_id = ids.next()
+            wx_id = next(ids)
             msg = 'Save model and state %g' % self.number_saved_state
             self.popUpMenu.Append(wx_id, name, msg)
@@ -2314,5 +2314,5 @@
                     value_ctrl.SetValue(format_number(value))
 
-                if name not in self.model.details.keys():
+                if name not in list(self.model.details.keys()):
                     self.model.details[name] = ["", None, None]
                 old_low, old_high = self.model.details[name][1:3]
@@ -2675,8 +2675,8 @@
         if disp_func is not None:
             try:
-                return POLYDISPERSITY_MODELS.values().index(disp_func.__class__)
+                return list(POLYDISPERSITY_MODELS.values()).index(disp_func.__class__)
             except ValueError:
                 pass  # Fall through to default class
-        return POLYDISPERSITY_MODELS.keys().index('gaussian')
+        return list(POLYDISPERSITY_MODELS.keys()).index('gaussian')
 
     def on_reset_clicked(self, event):
@@ -2770,5 +2770,5 @@
         gui_manager = self._manager.parent
         # loops through the panels [dic]
-        for _, item2 in gui_manager.plot_panels.iteritems():
+        for _, item2 in gui_manager.plot_panels.items():
             data_title = self.data.group_id
             # try to get all plots belonging to this control panel
@@ -3340,5 +3340,5 @@
             if self.data.__class__.__name__ == "Data2D":
                 name = item[1]
-                if name in content.keys():
+                if name in list(content.keys()):
                     values = content[name]
                     check = values[0]
@@ -3389,5 +3389,5 @@
                 if not item[1] in orient_param:
                     name = item[1]
-                    if name in content.keys():
+                    if name in list(content.keys()):
                         check = content[name][0]
                         # Avoid changing combox content
@@ -3615,15 +3615,15 @@
         sizer_cat = wx.BoxSizer(wx.HORIZONTAL)
         self.mbox_description.SetForegroundColour(wx.RED)
-        wx_id = self._ids.next()
+        wx_id = next(self._ids)
         self.model_func = wx.Button(self, wx_id, 'Help', size=(80, 23))
         self.model_func.Bind(wx.EVT_BUTTON, self.on_function_help_clicked,
                              id=wx_id)
         self.model_func.SetToolTipString("Full Model Function Help")
-        wx_id = self._ids.next()
+        wx_id = next(self._ids)
         self.model_help = wx.Button(self, wx_id, 'Description', size=(80, 23))
         self.model_help.Bind(wx.EVT_BUTTON, self.on_model_help_clicked,
                              id=wx_id)
         self.model_help.SetToolTipString("Short Model Function Description")
-        wx_id = self._ids.next()
+        wx_id = next(self._ids)
         self.model_view = wx.Button(self, wx_id, "Show 2D", size=(80, 23))
         self.model_view.Bind(wx.EVT_BUTTON, self._onModel2D, id=wx_id)
Index: src/sas/sasgui/perspectives/fitting/fit_thread.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/fit_thread.py	(revision ba8d326f0f4ceb55c77296a9b5af8bd013c7dd92)
+++ src/sas/sasgui/perspectives/fitting/fit_thread.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -11,5 +11,5 @@
 
 def map_apply(arguments):
-    return apply(arguments[0], arguments[1:])
+    return arguments[0](*arguments[1:])
 
 class FitThread(CalcThread):
@@ -49,5 +49,5 @@
         except KeyboardInterrupt:
             msg = "Fitting: terminated by the user."
-            raise KeyboardInterrupt, msg
+            raise KeyboardInterrupt(msg)
 
     def compute(self):
@@ -72,8 +72,8 @@
                 list_map_get_attr.append(map_getattr)
             #from multiprocessing import Pool
-            inputs = zip(list_map_get_attr, self.fitter, list_fit_function,
+            inputs = list(zip(list_map_get_attr, self.fitter, list_fit_function,
                          list_q, list_q, list_handler, list_curr_thread,
-                         list_reset_flag)
-            result = map(map_apply, inputs)
+                         list_reset_flag))
+            result = list(map(map_apply, inputs))
 
             self.complete(result=result,
@@ -84,5 +84,5 @@
                           elapsed=time.time() - self.starttime)
 
-        except KeyboardInterrupt, msg:
+        except KeyboardInterrupt as msg:
             # Thread was interrupted, just proceed and re-raise.
             # Real code should not print, but this is an example...
Index: src/sas/sasgui/perspectives/fitting/fitpage.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/fitpage.py	(revision 3bd677bda9477bbd07235d9fba448f09ae1b3932)
+++ src/sas/sasgui/perspectives/fitting/fitpage.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -281,5 +281,5 @@
 
         # Fit button
-        self.btFit = wx.Button(self, self._ids.next(), 'Fit')
+        self.btFit = wx.Button(self, next(self._ids), 'Fit')
         self.default_bt_colour = self.btFit.GetDefaultAttributes()
         self.btFit.Bind(wx.EVT_BUTTON, self._onFit, id=self.btFit.GetId())
@@ -377,5 +377,5 @@
 
         # Update and Draw button
-        self.draw_button = wx.Button(self, self._ids.next(), 'Compute')
+        self.draw_button = wx.Button(self, next(self._ids), 'Compute')
         self.draw_button.Bind(wx.EVT_BUTTON,
                               self._onDraw, id=self.draw_button.GetId())
@@ -532,5 +532,5 @@
         self.qmin.Bind(wx.EVT_TEXT, self.on_qrange_text)
         self.qmax.Bind(wx.EVT_TEXT, self.on_qrange_text)
-        wx_id = self._ids.next()
+        wx_id = next(self._ids)
         self.reset_qrange = wx.Button(self, wx_id, 'Reset')
 
@@ -540,5 +540,5 @@
         sizer = wx.GridSizer(5, 5, 2, 6)
 
-        self.btEditMask = wx.Button(self, self._ids.next(), 'Editor')
+        self.btEditMask = wx.Button(self, next(self._ids), 'Editor')
         self.btEditMask.Bind(wx.EVT_BUTTON, self._onMask,
                              id=self.btEditMask.GetId())
@@ -660,5 +660,5 @@
         self.text_disp_min.Show(True)
 
-        for item in self.model.dispersion.keys():
+        for item in list(self.model.dispersion.keys()):
             if not self.magnetic_on:
                 if item in self.model.magnetic_params:
@@ -675,5 +675,5 @@
 
                 iy += 1
-                for p in self.model.dispersion[item].keys():
+                for p in list(self.model.dispersion[item].keys()):
 
                     if p == "width":
@@ -765,5 +765,5 @@
                 disp_box = wx.ComboBox(self, wx.ID_ANY, size=(65, -1),
                                        style=wx.CB_READONLY, name='%s' % name1)
-                for key, value in POLYDISPERSITY_MODELS.iteritems():
+                for key, value in POLYDISPERSITY_MODELS.items():
                     name_disp = str(key)
                     disp_box.Append(name_disp, value)
@@ -779,5 +779,5 @@
                           wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
         first_orient = True
-        for item in self.model.dispersion.keys():
+        for item in list(self.model.dispersion.keys()):
             if not self.magnetic_on:
                 if item in self.model.magnetic_params:
@@ -795,5 +795,5 @@
 
                 iy += 1
-                for p in self.model.dispersion[item].keys():
+                for p in list(self.model.dispersion[item].keys()):
 
                     if p == "width":
@@ -929,5 +929,5 @@
                 disp_box = wx.ComboBox(self, wx.ID_ANY, size=(65, -1),
                                 style=wx.CB_READONLY, name='%s' % name1)
-                for key, value in POLYDISPERSITY_MODELS.iteritems():
+                for key, value in POLYDISPERSITY_MODELS.items():
                     name_disp = str(key)
                     disp_box.Append(name_disp, value)
@@ -1370,5 +1370,5 @@
             except:
                 tcrtl.SetBackgroundColour("pink")
-                msg = "Model Error:wrong value entered : %s" % sys.exc_value
+                msg = "Model Error:wrong value entered : %s" % sys.exc_info()[1]
                 wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
                 return
@@ -1482,10 +1482,10 @@
                 else:
                     tcrtl.SetBackgroundColour("pink")
-                    msg = "Model Error:wrong value entered : %s" % sys.exc_value
+                    msg = "Model Error:wrong value entered : %s" % sys.exc_info()[1]
                     wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
                     return
             except:
                 tcrtl.SetBackgroundColour("pink")
-                msg = "Model Error:wrong value entered : %s" % sys.exc_value
+                msg = "Model Error:wrong value entered : %s" % sys.exc_info()[1]
                 wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
                 return
@@ -1733,5 +1733,5 @@
         ind = 0
         while(ind < len(list)):
-            for key, val in list.items():
+            for key, val in list(list.items()):
                 if val == ind:
                     fun_box.Append(key, val)
@@ -1865,5 +1865,5 @@
                     wx.PostEvent(self._manager.parent, StatusEvent(status=msg,
                                                info="error"))
-                    raise ValueError, msg
+                    raise ValueError(msg)
 
             else:
@@ -1877,5 +1877,5 @@
                     wx.PostEvent(self._manager.parent, StatusEvent(status=msg,
                                                info="error"))
-                    raise ValueError, msg
+                    raise ValueError(msg)
                 # Maximum value of data
                 qmax = math.sqrt(x * x + y * y)
@@ -2118,5 +2118,5 @@
         self._on_fit_complete()
         if out is None or not np.isfinite(chisqr):
-            raise ValueError, "Fit error occured..."
+            raise ValueError("Fit error occured...")
 
         is_modified = False
@@ -2193,5 +2193,5 @@
                 i += 1
             else:
-                raise ValueError, "onsetValues: Invalid parameters..."
+                raise ValueError("onsetValues: Invalid parameters...")
         # Show error title when any errors displayed
         if has_error:
@@ -2951,6 +2951,6 @@
 
         # type can be either Guassian or Array
-        if len(self.model.dispersion.values()) > 0:
-            type = self.model.dispersion.values()[0]["type"]
+        if len(list(self.model.dispersion.values())) > 0:
+            type = list(self.model.dispersion.values())[0]["type"]
         else:
             type = "Gaussian"
Index: src/sas/sasgui/perspectives/fitting/fitpanel.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/fitpanel.py	(revision c9ecd1b562e38798001341ea1382b347f0cc1af9)
+++ src/sas/sasgui/perspectives/fitting/fitpanel.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -94,5 +94,5 @@
             batch_state = self.sim_page.set_state()
 
-        for uid, page in self.opened_pages.iteritems():
+        for uid, page in self.opened_pages.items():
             data = page.get_data()
             # state must be cloned
@@ -135,5 +135,5 @@
         if uid not in self.opened_pages:
             msg = "Fitpanel cannot find ID: %s in self.opened_pages" % str(uid)
-            raise ValueError, msg
+            raise ValueError(msg)
         else:
             return self.opened_pages[uid]
@@ -217,5 +217,5 @@
         page_is_opened = False
         if state is not None:
-            for uid, panel in self.opened_pages.iteritems():
+            for uid, panel in self.opened_pages.items():
                 # Don't return any panel is the exact same page is created
                 if uid == panel.uid and panel.data == state.data:
@@ -396,7 +396,7 @@
         """
         if data.__class__.__name__ != "list":
-            raise ValueError, "Fitpanel delete_data expect list of id"
+            raise ValueError("Fitpanel delete_data expect list of id")
         else:
-            for page in self.opened_pages.values():
+            for page in list(self.opened_pages.values()):
                 pos = self.GetPageIndex(page)
                 temp_data = page.get_data()
@@ -433,5 +433,5 @@
                 data_2d_list.append(data)
         page = None
-        for p in self.opened_pages.values():
+        for p in list(self.opened_pages.values()):
             # check if there is an empty page to fill up
             if not check_data_validity(p.get_data()) and p.batch_on:
@@ -503,5 +503,5 @@
                 return None
         focused_page = self.GetPage(self.GetSelection())
-        for page in self.opened_pages.values():
+        for page in list(self.opened_pages.values()):
             # check if the selected data existing in the fitpanel
             pos = self.GetPageIndex(page)
@@ -592,5 +592,5 @@
         if selected_page in page_finder:
             # Delete the name of the page into the list of open page
-            for uid, list in self.opened_pages.iteritems():
+            for uid, list in self.opened_pages.items():
                 # Don't return any panel is the exact same page is created
                 if flag and selected_page.uid == uid:
@@ -600,5 +600,5 @@
 
         # Delete the name of the page into the list of open page
-        for uid, list in self.opened_pages.iteritems():
+        for uid, list in self.opened_pages.items():
             # Don't return any panel is the exact same page is created
             if selected_page.uid == uid:
Index: src/sas/sasgui/perspectives/fitting/fitproblem.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/fitproblem.py	(revision 251ef68424c8f373dfb82af3c159086491c5f975)
+++ src/sas/sasgui/perspectives/fitting/fitproblem.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -309,5 +309,5 @@
         self._smear_on = flag
         if fid is None:
-            for value in self.values():
+            for value in list(self.values()):
                 value.enable_smearing(flag)
         elif fid in self:
@@ -320,5 +320,5 @@
         """
         if fid is None:
-            for value in self.values():
+            for value in list(self.values()):
                 value.set_smearer(smearer)
         elif fid in self:
@@ -336,5 +336,5 @@
         """
         if fid is None:
-            for value in self.values():
+            for value in list(self.values()):
                 value.save_model_name(name)
         elif fid in self:
@@ -346,5 +346,5 @@
         result = []
         if fid is None:
-            for value in self.values():
+            for value in list(self.values()):
                 result.append(value.get_name())
         elif fid in self:
@@ -360,5 +360,5 @@
         self.model = model
         if fid is None:
-            for value in self.values():
+            for value in list(self.values()):
                 value.set_model(self.model)
         elif fid in self:
@@ -456,5 +456,5 @@
         """
         if fid is None:
-            for value in self.values():
+            for value in list(self.values()):
                 value.set_model_param(name, value)
         elif fid in self:
@@ -486,5 +486,5 @@
         """
         self.scheduled = schedule
-        for value in self.values():
+        for value in list(self.values()):
             value.schedule_tofit(schedule)
 
@@ -502,5 +502,5 @@
         self.qmax = qmax
         if fid is None:
-            for value in self.values():
+            for value in list(self.values()):
                 value.set_range(self.qmin, self.qmax)
         elif fid in self:
@@ -519,5 +519,5 @@
         """
         if fid is None:
-            for value in self.values():
+            for value in list(self.values()):
                 value.set_weight(flag=flag, is2d=is2d)
         elif fid in self:
@@ -536,5 +536,5 @@
         """
         if fid is None:
-            for value in self.values():
+            for value in list(self.values()):
                 value.clear_model_param()
         elif fid in self:
@@ -545,5 +545,5 @@
         return fitproblem contained in this dictionary
         """
-        return self.values()
+        return list(self.values())
 
     def set_result(self, result, fid):
Index: src/sas/sasgui/perspectives/fitting/fitting.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/fitting.py	(revision 9706d88b71e719f590147b9914149630bce2f1d2)
+++ src/sas/sasgui/perspectives/fitting/fitting.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -11,5 +11,5 @@
 #copyright 2009, University of Tennessee
 ################################################################################
-from __future__ import print_function
+
 
 import re
@@ -142,5 +142,5 @@
         Given an ID create a fitproblem container
         """
-        if page_id in self.page_finder.iterkeys():
+        if page_id in iter(self.page_finder.keys()):
             del self.page_finder[page_id]
 
@@ -335,5 +335,5 @@
             if temp:
                 # Set the new plugin model list for all fit pages
-                for uid, page in self.fit_panel.opened_pages.iteritems():
+                for uid, page in self.fit_panel.opened_pages.items():
                     if hasattr(page, "formfactorbox"):
                         page.model_list_box = temp
@@ -350,5 +350,5 @@
                                 page.formfactorbox.SetLabel(current_val)
         except:
-            logger.error("update_custom_combo: %s", sys.exc_value)
+            logger.error("update_custom_combo: %s", sys.exc_info()[1])
 
     def set_edit_menu(self, owner):
@@ -556,5 +556,5 @@
         else:
             if len(data_list) > MAX_NBR_DATA:
-                from fitting_widgets import DataDialog
+                from .fitting_widgets import DataDialog
                 dlg = DataDialog(data_list=data_list, nb_data=MAX_NBR_DATA)
                 if dlg.ShowModal() == wx.ID_OK:
@@ -576,5 +576,5 @@
                         self.add_fit_page(data=[data])
             except:
-                msg = "Fitting set_data: " + str(sys.exc_value)
+                msg = "Fitting set_data: " + str(sys.exc_info()[1])
                 wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
 
@@ -590,5 +590,5 @@
                 msg = "Fitting: cannot deal with the theory received"
                 evt = StatusEvent(status=msg, info="error")
-                logger.error("set_theory " + msg + "\n" + str(sys.exc_value))
+                logger.error("set_theory " + msg + "\n" + str(sys.exc_info()[1]))
                 wx.PostEvent(self.parent, evt)
 
@@ -602,5 +602,5 @@
         """
         from pagestate import PageState
-        from simfitpage import SimFitPageState
+        from .simfitpage import SimFitPageState
         if isinstance(state, PageState):
             state = state.clone()
@@ -713,5 +713,5 @@
         if fid is None:
             return
-        if uid in self.page_finder.keys():
+        if uid in list(self.page_finder.keys()):
             self.page_finder[uid].set_weight(flag=flag, is2d=is2d)
 
@@ -726,5 +726,5 @@
         :param qmax: maximum  value of the fit range
         """
-        if uid in self.page_finder.keys():
+        if uid in list(self.page_finder.keys()):
             self.page_finder[uid].set_range(qmin=qmin, qmax=qmax, fid=fid)
 
@@ -737,5 +737,5 @@
         :param uid: the id related to a page contaning fitting information
         """
-        if uid in self.page_finder.keys():
+        if uid in list(self.page_finder.keys()):
             self.page_finder[uid].schedule_tofit(value)
 
@@ -756,5 +756,5 @@
         """
         sim_page_id = self.sim_page.uid
-        for uid, value in self.page_finder.iteritems():
+        for uid, value in self.page_finder.items():
             if uid != sim_page_id and uid != self.batch_page.uid:
                 model_list = value.get_model()
@@ -821,5 +821,5 @@
         Stop the fit
         """
-        if uid in self.fit_thread_list.keys():
+        if uid in list(self.fit_thread_list.keys()):
             calc_fit = self.fit_thread_list[uid]
             if calc_fit is not  None and calc_fit.isrunning():
@@ -833,7 +833,7 @@
         batch_flag = self.batch_page is not None and uid == self.batch_page.uid
         if sim_flag or batch_flag:
-            for uid, value in self.page_finder.iteritems():
+            for uid, value in self.page_finder.items():
                 if value.get_scheduled() == 1:
-                    if uid in self.fit_panel.opened_pages.keys():
+                    if uid in list(self.fit_panel.opened_pages.keys()):
                         panel = self.fit_panel.opened_pages[uid]
                         panel._on_fit_complete()
@@ -852,5 +852,5 @@
         :param draw: Determine if the theory needs to be plot
         """
-        if uid not in self.page_finder.keys():
+        if uid not in list(self.page_finder.keys()):
             return
         self.page_finder[uid].enable_smearing(flag=enable_smearer)
@@ -964,5 +964,5 @@
         list_page_id = []
         fit_id = 0
-        for page_id, page_info in self.page_finder.iteritems():
+        for page_id, page_info in self.page_finder.items():
             # For simulfit (uid give with None), do for-loop
             # if uid is specified (singlefit), do it only on the page.
@@ -991,5 +991,5 @@
 
                     pars = [str(element[1]) for element in page.param_toFit]
-                    fitproblem_list = page_info.values()
+                    fitproblem_list = list(page_info.values())
                     for fitproblem in  fitproblem_list:
                         if sim_fitter is None:
@@ -1013,5 +1013,5 @@
             except:
                 raise
-                msg = "Fitting error: %s" % str(sys.exc_value)
+                msg = "Fitting error: %s" % str(sys.exc_info()[1])
                 evt = StatusEvent(status=msg, info="error", type="stop")
                 wx.PostEvent(self.parent, evt)
@@ -1070,5 +1070,5 @@
         :param fid: the id of the fitproblem(data, model, range,etc)
         """
-        if uid not in self.page_finder.keys():
+        if uid not in list(self.page_finder.keys()):
             return
         fitproblemList = self.page_finder[uid].get_fit_problem(fid)
@@ -1116,5 +1116,5 @@
                 wx.PostEvent(self.parent, evt)
         except:
-            msg = "Creating Fit page: %s" % sys.exc_value
+            msg = "Creating Fit page: %s" % sys.exc_info()[1]
             wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
 
@@ -1196,9 +1196,9 @@
         # case that uid is not specified
         if uid is None:
-            for page_id in self.page_finder.keys():
+            for page_id in list(self.page_finder.keys()):
                 self.page_finder[page_id].schedule_tofit(value)
         # when uid is given
         else:
-            if uid in self.page_finder.keys():
+            if uid in list(self.page_finder.keys()):
                 self.page_finder[uid].schedule_tofit(value)
 
@@ -1234,5 +1234,5 @@
         panel = self.plot_panel
         if panel is None:
-            raise ValueError, "Fitting:_onSelect: NonType panel"
+            raise ValueError("Fitting:_onSelect: NonType panel")
         Plugin.on_perspective(self, event=event)
         self.select_data(panel)
@@ -1267,5 +1267,5 @@
         """
         uid = page_id[0]
-        if uid in self.fit_thread_list.keys():
+        if uid in list(self.fit_thread_list.keys()):
             del self.fit_thread_list[uid]
 
@@ -1293,16 +1293,16 @@
                 #get all fittable parameters of the current model
                 for param in  model.getParamList():
-                    if param  not in batch_outputs.keys():
+                    if param  not in list(batch_outputs.keys()):
                         batch_outputs[param] = []
                 for param in model.getDispParamList():
                     if not model.is_fittable(param) and \
-                        param in batch_outputs.keys():
+                        param in list(batch_outputs.keys()):
                         del batch_outputs[param]
                 # Add fitted parameters and their error
                 for param in res.param_list:
-                    if param not in batch_outputs.keys():
+                    if param not in list(batch_outputs.keys()):
                         batch_outputs[param] = []
                     err_param = "error on %s" % str(param)
-                    if err_param not in batch_inputs.keys():
+                    if err_param not in list(batch_inputs.keys()):
                         batch_inputs[err_param] = []
         msg = ""
@@ -1389,5 +1389,5 @@
                 #model
                 EMPTY = "-"
-                for key in batch_outputs.keys():
+                for key in list(batch_outputs.keys()):
                     if key not in param_list and key not in ["Chi2", "Data"]:
                         batch_outputs[key].append(EMPTY)
@@ -1432,5 +1432,5 @@
         tbatch_outputs = {}
         shownkeystr = cpage.get_copy_params()
-        for key in batch_outputs.keys():
+        for key in list(batch_outputs.keys()):
             if key in ["Chi2", "Data"] or shownkeystr.count(key) > 0:
                 tbatch_outputs[key] = batch_outputs[key]
@@ -1452,5 +1452,5 @@
         model = fitproblem.get_model()
         #fill batch result information
-        if "Data" not in batch_outputs.keys():
+        if "Data" not in list(batch_outputs.keys()):
             batch_outputs["Data"] = []
         from sas.sasgui.guiframe.data_processor import BatchCell
@@ -1482,6 +1482,6 @@
         cell.object = [data, theory_data]
         batch_outputs["Data"].append(cell)
-        for key, value in data.meta_data.iteritems():
-            if key not in batch_inputs.keys():
+        for key, value in data.meta_data.items():
+            if key not in list(batch_inputs.keys()):
                 batch_inputs[key] = []
             #if key.lower().strip() != "loader":
@@ -1489,5 +1489,5 @@
         param = "temperature"
         if hasattr(data.sample, param):
-            if param not in  batch_inputs.keys():
+            if param not in  list(batch_inputs.keys()):
                 batch_inputs[param] = []
             batch_inputs[param].append(data.sample.temperature)
@@ -1562,5 +1562,5 @@
         except:
             msg = ("Fit completed but the following error occurred: %s"
-                   % sys.exc_value)
+                   % sys.exc_info()[1])
             #import traceback; msg = "\n".join((traceback.format_exc(), msg))
             evt = StatusEvent(status=msg, info="warning", type="stop")
@@ -1650,5 +1650,5 @@
         if model is None:
             return
-        if uid not in self.page_finder.keys():
+        if uid not in list(self.page_finder.keys()):
             return
         # save the name containing the data name with the appropriate model
@@ -1903,5 +1903,5 @@
             return None
         try:
-            from model_thread import Calc2D
+            from .model_thread import Calc2D
             ## If a thread is already started, stop it
             if (self.calc_2D is not None) and self.calc_2D.isrunning():
@@ -1950,5 +1950,5 @@
             return
         try:
-            from model_thread import Calc1D
+            from .model_thread import Calc1D
             ## If a thread is already started, stop it
             if (self.calc_1D is not None) and self.calc_1D.isrunning():
@@ -1989,5 +1989,5 @@
         except:
             msg = " Error occurred when drawing %s Model 1D: " % model.name
-            msg += " %s" % sys.exc_value
+            msg += " %s" % sys.exc_info()[1]
             wx.PostEvent(self.parent, StatusEvent(status=msg))
 
Index: src/sas/sasgui/perspectives/fitting/model_thread.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/model_thread.py	(revision 0f9ea1cee80a92f71e4bec65998ca56a3de65fd6)
+++ src/sas/sasgui/perspectives/fitting/model_thread.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -65,5 +65,5 @@
         if self.data is None:
             msg = "Compute Calc2D receive data = %s.\n" % str(self.data)
-            raise ValueError, msg
+            raise ValueError(msg)
 
         # Define matrix where data will be plotted
Index: src/sas/sasgui/perspectives/fitting/simfitpage.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/simfitpage.py	(revision 0a3c740de7a24dad98e912cdeee179e382e3839a)
+++ src/sas/sasgui/perspectives/fitting/simfitpage.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -168,5 +168,5 @@
         i = 0
         for model in self.model_list:
-            model_id = self._format_id(model[1].keys()[0])
+            model_id = self._format_id(list(model[1].keys())[0])
             for saved_model in sim_state.model_list:
                 save_id = saved_model.pop('name')
@@ -195,8 +195,8 @@
                 param = item.pop('param_cbox')
                 equality = item.pop('egal_txt')
-                for key, value in init_map.items():
+                for key, value in list(init_map.items()):
                     model_cbox = model_cbox.replace(key, value)
                     constraint_value = constraint_value.replace(key, value)
-                for key, value in final_map.items():
+                for key, value in list(final_map.items()):
                     model_cbox = model_cbox.replace(key, value)
                     constraint_value = constraint_value.replace(key, value)
@@ -328,5 +328,5 @@
         sizer.Add(tab_used, (iy, ix), (1, 1),
                   wx.EXPAND | wx.ADJUST_MINSIZE, 0)
-        for id, value in self.page_finder.iteritems():
+        for id, value in self.page_finder.items():
             if id not in self.parent.opened_pages:
                 continue
@@ -757,10 +757,10 @@
         self.set_button.Disable()
 
-        for id, model in self.constraint_dict.iteritems():
+        for id, model in self.constraint_dict.items():
             # check if all parameters have been selected for constraint
             # then do not allow add constraint on parameters
             self.model_cbox_left.Append(str(model.name), model)
         self.model_cbox_left.Select(0)
-        for id, model in self.constraint_dict.iteritems():
+        for id, model in self.constraint_dict.items():
             # check if all parameters have been selected for constraint
             # then do not allow add constraint on parameters
@@ -814,5 +814,5 @@
         model_right = self.model_cbox_right.GetValue()
         model_b = self.model_cbox_right.GetClientData(selection_b)
-        for id, dic_model in self.constraint_dict.iteritems():
+        for id, dic_model in self.constraint_dict.items():
             if model == dic_model:
                 param_list = self.page_finder[id].get_param2fit()
@@ -857,5 +857,5 @@
         if len(self.constraints_list) != 0:
             nb_fit_param = 0
-            for id, model in self.constraint_dict.iteritems():
+            for id, model in self.constraint_dict.items():
                 nb_fit_param += len(self.page_finder[id].get_param2fit())
             # Don't add anymore
@@ -879,5 +879,5 @@
         model_cbox = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_READONLY)
         model_cbox.Clear()
-        for id, model in self.constraint_dict.iteritems():
+        for id, model in self.constraint_dict.items():
             # check if all parameters have been selected for constraint
             # then do not allow add constraint on parameters
@@ -898,5 +898,5 @@
         # Remove button
         #btRemove = wx.Button(self, self.ID_REMOVE, 'Remove')
-        bt_remove = wx.Button(self, self._ids.next(), 'Remove')
+        bt_remove = wx.Button(self, next(self._ids), 'Remove')
         bt_remove.Bind(wx.EVT_BUTTON, self.on_remove,
                       id=bt_remove.GetId())
@@ -931,5 +931,5 @@
         hide buttons related constraint
         """
-        for id in self.page_finder.iterkeys():
+        for id in self.page_finder.keys():
             self.page_finder[id].clear_model_param()
 
@@ -969,5 +969,5 @@
         model = model_cbox.GetClientData(n)
         param_list = []
-        for id, dic_model in self.constraint_dict.iteritems():
+        for id, dic_model in self.constraint_dict.items():
             if model == dic_model:
                 param_list = self.page_finder[id].get_param2fit()
@@ -1056,5 +1056,5 @@
                 msg += " in combobox to set constraint! "
                 wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
-            for id, value in self.constraint_dict.iteritems():
+            for id, value in self.constraint_dict.items():
                 if model == value:
                     if constraint == "":
@@ -1080,5 +1080,5 @@
                         return False
 
-                    for fid in self.page_finder[id].iterkeys():
+                    for fid in self.page_finder[id].keys():
                         # wrap in param/constraint in str() to remove unicode
                         self.page_finder[id].set_model_param(str(param),
Index: src/sas/sasgui/perspectives/invariant/__init__.py
===================================================================
--- src/sas/sasgui/perspectives/invariant/__init__.py	(revision 5a405bda2da663894b6b32ab94768a91d60525a4)
+++ src/sas/sasgui/perspectives/invariant/__init__.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -3,5 +3,5 @@
 
 from distutils.filelist import findall
-from invariant import *
+from .invariant import *
 
 def get_data_path(media):
Index: src/sas/sasgui/perspectives/invariant/invariant.py
===================================================================
--- src/sas/sasgui/perspectives/invariant/invariant.py	(revision 7432acb919f4013119ca918a73350297e0d4c93f)
+++ src/sas/sasgui/perspectives/invariant/invariant.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -133,5 +133,5 @@
             name = data.__class__.__name__
             msg = "Invariant use only Data1D got: [%s] " % str(name)
-            raise ValueError, msg
+            raise ValueError(msg)
         self.compute_helper(data=data)
 
@@ -169,5 +169,5 @@
                 msg += "Please select one.\n"
                 if len(data_list) > 1:
-                    from invariant_widgets import DataDialog
+                    from .invariant_widgets import DataDialog
                     dlg = DataDialog(data_list=data_1d_list, text=msg)
                     if dlg.ShowModal() == wx.ID_OK:
@@ -191,5 +191,5 @@
                     self.compute_helper(data)
                 except:
-                    msg = "Invariant Set_data: " + str(sys.exc_value)
+                    msg = "Invariant Set_data: " + str(sys.exc_info()[1])
                     wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
         else:
@@ -240,5 +240,5 @@
             msg = "invariant.save_file: the data being saved is"
             msg += " not a sas.sascalc.dataloader.data_info.Data1D object"
-            raise RuntimeError, msg
+            raise RuntimeError(msg)
 
     def set_state(self, state=None, datainfo=None):
@@ -258,5 +258,5 @@
                 msg = "invariant.set_state: datainfo parameter cannot"
                 msg += " be None in standalone mode"
-                raise RuntimeError, msg
+                raise RuntimeError(msg)
             # Make sure the user sees the invariant panel after loading
             # self.parent.set_perspective(self.perspective)
@@ -282,5 +282,5 @@
 
         except:
-            logger.error("invariant.set_state: %s" % sys.exc_value)
+            logger.error("invariant.set_state: %s" % sys.exc_info()[1])
 
     def on_set_state_helper(self, event=None):
@@ -320,5 +320,5 @@
         else:
             msg = "Scale can not be zero."
-            raise ValueError, msg
+            raise ValueError(msg)
         if len(new_plot.x) == 0:
             return
Index: src/sas/sasgui/perspectives/invariant/invariant_details.py
===================================================================
--- src/sas/sasgui/perspectives/invariant/invariant_details.py	(revision 959eb0181ad06828a748f04512f953aad5b31659)
+++ src/sas/sasgui/perspectives/invariant/invariant_details.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -6,5 +6,5 @@
 
 from sas.sasgui.guiframe.utils import format_number
-from invariant_widgets import OutputTextCtrl
+from .invariant_widgets import OutputTextCtrl
 # Dimensions related to chart
 RECTANGLE_WIDTH = 400.0
Index: src/sas/sasgui/perspectives/invariant/invariant_panel.py
===================================================================
--- src/sas/sasgui/perspectives/invariant/invariant_panel.py	(revision 7432acb919f4013119ca918a73350297e0d4c93f)
+++ src/sas/sasgui/perspectives/invariant/invariant_panel.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -318,10 +318,10 @@
         background = self.background_tcl.GetValue().lstrip().rstrip()
         if background == "":
-            raise ValueError, "Need a background"
+            raise ValueError("Need a background")
         if check_float(self.background_tcl):
             return float(background)
         else:
             msg = "Receive invalid value for background : %s" % (background)
-            raise ValueError, msg
+            raise ValueError(msg)
 
     def get_scale(self):
@@ -331,5 +331,5 @@
         scale = self.scale_tcl.GetValue().lstrip().rstrip()
         if scale == "":
-            raise ValueError, "Need a background"
+            raise ValueError("Need a background")
         if check_float(self.scale_tcl):
             if float(scale) <= 0.0:
@@ -337,8 +337,8 @@
                 self.scale_tcl.Refresh()
                 msg = "Receive invalid value for scale: %s" % (scale)
-                raise ValueError, msg
+                raise ValueError(msg)
             return float(scale)
         else:
-            raise ValueError, "Receive invalid value for scale : %s" % (scale)
+            raise ValueError("Receive invalid value for scale : %s" % (scale))
 
     def get_contrast(self):
@@ -389,5 +389,5 @@
                 self.volume_err_tcl.SetValue(format_number(None))
                 msg = "Error occurred computing volume "
-                msg += " fraction: %s" % sys.exc_value
+                msg += " fraction: %s" % sys.exc_info()[1]
                 wx.PostEvent(self.parent, StatusEvent(status=msg,
                                                       info="error",
@@ -409,5 +409,5 @@
                 self.surface_err_tcl.SetValue(format_number(None))
                 msg = "Error occurred computing "
-                msg += "specific surface: %s" % sys.exc_value
+                msg += "specific surface: %s" % sys.exc_info()[1]
                 wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
                                                       type="stop"))
@@ -431,5 +431,5 @@
             self.invariant_total_err_tcl.SetValue(format_number(None))
             msg = "Error occurred computing invariant using"
-            msg += " extrapolation: %s" % sys.exc_value
+            msg += " extrapolation: %s" % sys.exc_info()[1]
             wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
 
@@ -454,5 +454,5 @@
                 self._manager.plot_theory(name="Low-Q extrapolation")
                 msg = "Error occurred computing low-Q "
-                msg += "invariant: %s" % sys.exc_value
+                msg += "invariant: %s" % sys.exc_info()[1]
                 wx.PostEvent(self.parent,
                              StatusEvent(status=msg, type="stop"))
@@ -462,5 +462,5 @@
                 self._manager.plot_theory(name="Low-Q extrapolation")
             except:
-                logger.error(sys.exc_value)
+                logger.error(sys.exc_info()[1])
 
     def get_high_qstar(self, inv, high_q=False):
@@ -488,5 +488,5 @@
                 self._manager.plot_theory(name="High-Q extrapolation")
                 msg = "Error occurred computing high-Q "
-                msg += "invariant: %s" % sys.exc_value
+                msg += "invariant: %s" % sys.exc_info()[1]
                 wx.PostEvent(self.parent, StatusEvent(status=msg,
                                                       type="stop"))
@@ -496,5 +496,5 @@
                 self._manager.plot_theory(name="High-Q extrapolation")
             except:
-                logger.error(sys.exc_value)
+                logger.error(sys.exc_info()[1])
 
     def get_qstar(self, inv):
@@ -625,5 +625,5 @@
             scale = self.get_scale()
         except:
-            msg = "Invariant Error: %s" % (sys.exc_value)
+            msg = "Invariant Error: %s" % (sys.exc_info()[1])
             wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
             return
@@ -641,5 +641,5 @@
             inv, npts_high = self.set_extrapolation_high(inv=inv, high_q=high_q)
         except:
-            msg = "Error occurred computing invariant: %s" % sys.exc_value
+            msg = "Error occurred computing invariant: %s" % sys.exc_info()[1]
             wx.PostEvent(self.parent, StatusEvent(status=msg,
                                                   info="warning", type="stop"))
@@ -652,5 +652,5 @@
             self.get_qstar(inv=inv)
         except:
-            msg = "Error occurred computing invariant: %s" % sys.exc_value
+            msg = "Error occurred computing invariant: %s" % sys.exc_info()[1]
             wx.PostEvent(self.parent, StatusEvent(status=msg,
                                                   info="warning",
@@ -675,5 +675,5 @@
         except:
             msg = r_msg + "Error occurred computing invariant: %s" % \
-                                                            sys.exc_value
+                                                            sys.exc_info()[1]
             wx.PostEvent(self.parent, StatusEvent(status=msg,
                                                   info="error",
@@ -685,5 +685,5 @@
             #compute surface and set value to txtcrtl
         except:
-            msg = "Error occurred computing invariant: %s" % sys.exc_value
+            msg = "Error occurred computing invariant: %s" % sys.exc_info()[1]
             wx.PostEvent(self.parent, StatusEvent(status=msg,
                                                   info="warning",
@@ -695,5 +695,5 @@
 
         except:
-            msg = "Error occurred computing invariant: %s" % sys.exc_value
+            msg = "Error occurred computing invariant: %s" % sys.exc_info()[1]
             wx.PostEvent(self.parent, StatusEvent(status=msg,
                                                   info="warning",
@@ -847,5 +847,5 @@
             attr.SetValue(value)
         except:
-            logger.error("Invariant state: %s", sys.exc_value)
+            logger.error("Invariant state: %s", sys.exc_info()[1])
 
     def get_bookmark_by_num(self, num=None):
@@ -864,6 +864,6 @@
             _, _, current_state, comp_state = self.state.bookmark_list[int(num)]
         except:
-            logger.error(sys.exc_value)
-            raise ValueError, "No such bookmark exists"
+            logger.error(sys.exc_info()[1])
+            raise ValueError("No such bookmark exists")
 
         # set the parameters
@@ -959,5 +959,5 @@
                     self.state.clone_state()
         except:
-            logger.error(sys.exc_value)
+            logger.error(sys.exc_info()[1])
 
         self._set_undo_flag(True)
@@ -1003,5 +1003,5 @@
                 del self.state.state_list[str(i)]
             except:
-                logger.error(sys.exc_value)
+                logger.error(sys.exc_info()[1])
         # Enable the undo button if it was not
         self._set_undo_flag(True)
@@ -1068,5 +1068,5 @@
                 del self.state.state_list[str(i)]
             except:
-                logger.error(sys.exc_value)
+                logger.error(sys.exc_info()[1])
 
         # try to add new state of the text changes in the state_list
@@ -1083,5 +1083,5 @@
             self.state.state_list[str(self.state.state_num)] = self.state.clone_state()
         except:
-            logger.error(sys.exc_value)
+            logger.error(sys.exc_info()[1])
 
         self._set_undo_flag(True)
@@ -1105,5 +1105,5 @@
             self.state.state_list[str(self.state.state_num)] = self.state.clone_state()
         except:
-            logger.error(sys.exc_value)
+            logger.error(sys.exc_info()[1])
 
     def _get_input_list(self):
@@ -1114,6 +1114,6 @@
         compute_num = self.state.saved_state['compute_num']
         # find values and put into the input list
-        for key1, value1 in self.state.state_list[str(compute_num)].iteritems():
-            for key, _ in self.state.input_list.iteritems():
+        for key1, value1 in self.state.state_list[str(compute_num)].items():
+            for key, _ in self.state.input_list.items():
                 if key == key1:
                     self.state.input_list[key] = value1
Index: src/sas/sasgui/perspectives/invariant/invariant_state.py
===================================================================
--- src/sas/sasgui/perspectives/invariant/invariant_state.py	(revision 1fa4f7368e26981352615cbfb337a581ed97031f)
+++ src/sas/sasgui/perspectives/invariant/invariant_state.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -137,5 +137,5 @@
 
         # text ctl general inputs ( excluding extrapolation text ctl)
-        for key, value in self.input_list.iteritems():
+        for key, value in self.input_list.items():
             if value == '':
                 continue
@@ -163,5 +163,5 @@
         low_off = False
         high_off = False
-        for key, value in self.input_list.iteritems():
+        for key, value in self.input_list.items():
             key_split = key.split('_')
             max_ind = len(key_split) - 1
@@ -213,5 +213,5 @@
                 # other outputs than Q*
                 name = item[0] + "_tcl"
-                if name in self.saved_state.keys():
+                if name in list(self.saved_state.keys()):
                     value = self.saved_state[name]
 
@@ -298,5 +298,5 @@
         top_element.appendChild(state)
 
-        for name, value in self.saved_state.iteritems():
+        for name, value in self.saved_state.items():
             element = newdoc.createElement(str(name))
             element.appendChild(newdoc.createTextNode(str(value)))
@@ -307,7 +307,7 @@
         top_element.appendChild(history)
 
-        for name, value in self.state_list.iteritems():
+        for name, value in self.state_list.items():
             history_element = newdoc.createElement('state_' + str(name))
-            for state_name, state_value in value.iteritems():
+            for state_name, state_value in value.items():
                 state_element = newdoc.createElement(str(state_name))
                 child = newdoc.createTextNode(str(state_value))
@@ -322,5 +322,5 @@
         top_element.appendChild(bookmark)
         item_list = ['time', 'date', 'state', 'comp_state']
-        for name, value_list in self.bookmark_list.iteritems():
+        for name, value_list in self.bookmark_list.items():
             element = newdoc.createElement('mark_' + str(name))
             _, date, state, comp_state = value_list
@@ -331,10 +331,10 @@
             state_list_element = newdoc.createElement('state')
             comp_state_list_element = newdoc.createElement('comp_state')
-            for state_name, state_value in value_list[2].iteritems():
+            for state_name, state_value in value_list[2].items():
                 state_element = newdoc.createElement(str(state_name))
                 child = newdoc.createTextNode(str(state_value))
                 state_element.appendChild(child)
                 state_list_element.appendChild(state_element)
-            for comp_name, comp_value in value_list[3].iteritems():
+            for comp_name, comp_value in value_list[3].items():
                 comp_element = newdoc.createElement(str(comp_name))
                 comp_element.appendChild(newdoc.createTextNode(str(comp_value)))
@@ -365,5 +365,5 @@
             msg = "InvariantSate no longer supports non-CanSAS"
             msg += " format for invariant files"
-            raise RuntimeError, msg
+            raise RuntimeError(msg)
 
         if node.get('version')\
@@ -382,5 +382,5 @@
                 except:
                     msg = "InvariantSate.fromXML: Could not read"
-                    msg += " timestamp\n %s" % sys.exc_value
+                    msg += " timestamp\n %s" % sys.exc_info()[1]
                     logger.error(msg)
 
@@ -454,5 +454,5 @@
         # default string values
         for num in range(1, 19):
-            exec "s_%s = 'NA'" % str(num)
+            exec("s_%s = 'NA'" % str(num))
         lines = strings.split('\n')
         # get all string values from __str__()
@@ -695,5 +695,5 @@
         except:
             msg = "XML document does not contain invariant"
-            msg += " information.\n %s" % sys.exc_value
+            msg += " information.\n %s" % sys.exc_info()[1]
             logger.info(msg)
         return state
@@ -737,5 +737,5 @@
                         output.append(sas_entry)
         else:
-            raise RuntimeError, "%s is not a file" % path
+            raise RuntimeError("%s is not a file" % path)
 
         # Return output consistent with the loader's api
@@ -783,5 +783,5 @@
             msg = "The cansas writer expects a Data1D"
             msg += " instance: %s" % str(datainfo.__class__.__name__)
-            raise RuntimeError, msg
+            raise RuntimeError(msg)
         # make sure title and data run is filled up.
         if datainfo.title is None or datainfo.title == '':
Index: src/sas/sasgui/perspectives/invariant/report_dialog.py
===================================================================
--- src/sas/sasgui/perspectives/invariant/report_dialog.py	(revision 959eb0181ad06828a748f04512f953aad5b31659)
+++ src/sas/sasgui/perspectives/invariant/report_dialog.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -94,5 +94,5 @@
                     except:
                         # DO not open
-                        logger.error("Could not open file: %s" % sys.exc_value)
+                        logger.error("Could not open file: %s" % sys.exc_info()[1])
             # delete image file
             os.remove(pic_fname)
Index: src/sas/sasgui/perspectives/pr/__init__.py
===================================================================
--- src/sas/sasgui/perspectives/pr/__init__.py	(revision 959eb0181ad06828a748f04512f953aad5b31659)
+++ src/sas/sasgui/perspectives/pr/__init__.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -1,2 +1,2 @@
 PLUGIN_ID = "P(r) plug-in 1.0"
-from pr import *
+from .pr import *
Index: src/sas/sasgui/perspectives/pr/explore_dialog.py
===================================================================
--- src/sas/sasgui/perspectives/pr/explore_dialog.py	(revision 959eb0181ad06828a748f04512f953aad5b31659)
+++ src/sas/sasgui/perspectives/pr/explore_dialog.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -35,5 +35,5 @@
 from sas.sasgui.plottools.plottables import Graph
 
-from pr_widgets import PrTextCtrl
+from .pr_widgets import PrTextCtrl
 
 # Default number of points on the output plot
@@ -331,5 +331,5 @@
         selection_msg = wx.StaticText(self, -1, "Select a dependent variable:")
         self.output_box = wx.ComboBox(self, -1, style=wx.CB_READONLY)
-        for item in self.results.outputs.keys():
+        for item in list(self.results.outputs.keys()):
             self.output_box.Append(item, "")
         self.output_box.SetStringSelection(DEFAULT_OUTPUT)
@@ -419,5 +419,5 @@
                 # This inversion failed, skip this D_max value
                 msg = "ExploreDialog: inversion failed "
-                msg += "for D_max=%s\n%s" % (str(d), sys.exc_value)
+                msg += "for D_max=%s\n%s" % (str(d), sys.exc_info()[1])
                 logger.error(msg)
 
Index: src/sas/sasgui/perspectives/pr/inversion_panel.py
===================================================================
--- src/sas/sasgui/perspectives/pr/inversion_panel.py	(revision cb62bd55ae79395ac6e01d8b2077f4d496625c41)
+++ src/sas/sasgui/perspectives/pr/inversion_panel.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -12,8 +12,8 @@
 from sas.sasgui.guiframe.events import StatusEvent
 from sas.sasgui.guiframe.panel_base import PanelBase
-from inversion_state import InversionState
-from pr_widgets import PrTextCtrl
-from pr_widgets import DataFileTextCtrl
-from pr_widgets import OutputTextCtrl
+from .inversion_state import InversionState
+from .pr_widgets import PrTextCtrl
+from .pr_widgets import DataFileTextCtrl
+from .pr_widgets import OutputTextCtrl
 from sas.sasgui.guiframe.documentation_window import DocumentationWindow
 
@@ -754,5 +754,5 @@
         except:
             # No estimate or bad estimate, either do nothing
-            logger.error("InversionControl._on_accept_alpha: %s" % sys.exc_value)
+            logger.error("InversionControl._on_accept_alpha: %s" % sys.exc_info()[1])
 
     def _on_accept_nterms(self, evt):
@@ -770,5 +770,5 @@
         except:
             # No estimate or bad estimate, either do nothing
-            logger.error("InversionControl._on_accept_nterms: %s" % sys.exc_value)
+            logger.error("InversionControl._on_accept_nterms: %s" % sys.exc_info()[1])
 
     def clear_panel(self):
@@ -901,5 +901,5 @@
                 message += "than the number of points"
                 wx.PostEvent(self._manager.parent, StatusEvent(status=message))
-                raise ValueError, message
+                raise ValueError(message)
             self.nfunc_ctl.SetBackgroundColour(wx.WHITE)
             self.nfunc_ctl.Refresh()
@@ -957,5 +957,5 @@
         Invoke the d_max exploration dialog
         """
-        from explore_dialog import ExploreDialog
+        from .explore_dialog import ExploreDialog
         if self._manager._last_pr is not None:
             pr = self._manager._create_plot_pr()
@@ -1009,5 +1009,5 @@
                 self._set_analysis(True)
             except:
-                msg = "InversionControl._change_file: %s" % sys.exc_value
+                msg = "InversionControl._change_file: %s" % sys.exc_info()[1]
                 logger.error(msg)
 
Index: src/sas/sasgui/perspectives/pr/inversion_state.py
===================================================================
--- src/sas/sasgui/perspectives/pr/inversion_state.py	(revision 1fa4f7368e26981352615cbfb337a581ed97031f)
+++ src/sas/sasgui/perspectives/pr/inversion_state.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -238,5 +238,5 @@
             msg = "InversionState no longer supports non-CanSAS"
             msg += " format for P(r) files"
-            raise RuntimeError, msg
+            raise RuntimeError(msg)
 
         if node.get('version') and node.get('version') == '1.0':
@@ -254,5 +254,5 @@
                 except:
                     msg = "InversionState.fromXML: Could not read "
-                    msg += "timestamp\n %s" % sys.exc_value
+                    msg += "timestamp\n %s" % sys.exc_info()[1]
                     logger.error(msg)
 
@@ -434,5 +434,5 @@
         except:
             msg = "XML document does not contain P(r) "
-            msg += "information.\n %s" % sys.exc_value
+            msg += "information.\n %s" % sys.exc_info()[1]
             logger.info(msg)
 
@@ -481,5 +481,5 @@
                         output.append(sas_entry)
         else:
-            raise RuntimeError, "%s is not a file" % path
+            raise RuntimeError("%s is not a file" % path)
 
         # Return output consistent with the loader's api
@@ -525,5 +525,5 @@
             msg = "The cansas writer expects a Data1D "
             msg += "instance: %s" % str(datainfo.__class__.__name__)
-            raise RuntimeError, msg
+            raise RuntimeError(msg)
 
         # Create basic XML document
Index: src/sas/sasgui/perspectives/pr/pr.py
===================================================================
--- src/sas/sasgui/perspectives/pr/pr.py	(revision cb62bd55ae79395ac6e01d8b2077f4d496625c41)
+++ src/sas/sasgui/perspectives/pr/pr.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -15,5 +15,5 @@
 # Make sure the option of saving each curve is available
 # Use the I(q) curve as input and compare the output to P(r)
-from __future__ import print_function
+
 
 import sys
@@ -33,5 +33,5 @@
 import sas.sascalc.dataloader
 
-from pr_widgets import load_error
+from .pr_widgets import load_error
 from sas.sasgui.guiframe.plugin_base import PluginBase
 
@@ -107,5 +107,5 @@
 
         # Associate the inversion state reader with .prv files
-        from inversion_state import Reader
+        from .inversion_state import Reader
 
         # Create a CanSAS/Pr reader
@@ -151,5 +151,5 @@
                 msg = "Pr.set_state: datainfo parameter cannot "
                 msg += "be None in standalone mode"
-                raise RuntimeError, msg
+                raise RuntimeError(msg)
 
             # Ensuring that plots are coordinated correctly
@@ -185,5 +185,5 @@
             self.control_panel.set_state(state)
         except:
-            logger.error("prview.set_state: %s" % sys.exc_value)
+            logger.error("prview.set_state: %s" % sys.exc_info()[1])
 
 
@@ -195,5 +195,5 @@
 
         """
-        from inversion_panel import HelpDialog
+        from .inversion_panel import HelpDialog
         dialog = HelpDialog(None, -1)
         if dialog.ShowModal() == wx.ID_OK:
@@ -369,5 +369,5 @@
         Redisplay P(r) with a different number of points
         """
-        from inversion_panel import PrDistDialog
+        from .inversion_panel import PrDistDialog
         dialog = PrDistDialog(None, -1)
         dialog.set_content(self._pr_npts)
@@ -452,5 +452,5 @@
         # Notify the user if we could not read the file
         if dataread is None:
-            raise RuntimeError, "Invalid data"
+            raise RuntimeError("Invalid data")
 
         x = None
@@ -472,5 +472,5 @@
                 if dataread is None:
                     return x, y, err
-                raise RuntimeError, "This tool can only read 1D data"
+                raise RuntimeError("This tool can only read 1D data")
 
         self._current_file_data.x = x
@@ -512,5 +512,5 @@
                     data_err = np.append(data_err, err)
                 except:
-                    logger.error(sys.exc_value)
+                    logger.error(sys.exc_info()[1])
 
         if scale is not None:
@@ -563,5 +563,5 @@
                         data_err = np.append(data_err, err)
                     except:
-                        logger.error(sys.exc_value)
+                        logger.error(sys.exc_info()[1])
                 elif line.find("The 6 columns") >= 0:
                     data_started = True
@@ -720,5 +720,5 @@
             Start a calculation thread
         """
-        from pr_thread import CalcPr
+        from .pr_thread import CalcPr
 
         # If a thread is already started, stop it
@@ -850,11 +850,11 @@
                 pr = self._create_file_pr(data)
             except:
-                status = "Problem reading data: %s" % sys.exc_value
+                status = "Problem reading data: %s" % sys.exc_info()[1]
                 wx.PostEvent(self.parent, StatusEvent(status=status))
-                raise RuntimeError, status
+                raise RuntimeError(status)
 
             # If the file contains nothing, just return
             if pr is None:
-                raise RuntimeError, "Loaded data is invalid"
+                raise RuntimeError("Loaded data is invalid")
 
             self.pr = pr
@@ -906,5 +906,5 @@
             msg = "pr.save_data: the data being saved is not a"
             msg += " sas.data_info.Data1D object"
-            raise RuntimeError, msg
+            raise RuntimeError(msg)
 
     def setup_plot_inversion(self, alpha, nfunc, d_max, q_min=None, q_max=None,
@@ -929,5 +929,5 @@
                 self.perform_inversion()
         except:
-            wx.PostEvent(self.parent, StatusEvent(status=sys.exc_value))
+            wx.PostEvent(self.parent, StatusEvent(status=sys.exc_info()[1]))
 
     def estimate_plot_inversion(self, alpha, nfunc, d_max,
@@ -953,5 +953,5 @@
                 self.perform_estimate()
         except:
-            wx.PostEvent(self.parent, StatusEvent(status=sys.exc_value))
+            wx.PostEvent(self.parent, StatusEvent(status=sys.exc_info()[1]))
 
     def _create_plot_pr(self, estimate=False):
@@ -1034,5 +1034,5 @@
                 self.perform_inversion()
         except:
-            wx.PostEvent(self.parent, StatusEvent(status=sys.exc_value))
+            wx.PostEvent(self.parent, StatusEvent(status=sys.exc_info()[1]))
 
     def estimate_file_inversion(self, alpha, nfunc, d_max, data,
@@ -1057,5 +1057,5 @@
                 self.perform_estimate()
         except:
-            wx.PostEvent(self.parent, StatusEvent(status=sys.exc_value))
+            wx.PostEvent(self.parent, StatusEvent(status=sys.exc_info()[1]))
 
     def _create_file_pr(self, data):
@@ -1086,5 +1086,5 @@
             x, y, err = data.x, data.y, data.dy
         except:
-            load_error(sys.exc_value)
+            load_error(sys.exc_info()[1])
             return None
 
@@ -1125,5 +1125,5 @@
             return pr
         except:
-            load_error(sys.exc_value)
+            load_error(sys.exc_info()[1])
         return None
 
@@ -1132,5 +1132,5 @@
             Perform parameter estimation
         """
-        from pr_thread import EstimatePr
+        from .pr_thread import EstimatePr
 
         # If a thread is already started, stop it
@@ -1162,5 +1162,5 @@
             Perform parameter estimation
         """
-        from pr_thread import EstimateNT
+        from .pr_thread import EstimateNT
 
         # If a thread is already started, stop it
@@ -1239,5 +1239,5 @@
             Create and return a list of panel objects
         """
-        from inversion_panel import InversionControl
+        from .inversion_panel import InversionControl
 
         self.parent = parent
@@ -1287,5 +1287,5 @@
                 msg += "Please select one.\n"
                 if len(data_list) > 1:
-                    from pr_widgets import DataDialog
+                    from .pr_widgets import DataDialog
                     dlg = DataDialog(data_list=data_1d_list, text=msg)
                     if dlg.ShowModal() == wx.ID_OK:
@@ -1307,5 +1307,5 @@
                     self.control_panel._change_file(evt=None, data=data)
                 except:
-                    msg = "Prview Set_data: " + str(sys.exc_value)
+                    msg = "Prview Set_data: " + str(sys.exc_info()[1])
                     wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
             else:
Index: src/sas/sasgui/perspectives/pr/pr_thread.py
===================================================================
--- src/sas/sasgui/perspectives/pr/pr_thread.py	(revision ac07a3acc20dbf79c564bf642aece71a02c2c862)
+++ src/sas/sasgui/perspectives/pr/pr_thread.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -43,5 +43,5 @@
         except:
             if self.error_func is not None:
-                self.error_func("CalcPr.compute: %s" % sys.exc_value)
+                self.error_func("CalcPr.compute: %s" % sys.exc_info()[1])
 
 class EstimatePr(CalcThread):
@@ -71,5 +71,5 @@
         except:
             if self.error_func is not None:
-                self.error_func("EstimatePr.compute: %s" % sys.exc_value)
+                self.error_func("EstimatePr.compute: %s" % sys.exc_info()[1])
 
 class EstimateNT(CalcThread):
@@ -111,3 +111,3 @@
         except:
             if self.error_func is not None:
-                self.error_func("EstimatePr2.compute: %s" % sys.exc_value)
+                self.error_func("EstimatePr2.compute: %s" % sys.exc_info()[1])
Index: src/sas/sasgui/plottools/LineModel.py
===================================================================
--- src/sas/sasgui/plottools/LineModel.py	(revision a26f67f1457e7e39b79d4c3800c03bcaa208dff7)
+++ src/sas/sasgui/plottools/LineModel.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -83,5 +83,5 @@
         elif x.__class__.__name__ == 'tuple':
             msg = "Tuples are not allowed as input to BaseComponent models"
-            raise ValueError, msg
+            raise ValueError(msg)
         else:
             return self._line(x)
@@ -105,5 +105,5 @@
         elif x.__class__.__name__ == 'tuple':
             msg = "Tuples are not allowed as input to BaseComponent models"
-            raise ValueError, msg
+            raise ValueError(msg)
         else:
             return self._line(x)
Index: src/sas/sasgui/plottools/PlotPanel.py
===================================================================
--- src/sas/sasgui/plottools/PlotPanel.py	(revision a1b8fee73528559155793dd74cda07d0c32e9d72)
+++ src/sas/sasgui/plottools/PlotPanel.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -2,5 +2,5 @@
     Plot panel.
 """
-from __future__ import print_function
+
 
 import logging
@@ -16,13 +16,13 @@
 from matplotlib.figure import Figure
 import os
-import transform
+from . import transform
 #TODO: make the plottables interactive
-from binder import BindArtist
+from .binder import BindArtist
 from matplotlib.font_manager import FontProperties
 DEBUG = False
 
-from plottables import Graph
-from TextDialog import TextDialog
-from LabelDialog import LabelDialog
+from .plottables import Graph
+from .TextDialog import TextDialog
+from .LabelDialog import LabelDialog
 import operator
 
@@ -44,5 +44,5 @@
         for a in obj.get_children(): show_tree(a, d + 1)
 
-from convert_units import convert_unit
+from .convert_units import convert_unit
 
 
@@ -116,5 +116,5 @@
         self.figure = Figure(None, dpi, linewidth=2.0)
         self.color = '#b3b3b3'
-        from canvas import FigureCanvas
+        from .canvas import FigureCanvas
         self.canvas = FigureCanvas(self, -1, self.figure)
         self.SetColor(color)
@@ -657,8 +657,8 @@
         else:
             plot_dict = plotlist
-        from fitDialog import LinearFit
-
-        if len(plot_dict.keys()) > 0:
-            first_item = plot_dict.keys()[0]
+        from .fitDialog import LinearFit
+
+        if len(list(plot_dict.keys())) > 0:
+            first_item = list(plot_dict.keys())[0]
             dlg = LinearFit(parent=None, plottable=first_item,
                             push_data=self.onFitDisplay,
@@ -691,5 +691,5 @@
             return
         name = menu.GetHelpString(id)
-        for plot in self.plots.values():
+        for plot in list(self.plots.values()):
             if plot.name == name:
                 self.graph.selected_plottable = plot.id
@@ -703,5 +703,5 @@
 
         """
-        from fitDialog import LinearFit
+        from .fitDialog import LinearFit
         if self._fit_dialog is not None:
             return
@@ -733,8 +733,8 @@
             self._fit_dialog = None
         plot_list = self.graph.returnPlottable()
-        if len(plot_list.keys()) > 0:
-            first_item = plot_list.keys()[0]
+        if len(list(plot_list.keys())) > 0:
+            first_item = list(plot_list.keys())[0]
             if first_item.x != []:
-                from PropertyDialog import Properties
+                from .PropertyDialog import Properties
                 dial = Properties(self, -1, 'Properties')
                 dial.setValues(self.prevXtrans, self.prevYtrans, self.viewModel)
@@ -956,5 +956,5 @@
             hl = sorted(zip(handles, labels),
                         key=operator.itemgetter(1))
-            handles2, labels2 = zip(*hl)
+            handles2, labels2 = list(zip(*hl))
             self.line_collections_list = handles2
             self.legend = self.subplot.legend(handles2, labels2,
@@ -986,5 +986,5 @@
         hl = sorted(zip(handles, labels),
                     key=operator.itemgetter(1))
-        handles2, labels2 = zip(*hl)
+        handles2, labels2 = list(zip(*hl))
         self.line_collections_list = handles2
         self.legend = self.subplot.legend(handles2, labels2,
@@ -1247,5 +1247,5 @@
                 hl = sorted(zip(handles, labels),
                             key=operator.itemgetter(1))
-                handles2, labels2 = zip(*hl)
+                handles2, labels2 = list(zip(*hl))
                 self.line_collections_list = handles2
                 self.legend = ax.legend(handles2, labels2,
@@ -1327,5 +1327,5 @@
         if id is None:
             id = name
-        from plottable_interactor import PointInteractor
+        from .plottable_interactor import PointInteractor
         p = PointInteractor(self, self.subplot, zorder=zorder, id=id)
         if p.markersize is not None:
@@ -1344,5 +1344,5 @@
         if id is None:
             id = name
-        from plottable_interactor import PointInteractor
+        from .plottable_interactor import PointInteractor
         p = PointInteractor(self, self.subplot, zorder=zorder, id=id)
         p.curve(x, y, dy=dy, color=color, symbol=symbol, zorder=zorder,
@@ -1754,5 +1754,5 @@
             self.graph.delete(self.fit_result)
             if hasattr(self, 'plots'):
-                if 'fit' in self.plots.keys():
+                if 'fit' in list(self.plots.keys()):
                     del self.plots['fit']
         self.ly = None
Index: src/sas/sasgui/plottools/__init__.py
===================================================================
--- src/sas/sasgui/plottools/__init__.py	(revision efe730d4bba7a05c203957e4495eece8359fcd77)
+++ src/sas/sasgui/plottools/__init__.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -1,2 +1,2 @@
-from PlotPanel import PlotPanel
-from plottables import Data1D, Theory1D
+from .PlotPanel import PlotPanel
+from .plottables import Data1D, Theory1D
Index: src/sas/sasgui/plottools/arrow3d.py
===================================================================
--- src/sas/sasgui/plottools/arrow3d.py	(revision 7432acb919f4013119ca918a73350297e0d4c93f)
+++ src/sas/sasgui/plottools/arrow3d.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -59,5 +59,5 @@
             return
         xs3d, ys3d, zs3d = self._verts3d
-        for i in xrange(len(xs3d)):
+        for i in range(len(xs3d)):
             xs, ys, _ = proj3d.proj_transform(xs3d[i], ys3d[i], zs3d[i], renderer.M)
             self.set_positions((xs[0], ys[0]), (xs[1], ys[1]))
Index: src/sas/sasgui/plottools/binder.py
===================================================================
--- src/sas/sasgui/plottools/binder.py	(revision a1b8fee73528559155793dd74cda07d0c32e9d72)
+++ src/sas/sasgui/plottools/binder.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -2,5 +2,5 @@
 Extension to MPL to support the binding of artists to key/mouse events.
 """
-from __future__ import print_function
+
 
 import sys
@@ -28,5 +28,5 @@
         return self.artist is not other.artist
 
-    def __nonzero__(self):
+    def __bool__(self):
         return self.artist is not None
 
@@ -125,5 +125,5 @@
             for cid in self._connections: self.canvas.mpl_disconnect(cid)
         except:
-            logger.error("Error disconnection canvas: %s" % sys.exc_value)
+            logger.error("Error disconnection canvas: %s" % sys.exc_info()[1])
         self._connections = []
 
@@ -189,6 +189,6 @@
         # Check that the trigger is valid
         if trigger not in self._actions:
-            raise ValueError, "%s invalid --- valid triggers are %s"\
-                 % (trigger, ", ".join(self.events))
+            raise ValueError("%s invalid --- valid triggers are %s"\
+                 % (trigger, ", ".join(self.events)))
 
         # Register the trigger callback
@@ -205,5 +205,5 @@
         """
         if action not in self.events:
-            raise ValueError, "Trigger expects " + ", ".join(self.events)
+            raise ValueError("Trigger expects " + ", ".join(self.events))
 
         # Tag the event with modifiers
Index: src/sas/sasgui/plottools/canvas.py
===================================================================
--- src/sas/sasgui/plottools/canvas.py	(revision 7432acb919f4013119ca918a73350297e0d4c93f)
+++ src/sas/sasgui/plottools/canvas.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -98,5 +98,5 @@
             dc.DrawBitmap(self.canvas.bitmap, (0, 0))
         except:
-            logger.error(sys.exc_value)
+            logger.error(sys.exc_info()[1])
 
     # restore original figure  resolution
@@ -209,5 +209,5 @@
                 fig.draw(self)
             except ValueError:
-                logger.error(sys.exc_value)
+                logger.error(sys.exc_info()[1])
         else:
             self._isRendered = False
Index: src/sas/sasgui/plottools/convert_units.py
===================================================================
--- src/sas/sasgui/plottools/convert_units.py	(revision a1b8fee73528559155793dd74cda07d0c32e9d72)
+++ src/sas/sasgui/plottools/convert_units.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -3,5 +3,5 @@
     This is a cleaned up version of unitConverter.py
 """
-from __future__ import print_function
+
 
 import re
@@ -44,10 +44,10 @@
                             unit = toks[0] + "^{" + str(powerer) + "}"
                 else:
-                    raise ValueError, "missing } in unit expression"
+                    raise ValueError("missing } in unit expression")
         else:  # no powerer
             if  power != 1:
                 unit = "(" + unit + ")" + "^{" + str(power) + "}"
     else:
-        raise ValueError, "empty unit ,enter a powerer different from zero"
+        raise ValueError("empty unit ,enter a powerer different from zero")
     return unit
 
Index: src/sas/sasgui/plottools/fitDialog.py
===================================================================
--- src/sas/sasgui/plottools/fitDialog.py	(revision 7432acb919f4013119ca918a73350297e0d4c93f)
+++ src/sas/sasgui/plottools/fitDialog.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -1,8 +1,8 @@
 import wx
-from plottables import Theory1D
+from .plottables import Theory1D
 import math
 import numpy as np
-import fittings
-import transform
+from . import fittings
+from . import transform
 import sys
 
@@ -86,5 +86,5 @@
         self.layout()
         # Receives the type of model for the fitting
-        from LineModel import LineModel
+        from .LineModel import LineModel
         self.model = LineModel()
         # Display the fittings values
@@ -671,5 +671,5 @@
                 return x
             else:
-                raise ValueError, "cannot compute log of a negative number"
+                raise ValueError("cannot compute log of a negative number")
 
     def floatInvTransform(self, x):
@@ -734,5 +734,5 @@
         except:
             msg = "LinearFit.set_fit_region: fit range must be floats"
-            raise ValueError, msg
+            raise ValueError(msg)
         self.xminFit.SetValue(format_number(xmin))
         self.xmaxFit.SetValue(format_number(xmax))
Index: src/sas/sasgui/plottools/fittings.py
===================================================================
--- src/sas/sasgui/plottools/fittings.py	(revision a1b8fee73528559155793dd74cda07d0c32e9d72)
+++ src/sas/sasgui/plottools/fittings.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -14,5 +14,5 @@
 
 """
-from __future__ import print_function
+
 
 from scipy import optimize
@@ -101,5 +101,5 @@
     # Testing implementation
     # Fit a Line model
-    from LineModel import LineModel
+    from .LineModel import LineModel
     line = LineModel()
     cstA = Parameter(line, 'A', event.cstA)
Index: src/sas/sasgui/plottools/plottable_interactor.py
===================================================================
--- src/sas/sasgui/plottools/plottable_interactor.py	(revision a1b8fee73528559155793dd74cda07d0c32e9d72)
+++ src/sas/sasgui/plottools/plottable_interactor.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -2,7 +2,7 @@
     This module allows more interaction with the plot
 """
-from __future__ import print_function
-
-from BaseInteractor import _BaseInteractor
+
+
+from .BaseInteractor import _BaseInteractor
 
 
Index: src/sas/sasgui/plottools/plottables.py
===================================================================
--- src/sas/sasgui/plottools/plottables.py	(revision 2d9526dfda3bdfe3c9159ab7d9db8399b4a00848)
+++ src/sas/sasgui/plottools/plottables.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -242,5 +242,5 @@
         selected_color = plottable.custom_color
         selected_plottable = None
-        for p in self.plottables.keys():
+        for p in list(self.plottables.keys()):
             if plottable.id == p.id:
                 selected_plottable = p
@@ -389,5 +389,5 @@
 
         """
-        raise NotImplemented, "Not a valid transform"
+        raise NotImplemented("Not a valid transform")
 
     # Related issues
@@ -517,5 +517,5 @@
                 label_dict[collection[0]] = basename
             else:
-                for i in xrange(len(collection)):
+                for i in range(len(collection)):
                     label_dict[collection[i]] = "%s %d" % (basename, i)
         return label_dict
@@ -689,15 +689,15 @@
                 msg = "Plottable.View: Given x and dx are not"
                 msg += " of the same length"
-                raise ValueError, msg
+                raise ValueError(msg)
             # Check length of y array
             if not len(y) == len(x):
                 msg = "Plottable.View: Given y "
                 msg += "and x are not of the same length"
-                raise ValueError, msg
+                raise ValueError(msg)
 
             if dy is not None and not len(dy) == 0 and not len(y) == len(dy):
                 msg = "Plottable.View: Given y and dy are not of the same "
                 msg += "length: len(y)=%s, len(dy)=%s" % (len(y), len(dy))
-                raise ValueError, msg
+                raise ValueError(msg)
             self.x = []
             self.y = []
@@ -734,13 +734,13 @@
                 msg = "Plottable.View: transformed x "
                 msg += "and y are not of the same length"
-                raise ValueError, msg
+                raise ValueError(msg)
             if has_err_x and not (len(self.x) == len(self.dx)):
                 msg = "Plottable.View: transformed x and dx"
                 msg += " are not of the same length"
-                raise ValueError, msg
+                raise ValueError(msg)
             if has_err_y and not (len(self.y) == len(self.dy)):
                 msg = "Plottable.View: transformed y"
                 msg += " and dy are not of the same length"
-                raise ValueError, msg
+                raise ValueError(msg)
             # Check that negative values are not plot on x and y axis for
             # log10 transformation
@@ -814,5 +814,5 @@
                 except:
                     logger.error("check_data_logX: skipping point x %g", self.x[i])
-                    logger.error(sys.exc_value)
+                    logger.error(sys.exc_info()[1])
             self.x = tempx
             self.y = tempy
@@ -844,5 +844,5 @@
                 except:
                     logger.error("check_data_logY: skipping point %g", self.y[i])
-                    logger.error(sys.exc_value)
+                    logger.error(sys.exc_info()[1])
 
             self.x = tempx
@@ -1108,5 +1108,5 @@
         Plottable.__init__(self)
         msg = "Theory1D is no longer supported, please use Data1D and change symbol.\n"
-        raise DeprecationWarning, msg
+        raise DeprecationWarning(msg)
 
 class Fit1D(Plottable):
Index: src/sas/sasgui/plottools/transform.py
===================================================================
--- src/sas/sasgui/plottools/transform.py	(revision 7432acb919f4013119ca918a73350297e0d4c93f)
+++ src/sas/sasgui/plottools/transform.py	(revision fa81e94af983227efded2314c2c544f98e2c56ba)
@@ -24,5 +24,5 @@
     """
     if not x > 0:
-        raise ValueError, "Transformation only accepts positive values."
+        raise ValueError("Transformation only accepts positive values.")
     else:
         return x
@@ -50,5 +50,5 @@
     """
     if not x >= 0:
-        raise ValueError, "square root of a negative value "
+        raise ValueError("square root of a negative value ")
     else:
         return math.sqrt(x)
@@ -76,5 +76,5 @@
     """
     if not x >= 0:
-        raise ValueError, "double square root of a negative value "
+        raise ValueError("double square root of a negative value ")
     else:
         return math.sqrt(math.sqrt(x))
@@ -90,5 +90,5 @@
     """
     if not x > 0:
-        raise ValueError, "Log(x)of a negative value "
+        raise ValueError("Log(x)of a negative value ")
     else:
         return math.log(x)
@@ -100,5 +100,5 @@
         return 1 / x
     else:
-        raise ValueError, "cannot divide by zero"
+        raise ValueError("cannot divide by zero")
 
 
@@ -109,5 +109,5 @@
         return 1 / math.sqrt(y)
     else:
-        raise ValueError, "transform.toOneOverSqrtX: cannot be computed"
+        raise ValueError("transform.toOneOverSqrtX: cannot be computed")
 
 
@@ -118,5 +118,5 @@
         return math.log(y * (x ** 2))
     else:
-        raise ValueError, "transform.toLogYX2: cannot be computed"
+        raise ValueError("transform.toLogYX2: cannot be computed")
 
 
@@ -127,5 +127,5 @@
         return math.log(math.pow(x, 4) * y)
     else:
-        raise ValueError, "transform.toLogYX4: input error"
+        raise ValueError("transform.toLogYX4: input error")
 
 
@@ -149,5 +149,5 @@
     """
     if not (x * y) > 0:
-        raise ValueError, "Log(X*Y)of a negative value "
+        raise ValueError("Log(X*Y)of a negative value ")
     else:
         return math.log(x * y)
@@ -211,5 +211,5 @@
     else:
         msg = "transform.errFromX2: can't compute error of negative x"
-        raise ValueError, msg
+        raise ValueError(msg)
 
 
@@ -245,5 +245,5 @@
     else:
         msg = "transform.errFromX4: can't compute error of negative x"
-        raise ValueError, msg
+        raise ValueError(msg)
 
 
@@ -264,9 +264,9 @@
         msg = "Transformation does not accept"
         msg += " point that are consistent with zero."
-        raise ValueError, msg
+        raise ValueError(msg)
     if x != 0:
         dx = dx / (x * math.log(10))
     else:
-        raise ValueError, "errToLogX: divide by zero"
+        raise ValueError("errToLogX: divide by zero")
     return dx
 
@@ -287,5 +287,5 @@
         dx = dx / x
     else:
-        raise ValueError, "errToLogX: divide by zero"
+        raise ValueError("errToLogX: divide by zero")
     return dx
 
@@ -312,5 +312,5 @@
         msg = "Transformation does not accept point "
         msg += " that are consistent with zero."
-        raise ValueError, msg
+        raise ValueError(msg)
     if x != 0 and y != 0:
         if dx is None:
@@ -320,5 +320,5 @@
         err = (dx / x) ** 2 + (dy / y) ** 2
     else:
-        raise ValueError, "cannot compute this error"
+        raise ValueError("cannot compute this error")
 
     return math.sqrt(math.fabs(err))
@@ -335,5 +335,5 @@
         msg = "Transformation does not accept point"
         msg += " that are consistent with zero."
-        raise ValueError, msg
+        raise ValueError(msg)
     if x > 0 and y > 0:
         if dx is None:
@@ -343,5 +343,5 @@
         err = (2.0 * dx / x) ** 2 + (dy / y) ** 2
     else:
-        raise ValueError, "cannot compute this error"
+        raise ValueError("cannot compute this error")
     return math.sqrt(math.fabs(err))
 
@@ -357,5 +357,5 @@
         err = dx / x ** 2
     else:
-        raise ValueError, "Cannot compute this error"
+        raise ValueError("Cannot compute this error")
     return math.fabs(err)
 
@@ -371,5 +371,5 @@
         err = -1 / 2 * math.pow(x, -3.0 / 2.0) * dx
     else:
-        raise ValueError, "Cannot compute this error"
+        raise ValueError("Cannot compute this error")
     return math.fabs(err)
 
@@ -387,5 +387,5 @@
         msg = "Transformation does not accept point "
         msg += " that are consistent with zero."
-        raise ValueError, msg
+        raise ValueError(msg)
     if dx is None:
         dx = 0
