Index: src/sas/qtgui/GUITests.py
===================================================================
--- src/sas/qtgui/GUITests.py	(revision 0595bb7954497ab0a25be7ffb88395a048df52ae)
+++ src/sas/qtgui/GUITests.py	(revision 676f1378496edab35bd11ed8e0ce2614c0db17b6)
@@ -57,7 +57,11 @@
 from Perspectives.Fitting.UnitTesting import FittingOptionsTest
 from Perspectives.Fitting.UnitTesting import MultiConstraintTest
+from Perspectives.Fitting.UnitTesting import ConstraintWidgetTest
 
 #  Invariant
 from Perspectives.Invariant.UnitTesting import InvariantPerspectiveTest
+
+#  Inversion
+from Perspectives.Inversion.UnitTesting import InversionPerspectiveTest
 
 def suite():
@@ -113,10 +117,11 @@
         unittest.makeSuite(FittingOptionsTest.FittingOptionsTest,         'test'),
         unittest.makeSuite(MultiConstraintTest.MultiConstraintTest,       'test'),
+        unittest.makeSuite(ConstraintWidgetTest.ConstraintWidgetTest,     'test'),
+
         #  Invariant
         unittest.makeSuite(InvariantPerspectiveTest.InvariantPerspectiveTest,  'test'),
-        unittest.makeSuite(InvariantDetailsTest.InvariantDetailsTest,     'test'),
         #  Inversion
         unittest.makeSuite(InversionPerspectiveTest.InversionTest,  'test'),
-     )
+        )
     return unittest.TestSuite(suites)
 
Index: src/sas/qtgui/MainWindow/GuiManager.py
===================================================================
--- src/sas/qtgui/MainWindow/GuiManager.py	(revision e90988cd3c71ed989b47df2bed55bcd11e7733ce)
+++ src/sas/qtgui/MainWindow/GuiManager.py	(revision 676f1378496edab35bd11ed8e0ce2614c0db17b6)
@@ -185,7 +185,7 @@
 
         # Save users from themselves...
-        if isinstance(self._current_perspective, Perspectives.PERSPECTIVES[str(perspective_name)]):
-            self.setupPerspectiveMenubarOptions(self._current_perspective)
-            return
+        #if isinstance(self._current_perspective, Perspectives.PERSPECTIVES[str(perspective_name)]):
+        self.setupPerspectiveMenubarOptions(self._current_perspective)
+        #    return
 
         # Close the previous perspective
@@ -623,7 +623,10 @@
     def actionConstrained_Fit(self):
         """
-        """
-        print("actionConstrained_Fit TRIGGERED")
-        pass
+        Add a new Constrained and Simult. Fit page in the fitting perspective.
+        """
+        per = self.perspective()
+        if not isinstance(per, FittingWindow):
+            return
+        per.addConstraintTab()
 
     def actionCombine_Batch_Fit(self):
Index: src/sas/qtgui/Perspectives/Fitting/ConstraintWidget.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/ConstraintWidget.py	(revision 676f1378496edab35bd11ed8e0ce2614c0db17b6)
+++ src/sas/qtgui/Perspectives/Fitting/ConstraintWidget.py	(revision 676f1378496edab35bd11ed8e0ce2614c0db17b6)
@@ -0,0 +1,81 @@
+import os
+import sys
+
+import sas.qtgui.Utilities.GuiUtils as GuiUtils
+from PyQt5 import QtGui, QtCore, QtWidgets
+
+import sas.qtgui.Utilities.ObjectLibrary as ObjectLibrary
+
+from sas.qtgui.Perspectives.Fitting.UI.ConstraintWidgetUI import Ui_ConstraintWidgetUI
+
+class ConstraintWidget(QtWidgets.QWidget, Ui_ConstraintWidgetUI):
+    """
+    Constraints Dialog to select the desired parameter/model constraints
+    """
+
+    def __init__(self, parent=None):
+        super(ConstraintWidget, self).__init__()
+        self.parent = parent
+        self.setupUi(self)
+        self.currentType = "FitPage"
+
+        # Set up signals/slots
+        self.initializeSignals()
+
+        # Create the list of tabs
+        self.updateFitList()
+
+    def initializeSignals(self):
+        """
+        Set up signals/slots for this widget
+        """
+        self.btnSingle.toggled.connect(self.onFitTypeChange)
+        self.btnBatch.toggled.connect(self.onFitTypeChange)
+        self.cbSeries.indexChanged.connect(self.onSpecialCaseChange)
+        self.cmdFit.clicked.connect(self.onFit)
+        self.cmdHelp.clicked.connect(self.onHelp)
+
+    def onFitTypeChange(self, checked):
+        """
+        Respond to the fit type change
+        single fit/batch fit
+        """
+        pass
+
+    def onSpecialCaseChange(self, index):
+        """
+        Respond to the combobox change for special case constraint sets
+        """
+        pass
+
+    def onFit(self):
+        """
+        Perform the constrained/simultaneous fit
+        """
+        pass
+
+    def onHelp(self):
+        """
+        Display the help page
+        """
+        pass
+
+    def updateFitList(self):
+        """
+        Fill the list of model/data sets for fitting/constraining
+        """
+        # look at the object library to find all fit tabs
+        objects = ObjectLibrary.listObjects()
+        if not objects:
+            return
+        tabs = [tab for tab in ObjectLibrary.listObjects() if self.currentType in tab]
+
+        pass
+
+    def updateConstraintList(self):
+        """
+        Fill the list of constraints for the current selection of model/data sets
+        """
+        pass
+
+
Index: src/sas/qtgui/Perspectives/Fitting/FittingPerspective.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/FittingPerspective.py	(revision e90988cd3c71ed989b47df2bed55bcd11e7733ce)
+++ src/sas/qtgui/Perspectives/Fitting/FittingPerspective.py	(revision 676f1378496edab35bd11ed8e0ce2614c0db17b6)
@@ -11,4 +11,5 @@
 
 from sas.qtgui.Perspectives.Fitting.FittingWidget import FittingWidget
+from sas.qtgui.Perspectives.Fitting.ConstraintWidget import ConstraintWidget
 from sas.qtgui.Perspectives.Fitting.FittingOptions import FittingOptions
 from sas.qtgui.Perspectives.Fitting.GPUOptions import GPUOptions
@@ -30,4 +31,5 @@
         # Max index for adding new, non-clashing tab names
         self.maxIndex = 0
+        self.maxCSIndex = 0
 
         # Index of the current tab
@@ -108,5 +110,5 @@
         tab.is_batch_fitting = is_batch
         # Add this tab to the object library so it can be retrieved by scripting/jupyter
-        tab_name = self.tabName(is_batch=is_batch)
+        tab_name = self.getTabName(is_batch=is_batch)
         ObjectLibrary.addObject(tab_name, tab)
         self.tabs.append(tab)
@@ -116,4 +118,16 @@
         self.addTab(tab, tab_name)
 
+    def addConstraintTab(self):
+        """
+        Add a new C&S fitting tab
+        """
+        tab	= ConstraintWidget(parent=self.parent)
+        # Add this tab to the object library so it can be retrieved by scripting/jupyter
+        tab_name = self.getCSTabName() # TODO update the tab name scheme
+        ObjectLibrary.addObject(tab_name, tab)
+        self.tabs.append(tab)
+        self.maxCSIndex += 1
+        self.addTab(tab, tab_name)
+
     def updateFitDict(self, item_key, tab_name):
         """
@@ -126,7 +140,5 @@
             self.dataToFitTab[item_key_str] = [tab_name]
 
-        #print "CURRENT dict: ", self.dataToFitTab
-
-    def tabName(self, is_batch=False):
+    def getTabName(self, is_batch=False):
         """
         Get the new tab name, based on the number of fitting tabs so far
@@ -134,4 +146,12 @@
         page_name = "BatchPage" if is_batch else "FitPage"
         page_name = page_name + str(self.maxIndex)
+        return page_name
+
+    def getCSTabName(self):
+        """
+        Get the new tab name, based on the number of fitting tabs so far
+        """
+        page_name = "Const. & Simul. Fit"
+        page_name = page_name + str(self.maxCSIndex)
         return page_name
 
Index: src/sas/qtgui/Perspectives/Fitting/FittingWidget.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/FittingWidget.py	(revision d3c0b9586dd15f08a6d08159dc5c41bfae457c3b)
+++ src/sas/qtgui/Perspectives/Fitting/FittingWidget.py	(revision 676f1378496edab35bd11ed8e0ce2614c0db17b6)
@@ -552,5 +552,5 @@
         """
         selected_rows = self.lstParams.selectionModel().selectedRows()
-        assert(len(selected_rows), 2)
+        assert(len(selected_rows)==2)
 
         params_list = [s.data() for s in selected_rows]
Index: src/sas/qtgui/Perspectives/Fitting/UI/ConstraintWidgetUI.ui
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/UI/ConstraintWidgetUI.ui	(revision 676f1378496edab35bd11ed8e0ce2614c0db17b6)
+++ src/sas/qtgui/Perspectives/Fitting/UI/ConstraintWidgetUI.ui	(revision 676f1378496edab35bd11ed8e0ce2614c0db17b6)
@@ -0,0 +1,194 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConstraintWidgetUI</class>
+ <widget class="QWidget" name="ConstraintWidgetUI">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>340</width>
+    <height>441</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Constrained and Simultaneous Fit</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout_3">
+   <item row="0" column="0">
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>Source choice for simultaneous fitting</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout">
+      <item row="0" column="0">
+       <layout class="QHBoxLayout" name="horizontalLayout_2">
+        <item>
+         <widget class="QRadioButton" name="btnSingle">
+          <property name="text">
+           <string>Single fits</string>
+          </property>
+          <property name="checked">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QRadioButton" name="btnBatch">
+          <property name="text">
+           <string>Batch fits</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <spacer name="horizontalSpacer">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>198</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+       </layout>
+      </item>
+      <item row="1" column="0">
+       <widget class="QTableWidget" name="tblTabList">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="alternatingRowColors">
+         <bool>true</bool>
+        </property>
+        <property name="selectionBehavior">
+         <enum>QAbstractItemView::SelectRows</enum>
+        </property>
+        <row>
+         <property name="text">
+          <string/>
+         </property>
+        </row>
+        <row>
+         <property name="text">
+          <string/>
+         </property>
+        </row>
+        <row>
+         <property name="text">
+          <string/>
+         </property>
+        </row>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item row="1" column="0">
+    <widget class="QGroupBox" name="groupBox_2">
+     <property name="title">
+      <string>Constraints</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_2">
+      <item row="0" column="0">
+       <layout class="QHBoxLayout" name="horizontalLayout">
+        <item>
+         <widget class="QLabel" name="label">
+          <property name="text">
+           <string>Special cases</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QComboBox" name="cbCases">
+          <item>
+           <property name="text">
+            <string>None</string>
+           </property>
+          </item>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item row="1" column="0">
+       <widget class="QTableWidget" name="tblConstraints">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <column>
+         <property name="text">
+          <string/>
+         </property>
+        </column>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item row="2" column="0">
+    <layout class="QHBoxLayout" name="horizontalLayout_3">
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>273</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="cmdFit">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>75</width>
+         <height>23</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>Fit</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="cmdHelp">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>75</width>
+         <height>23</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>Help</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
Index: src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingPerspectiveTest.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingPerspectiveTest.py	(revision 53c771ed573b1c8716ecc969b54a1d617a4cb931)
+++ src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingPerspectiveTest.py	(revision 676f1378496edab35bd11ed8e0ce2614c0db17b6)
@@ -44,5 +44,6 @@
         self.assertEqual(len(self.widget.tabs), 1)
         self.assertEqual(self.widget.maxIndex, 1)
-        self.assertEqual(self.widget.tabName(), "FitPage1")
+        self.assertEqual(self.widget.maxCSIndex, 0)
+        self.assertEqual(self.widget.getTabName(), "FitPage1")
 
     def testAddTab(self):
@@ -52,16 +53,23 @@
         self.widget.addFit(None)
         self.assertEqual(len(self.widget.tabs), 2)
-        self.assertEqual(self.widget.tabName(), "FitPage2")
+        self.assertEqual(self.widget.getTabName(), "FitPage2")
         self.assertEqual(self.widget.maxIndex, 2)
         # Add an empty batch tab
         self.widget.addFit(None, is_batch=True)
         self.assertEqual(len(self.widget.tabs), 3)
-        self.assertEqual(self.widget.tabName(2), "BatchPage3")
+        self.assertEqual(self.widget.getTabName(2), "BatchPage3")
         self.assertEqual(self.widget.maxIndex, 3)
+
+    def testAddCSTab(self):
+        ''' Add a constraint/simult tab'''
+        self.widget.addConstraintTab()
+        self.assertEqual(len(self.widget.tabs), 2)
+        self.assertEqual(self.widget.getCSTabName(), "Const. & Simul. Fit1")
+        self.assertEqual(self.widget.maxCSIndex, 1)
 
     def testResetTab(self):
         ''' Remove data from last tab'''
         self.assertEqual(len(self.widget.tabs), 1)
-        self.assertEqual(self.widget.tabName(), "FitPage1")
+        self.assertEqual(self.widget.getTabName(), "FitPage1")
         self.assertEqual(self.widget.maxIndex, 1)
 
@@ -71,5 +79,5 @@
         # see that the tab didn't disappear, just changed the name/id
         self.assertEqual(len(self.widget.tabs), 1)
-        self.assertEqual(self.widget.tabName(), "FitPage2")
+        self.assertEqual(self.widget.getTabName(), "FitPage2")
         self.assertEqual(self.widget.maxIndex, 2)
 
@@ -95,5 +103,5 @@
         self.assertEqual(len(self.widget.tabs), 1)
         self.assertEqual(self.widget.maxIndex, 2)
-        self.assertEqual(self.widget.tabName(), "FitPage2")
+        self.assertEqual(self.widget.getTabName(), "FitPage2")
 
         # Attemtp to remove the last tab
@@ -102,5 +110,5 @@
         self.assertEqual(len(self.widget.tabs), 1)
         self.assertEqual(self.widget.maxIndex, 3)
-        self.assertEqual(self.widget.tabName(), "FitPage3")
+        self.assertEqual(self.widget.getTabName(), "FitPage3")
 
     def testAllowBatch(self):
Index: src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingWidgetTest.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingWidgetTest.py	(revision 53c771ed573b1c8716ecc969b54a1d617a4cb931)
+++ src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingWidgetTest.py	(revision 676f1378496edab35bd11ed8e0ce2614c0db17b6)
@@ -112,30 +112,29 @@
         fittingWindow =  self.widget
 
-        self.assertIsInstance(fittingWindow.lstMagnetic.itemDelegate(), QtGui.QStyledItemDelegate)
+        self.assertIsInstance(fittingWindow.lstMagnetic.itemDelegate(), QtWidgets.QStyledItemDelegate)
         #Test loading from json categories
         fittingWindow.SASModelToQModel("cylinder")
         mag_index = fittingWindow.lstMagnetic.model().index(0,0)
-        self.assertEqual(str(mag_index.data().toString()), "up:frac_i")
+        self.assertEqual(mag_index.data(), "up:frac_i")
         mag_index = fittingWindow.lstMagnetic.model().index(1,0)
-        self.assertEqual(str(mag_index.data().toString()), "up:frac_f")
+        self.assertEqual(mag_index.data(), "up:frac_f")
         mag_index = fittingWindow.lstMagnetic.model().index(2,0)
-        self.assertEqual(str(mag_index.data().toString()), "up:angle")
+        self.assertEqual(mag_index.data(), "up:angle")
         mag_index = fittingWindow.lstMagnetic.model().index(3,0)
-        self.assertEqual(str(mag_index.data().toString()), "M0:sld")
+        self.assertEqual(mag_index.data(), "M0:sld")
         mag_index = fittingWindow.lstMagnetic.model().index(4,0)
-        self.assertEqual(str(mag_index.data().toString()), "mtheta:sld")
+        self.assertEqual(mag_index.data(), "mtheta:sld")
         mag_index = fittingWindow.lstMagnetic.model().index(5,0)
-        self.assertEqual(str(mag_index.data().toString()), "mphi:sld")
+        self.assertEqual(mag_index.data(), "mphi:sld")
         mag_index = fittingWindow.lstMagnetic.model().index(6,0)
-        self.assertEqual(str(mag_index.data().toString()), "M0:sld_solvent")
+        self.assertEqual(mag_index.data(), "M0:sld_solvent")
         mag_index = fittingWindow.lstMagnetic.model().index(7,0)
-        self.assertEqual(str(mag_index.data().toString()), "mtheta:sld_solvent")
+        self.assertEqual(mag_index.data(), "mtheta:sld_solvent")
         mag_index = fittingWindow.lstMagnetic.model().index(8,0)
-        self.assertEqual(str(mag_index.data().toString()), "mphi:sld_solvent")
+        self.assertEqual(mag_index.data(), "mphi:sld_solvent")
 
         # test the delegate a bit
         delegate = fittingWindow.lstMagnetic.itemDelegate()
         self.assertEqual(delegate.editableParameters(), [1, 2, 3])
-        self.assertIsInstance(delegate.combo_updated, QtCore.pyqtBoundSignal)
 
     def testSelectStructureFactor(self):
