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 be8f4b04004041064478b2c80059a875e52e30d3)
@@ -8,8 +8,9 @@
 
 from sas.qtgui.Perspectives.Fitting.UI.ConstraintWidgetUI import Ui_ConstraintWidgetUI
+from sas.qtgui.Perspectives.Fitting.FittingWidget import FittingWidget
 
 class ConstraintWidget(QtWidgets.QWidget, Ui_ConstraintWidgetUI):
     """
-    Constraints Dialog to select the desired parameter/model constraints
+    Constraints Dialog to select the desired parameter/model constraints.
     """
 
@@ -20,9 +21,39 @@
         self.currentType = "FitPage"
 
+        # Set up the widgets
+        self.initializeWidgets()
+
         # Set up signals/slots
         self.initializeSignals()
 
         # Create the list of tabs
-        self.updateFitList()
+        self.initializeFitList()
+
+    def acceptsData(self):
+        """ Tells the caller this widget doesn't accept data """
+        return False
+
+    def initializeWidgets(self):
+        """
+        Set up various widget states
+        """
+        labels = ['FitPage', 'Model', 'Data', 'Mnemonics']
+        # tab widget - headers
+        self.tblTabList.setColumnCount(len(labels))
+        self.tblTabList.setHorizontalHeaderLabels(labels)
+        self.tblTabList.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
+
+        self.tblTabList.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
+        self.tblTabList.customContextMenuRequested.connect(self.showModelContextMenu)
+
+        # disabled constraint 
+        labels = ['Constraint']
+        self.tblConstraints.setColumnCount(len(labels))
+        self.tblConstraints.setHorizontalHeaderLabels(labels)
+        self.tblConstraints.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
+        self.tblConstraints.setEnabled(False)
+
+        self.tblConstraints.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
+        self.tblConstraints.customContextMenuRequested.connect(self.showConstrContextMenu)
 
     def initializeSignals(self):
@@ -32,7 +63,16 @@
         self.btnSingle.toggled.connect(self.onFitTypeChange)
         self.btnBatch.toggled.connect(self.onFitTypeChange)
-        self.cbSeries.indexChanged.connect(self.onSpecialCaseChange)
+        self.cbCases.currentIndexChanged.connect(self.onSpecialCaseChange)
         self.cmdFit.clicked.connect(self.onFit)
         self.cmdHelp.clicked.connect(self.onHelp)
+        self.parent.tabsModifiedSignal.connect(self.initializeFitList)
+
+    def updateSignalsFromTab(self, tab=None):
+        """
+        Intercept update signals from fitting tabs
+        """
+        if tab is not None:
+            ObjectLibrary.getObject(tab).constraintAddedSignal.connect(self.initializeFitList)
+            ObjectLibrary.getObject(tab).newModelSignal.connect(self.initializeFitList)
 
     def onFitTypeChange(self, checked):
@@ -61,21 +101,233 @@
         pass
 
-    def updateFitList(self):
+    def isTabImportable(self, tab):
+        """
+        Determines if the tab can be imported and included in the widget
+        """
+        if not self.currentType in tab: return False
+        object = ObjectLibrary.getObject(tab)
+        if not isinstance(object, FittingWidget): return False
+        if object.data is None: return False
+        return True
+
+    def showModelContextMenu(self, position):
+        """
+        Show context specific menu in the tab table widget.
+        """
+        menu = QtWidgets.QMenu()
+        rows = [s.row() for s in self.tblTabList.selectionModel().selectedRows()]
+        num_rows = len(rows)
+        if num_rows <= 0:
+            return
+        # Select for fitting
+        param_string = "Fit Page " if num_rows==1 else "Fit Pages "
+        to_string = "to its current value" if num_rows==1 else "to their current values"
+
+        self.actionSelect = QtWidgets.QAction(self)
+        self.actionSelect.setObjectName("actionSelect")
+        self.actionSelect.setText(QtCore.QCoreApplication.translate("self", "Select "+param_string+" for fitting"))
+        # Unselect from fitting
+        self.actionDeselect = QtWidgets.QAction(self)
+        self.actionDeselect.setObjectName("actionDeselect")
+        self.actionDeselect.setText(QtCore.QCoreApplication.translate("self", "De-select "+param_string+" from fitting"))
+
+        self.actionRemoveConstraint = QtWidgets.QAction(self)
+        self.actionRemoveConstraint.setObjectName("actionRemoveConstrain")
+        self.actionRemoveConstraint.setText(QtCore.QCoreApplication.translate("self", "Remove all constraints on selected models"))
+
+        self.actionMutualMultiConstrain = QtWidgets.QAction(self)
+        self.actionMutualMultiConstrain.setObjectName("actionMutualMultiConstrain")
+        self.actionMutualMultiConstrain.setText(QtCore.QCoreApplication.translate("self", "Mutual constrain of parameters in selected models..."))
+
+        menu.addAction(self.actionSelect)
+        menu.addAction(self.actionDeselect)
+        menu.addSeparator()
+
+        #menu.addAction(self.actionRemoveConstraint)
+        if num_rows >= 2:
+            menu.addAction(self.actionMutualMultiConstrain)
+
+        # Define the callbacks
+        #self.actionConstrain.triggered.connect(self.addSimpleConstraint)
+        #self.actionRemoveConstraint.triggered.connect(self.deleteConstraint)
+        #self.actionMutualMultiConstrain.triggered.connect(self.showMultiConstraint)
+        self.actionSelect.triggered.connect(self.selectModels)
+        self.actionDeselect.triggered.connect(self.deselectModels)
+        try:
+            menu.exec_(self.tblTabList.viewport().mapToGlobal(position))
+        except AttributeError as ex:
+            logging.error("Error generating context menu: %s" % ex)
+        return
+
+    def showConstrContextMenu(self, position):
+        """
+        Show context specific menu in the tab table widget.
+        """
+        menu = QtWidgets.QMenu()
+        rows = [s.row() for s in self.tblConstraints.selectionModel().selectedRows()]
+        num_rows = len(rows)
+        if num_rows <= 0:
+            return
+        # Select for fitting
+        param_string = "constraint " if num_rows==1 else "constraints "
+        to_string = "to its current value" if num_rows==1 else "to their current values"
+
+        self.actionSelect = QtWidgets.QAction(self)
+        self.actionSelect.setObjectName("actionSelect")
+        self.actionSelect.setText(QtCore.QCoreApplication.translate("self", "Select "+param_string+" for fitting"))
+        # Unselect from fitting
+        self.actionDeselect = QtWidgets.QAction(self)
+        self.actionDeselect.setObjectName("actionDeselect")
+        self.actionDeselect.setText(QtCore.QCoreApplication.translate("self", "De-select "+param_string+" from fitting"))
+
+        self.actionRemoveConstraint = QtWidgets.QAction(self)
+        self.actionRemoveConstraint.setObjectName("actionRemoveConstrain")
+        self.actionRemoveConstraint.setText(QtCore.QCoreApplication.translate("self", "Remove "+param_string))
+
+        menu.addAction(self.actionSelect)
+        menu.addAction(self.actionDeselect)
+        menu.addSeparator()
+        menu.addAction(self.actionRemoveConstraint)
+
+        # Define the callbacks
+        #self.actionConstrain.triggered.connect(self.addSimpleConstraint)
+        self.actionRemoveConstraint.triggered.connect(self.deleteConstraint)
+        #self.actionMutualMultiConstrain.triggered.connect(self.showMultiConstraint)
+        self.actionSelect.triggered.connect(self.selectConstraints)
+        self.actionDeselect.triggered.connect(self.deselectConstraints)
+        try:
+            menu.exec_(self.tblConstraints.viewport().mapToGlobal(position))
+        except AttributeError as ex:
+            logging.error("Error generating context menu: %s" % ex)
+        return
+
+    def selectConstraints(self):
+        """
+        Selected constraints are chosen for fitting
+        """
+        status = QtCore.Qt.Checked
+        self.setRowSelection(self.tblConstraints, status)
+
+    def deselectConstraints(self):
+        """
+        Selected constraints are removed for fitting
+        """
+        status = QtCore.Qt.Unchecked
+        self.setRowSelection(self.tblConstraints, status)
+
+    def selectModels(self):
+        """
+        Selected models are chosen for fitting
+        """
+        status = QtCore.Qt.Checked
+        self.setRowSelection(self.tblTabList, status)
+
+    def deselectModels(self):
+        """
+        Selected models are removed for fitting
+        """
+        status = QtCore.Qt.Unchecked
+        self.setRowSelection(self.tblTabList, status)
+
+    def selectedParameters(self, widget):
+        """ Returns list of selected (highlighted) parameters """
+        return [s.row() for s in widget.selectionModel().selectedRows()]
+
+    def setRowSelection(self, widget, status=QtCore.Qt.Unchecked):
+        """
+        Selected models are chosen for fitting
+        """
+        # Convert to proper indices and set requested enablement
+        for row in self.selectedParameters(widget):
+            widget.item(row, 0).setCheckState(status)
+
+    def deleteConstraint(self):#, row):
+        """
+        Delete all selected constraints.
+        """
+        constraints_to_delete = []
+        for row in self.selectedParameters(self.tblConstraints):
+            constraints_to_delete.append(self.tblConstraints.item(row, 0).data(0))
+        for constraint in constraints_to_delete:
+            moniker = constraint[:constraint.index(':')]
+            param = constraint[constraint.index(':')+1:constraint.index('=')].strip()
+            tab = self.available_tabs[moniker]
+            tab.deleteConstraintOnParameter(param)
+        # Constraints removed - refresh the table widget
+        self.initializeFitList()
+
+    def updateFitLine(self, tab):
+        """
+        Update a single line of the table widget with tab info
+        """
+        model = ObjectLibrary.getObject(tab).kernel_module
+        if model is None:
+            return
+        tab_name = tab
+        model_name = model.id
+        moniker = model.name
+        model_data = ObjectLibrary.getObject(tab).data
+        model_filename = model_data.filename
+        self.available_tabs[moniker] = ObjectLibrary.getObject(tab)
+
+        # Update the model table widget
+        item = QtWidgets.QTableWidgetItem(tab_name)
+        item.setCheckState(QtCore.Qt.Checked)
+        pos = self.tblTabList.rowCount()
+        self.tblTabList.insertRow(pos)
+        self.tblTabList.setItem(pos, 0, item)
+        self.tblTabList.setItem(pos, 1, QtWidgets.QTableWidgetItem(model_name))
+        self.tblTabList.setItem(pos, 2, QtWidgets.QTableWidgetItem(model_filename))
+        self.tblTabList.setItem(pos, 3, QtWidgets.QTableWidgetItem(moniker))
+
+        #self.available_tabs[pos] = (model, model_data)
+        #self.available_tabs[moniker] = tab
+
+        # Check if any constraints present in tab
+        constraints = ObjectLibrary.getObject(tab).getConstraintsForModel()
+        if not constraints: 
+            return
+        self.tblConstraints.setEnabled(True)
+        for constraint in constraints:
+            # Create the text for widget item
+            label = moniker + ":"+ constraint[0] + " = " + constraint[1]
+
+            # Show the text in the constraint table
+            item = QtWidgets.QTableWidgetItem(label)
+            item.setCheckState(QtCore.Qt.Checked)
+            pos = self.tblConstraints.rowCount()
+            self.tblConstraints.insertRow(pos)
+            self.tblConstraints.setItem(pos, 0, item)
+            self.available_constraints[pos] = constraints
+
+    def initializeFitList(self):
         """
         Fill the list of model/data sets for fitting/constraining
         """
         # look at the object library to find all fit tabs
+        # Show the content of the current "model"
         objects = ObjectLibrary.listObjects()
+
+        # Tab dict
+        # moniker -> (kernel_module, data)
+        self.available_tabs = {}
+        # Constraint dict
+        # moniker -> [constraints]
+        self.available_constraints = {}
+
+        # Reset the table widgets
+        self.tblTabList.setRowCount(0)
+        self.tblConstraints.setRowCount(0)
+
+        # Fit disabled
+        self.cmdFit.setEnabled(False)
+
         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
-
-
+
+        tabs = [tab for tab in ObjectLibrary.listObjects() if self.isTabImportable(tab)]
+        for tab in tabs:
+            self.updateFitLine(tab)
+            self.updateSignalsFromTab(tab)
+            # We have at least 1 fit page, allow fitting
+            self.cmdFit.setEnabled(True)
Index: src/sas/qtgui/Perspectives/Fitting/FittingPerspective.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/FittingPerspective.py	(revision 676f1378496edab35bd11ed8e0ce2614c0db17b6)
+++ src/sas/qtgui/Perspectives/Fitting/FittingPerspective.py	(revision be8f4b04004041064478b2c80059a875e52e30d3)
@@ -18,4 +18,5 @@
     """
     """
+    tabsModifiedSignal = QtCore.pyqtSignal()
     name = "Fitting" # For displaying in the combo box in DataExplorer
     def __init__(self, parent=None, data=None):
@@ -117,4 +118,5 @@
         self.maxIndex += 1
         self.addTab(tab, tab_name)
+        self.tabsModifiedSignal.emit()
 
     def addConstraintTab(self):
@@ -122,5 +124,5 @@
         Add a new C&S fitting tab
         """
-        tab	= ConstraintWidget(parent=self.parent)
+        tab	= ConstraintWidget(parent=self)
         # 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
@@ -182,4 +184,5 @@
             self.removeTab(index)
             del self.tabs[index]
+            self.tabsModifiedSignal.emit()
         except IndexError:
             # The tab might have already been deleted previously
Index: src/sas/qtgui/Perspectives/Fitting/FittingWidget.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/FittingWidget.py	(revision 676f1378496edab35bd11ed8e0ce2614c0db17b6)
+++ src/sas/qtgui/Perspectives/Fitting/FittingWidget.py	(revision be8f4b04004041064478b2c80059a875e52e30d3)
@@ -82,5 +82,6 @@
     Main widget for selecting form and structure factor models
     """
-    #constraintAddedSignal = QtCore.pyqtSignal(list)
+    constraintAddedSignal = QtCore.pyqtSignal(list)
+    newModelSignal = QtCore.pyqtSignal()
     def __init__(self, parent=None, data=None, tab_id=1):
 
@@ -574,5 +575,5 @@
         row = self.rowFromName(constraint.param)
         self._model_model.item(row, 1).setChild(0, item)
-        #self.constraintAddedSignal.emit([row])
+        self.constraintAddedSignal.emit([row])
 
         # Show visual hints for the constraint
@@ -626,5 +627,5 @@
             max = self._model_model.item(row, max_col).text()
             # Create a Constraint object
-            constraint = Constraint(param=param, value=value, min=min, max=max)
+            constraint = Constraint(param=param, value=value, min=min, max=max, func=value)
             # Create a new item and add the Constraint object as a child
             item = QtGui.QStandardItem()
@@ -634,5 +635,5 @@
             self._model_model.item(row, min_col).setText(value)
             self._model_model.item(row, max_col).setText(value)
-            #self.constraintAddedSignal.emit([row])
+            self.constraintAddedSignal.emit([row])
             # Show visual hints for the constraint
             font = QtGui.QFont()
@@ -647,7 +648,13 @@
         Delete constraints from selected parameters.
         """
+        self.deleteConstraintOnParameter(param=None)
+
+    def deleteConstraintOnParameter(self, param=None):
+        """
+        Delete the constraint on model parameter 'param'
+        """
         min_col = self.lstParams.itemDelegate().param_min
         max_col = self.lstParams.itemDelegate().param_max
-        for row in self.selectedParameters():
+        for row in range(self._model_model.rowCount()):
             # Get the Constraint object from of the model item
             item = self._model_model.item(row, 1)
@@ -659,4 +666,7 @@
             if not isinstance(constraint, Constraint):
                 continue
+            if param and constraint.param != param:
+                continue
+            # Now we got the right row. Delete the constraint and clean up
             # Retrieve old values and put them on the model
             if constraint.min is not None:
@@ -666,8 +676,11 @@
             # Remove constraint item
             item.removeRow(0)
-            #self.constraintAddedSignal.emit([row])
+            self.constraintAddedSignal.emit([row])
             self.modifyViewOnRow(row)
+            break
+
         self.communicate.statusBarUpdateSignal.emit('Constraint removed')
         pass
+
 
     def getConstraintForRow(self, row):
@@ -687,5 +700,10 @@
         """
         item = self._model_model.item(row,1)
-        return True if (item.hasChildren() and isinstance(item.child(0).data(), Constraint)) else False
+        if item.hasChildren():
+            c = item.child(0).data()
+            if isinstance(c, Constraint) and c.func:
+                return True
+        return False
+        #return True if (item.hasChildren() and isinstance(item.child(0).data(), Constraint)) else False
 
     def selectParameters(self):
@@ -726,6 +744,12 @@
         self.kernel_module.name = model_name
         param_number = self._model_model.rowCount()
+        def preamble(s):
+            func = self._model_model.item(s, 1).child(0).data().func
+            value = self._model_model.item(s, 1).child(0).data().value
+            if func == value:
+                return ""
+            return model_name + "."
         params = [(self._model_model.item(s, 0).text(),
-                    model_name+"."+self._model_model.item(s, 1).child(0).data().func)
+                    preamble(s) +self._model_model.item(s, 1).child(0).data().func)
                     for s in range(param_number) if self.rowHasConstraint(s)]
         return params
@@ -806,4 +830,7 @@
         # Update state stack
         self.updateUndo()
+
+        # Let others know
+        self.newModelSignal.emit()
 
     def onSelectCategory(self):
@@ -1523,4 +1550,6 @@
         # Now we claim the model has been loaded
         self.model_is_loaded = True
+        # Change the model name to a monicker
+        self.kernel_module.name = self.modelName()
 
         # (Re)-create headers
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 be8f4b04004041064478b2c80059a875e52e30d3)
@@ -63,4 +63,7 @@
          </sizepolicy>
         </property>
+        <property name="contextMenuPolicy">
+         <enum>Qt::ActionsContextMenu</enum>
+        </property>
         <property name="alternatingRowColors">
          <bool>true</bool>
@@ -69,19 +72,4 @@
          <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>
@@ -122,4 +110,7 @@
           <verstretch>0</verstretch>
          </sizepolicy>
+        </property>
+        <property name="contextMenuPolicy">
+         <enum>Qt::ActionsContextMenu</enum>
         </property>
         <column>
