Index: src/sas/qtgui/MainWindow/GuiManager.py
===================================================================
--- src/sas/qtgui/MainWindow/GuiManager.py	(revision e4335ae394f216c131b40e0ebbd740e37d9c6aa7)
+++ src/sas/qtgui/MainWindow/GuiManager.py	(revision 8e2cd7908505a021dacf288aaa6c69c2d7d505e5)
@@ -520,13 +520,16 @@
     def actionCopy(self):
         """
-        """
-        print("actionCopy TRIGGERED")
+        Send a signal to the fitting perspective so parameters
+        can be saved to the clipboard
+        """
+        self.communicate.copyFitParamsSignal.emit("")
         pass
 
     def actionPaste(self):
         """
-        """
-        print("actionPaste TRIGGERED")
-        pass
+        Send a signal to the fitting perspective so parameters
+        from the clipboard can be used to modify the fit state
+        """
+        self.communicate.pasteFitParamsSignal.emit()
 
     def actionReport(self):
@@ -555,13 +558,15 @@
     def actionExcel(self):
         """
-        """
-        print("actionExcel TRIGGERED")
-        pass
+        Send a signal to the fitting perspective so parameters
+        can be saved to the clipboard
+        """
+        self.communicate.copyFitParamsSignal.emit("Excel")
 
     def actionLatex(self):
         """
-        """
-        print("actionLatex TRIGGERED")
-        pass
+        Send a signal to the fitting perspective so parameters
+        can be saved to the clipboard
+        """
+        self.communicate.copyFitParamsSignal.emit("Latex")
 
     #============ VIEW =================
Index: src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py	(revision b87dc1adecb77678c17809b18472323a59e4dbaa)
+++ src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py	(revision 8e2cd7908505a021dacf288aaa6c69c2d7d505e5)
@@ -3,5 +3,4 @@
 from PyQt5 import QtCore
 from PyQt5 import QtGui
-from PyQt5 import QtWidgets
 
 import numpy
@@ -99,5 +98,5 @@
                     continue
                 width = kernel_module.getParam(p.name+'.width')
-                type = kernel_module.getParam(p.name+'.type')
+                ptype = kernel_module.getParam(p.name+'.type')
 
                 item1_2 = QtGui.QStandardItem(str(width))
@@ -107,5 +106,5 @@
                 item1_4 = QtGui.QStandardItem()
                 item1_4.setEditable(False)
-                item1_5 = QtGui.QStandardItem(type)
+                item1_5 = QtGui.QStandardItem(ptype)
                 item1_5.setEditable(False)
                 poly_item.appendRow([item1_1, item1_2, item1_3, item1_4, item1_5])
@@ -458,5 +457,5 @@
     returning the modified (deep) copy of the kernel.
     """
-    assert(isinstance(results, dict))
+    assert isinstance(results, dict)
     local_kernel = copy.deepcopy(kernel)
 
@@ -479,6 +478,6 @@
     for row in range(num_rows):
         param_name = model.item(row, 0).text()
-        checkbox_state = model.item(row,0).checkState() == QtCore.Qt.Checked
-        value= model.item(row, 1).text()
+        checkbox_state = model.item(row, 0).checkState() == QtCore.Qt.Checked
+        value = model.item(row, 1).text()
         column_shift = 0
         if model.columnCount() == 5: # no error column
@@ -509,5 +508,5 @@
     """
     param = []
-    if kernel_module is None: 
+    if kernel_module is None:
         return None
     for param_name in list(kernel_module.params.keys()):
@@ -526,2 +525,107 @@
 
     return param
+
+def formatParameters(parameters):
+    """
+    Prepare the parameter string in the standard SasView layout
+    """
+    assert parameters is not None
+    assert isinstance(parameters, list)
+    output_string = "sasview_parameter_values:"
+    for parameter in parameters:
+        output_string += ",".join([p for p in parameter if p is not None])
+        output_string += ":"
+    return output_string
+
+def formatParametersExcel(parameters):
+    """
+    Prepare the parameter string in the Excel format (tab delimited)
+    """
+    assert parameters is not None
+    assert isinstance(parameters, list)
+    crlf = chr(13) + chr(10)
+    tab = chr(9)
+
+    output_string = ""
+    # names
+    names = ""
+    values = ""
+    for parameter in parameters:
+        names += parameter[0]+tab
+        # Add the error column if fitted
+        if parameter[1] == "True" and parameter[3] is not None:
+            names += parameter[0]+"_err"+tab
+
+        values += parameter[2]+tab
+        if parameter[1] == "True" and parameter[3] is not None:
+            values += parameter[3]+tab
+        # add .npts and .nsigmas when necessary
+        if parameter[0][-6:] == ".width":
+            names += parameter[0].replace('.width', '.nsigmas') + tab
+            names += parameter[0].replace('.width', '.npts') + tab
+            values += parameter[5] + tab + parameter[4] + tab
+
+    output_string = names + crlf + values
+    return output_string
+
+def formatParametersLatex(parameters):
+    """
+    Prepare the parameter string in latex
+    """
+    assert parameters is not None
+    assert isinstance(parameters, list)
+    output_string = r'\begin{table}'
+    output_string += r'\begin{tabular}[h]'
+
+    crlf = chr(13) + chr(10)
+    output_string += '{|'
+    output_string += 'l|l|'*len(parameters)
+    output_string += r'}\hline'
+    output_string += crlf
+
+    for index, parameter in enumerate(parameters):
+        name = parameter[0] # Parameter name
+        output_string += name.replace('_', r'\_')  # Escape underscores
+        # Add the error column if fitted
+        if parameter[1] == "True" and parameter[3] is not None:
+            output_string += ' & '
+            output_string += parameter[0]+r'\_err'
+
+        if index < len(parameters) - 1:
+            output_string += ' & '
+
+        # add .npts and .nsigmas when necessary
+        if parameter[0][-6:] == ".width":
+            output_string += parameter[0].replace('.width', '.nsigmas') + ' & '
+            output_string += parameter[0].replace('.width', '.npts')
+
+            if index < len(parameters) - 1:
+                output_string += ' & '
+
+    output_string += r'\\ \hline'
+    output_string += crlf
+
+    # Construct row of values and errors
+    for index, parameter in enumerate(parameters):
+        output_string += parameter[2]
+        if parameter[1] == "True" and parameter[3] is not None:
+            output_string += ' & '
+            output_string += parameter[3]
+
+        if index < len(parameters) - 1:
+            output_string += ' & '
+
+        # add .npts and .nsigmas when necessary
+        if parameter[0][-6:] == ".width":
+            output_string += parameter[5] + ' & '
+            output_string += parameter[4]
+
+            if index < len(parameters) - 1:
+                output_string += ' & '
+
+    output_string += r'\\ \hline'
+    output_string += crlf
+    output_string += r'\end{tabular}'
+    output_string += r'\end{table}'
+
+    return output_string
Index: src/sas/qtgui/Perspectives/Fitting/FittingWidget.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/FittingWidget.py	(revision 14acf927010d673256d31cb372e20e7fd92fc381)
+++ src/sas/qtgui/Perspectives/Fitting/FittingWidget.py	(revision 97df8a93fcfe23a3b54f1f4b5365775da012c4a2)
@@ -1,5 +1,4 @@
 import json
 import os
-import copy
 from collections import defaultdict
 
@@ -70,5 +69,5 @@
     """
     def __init__(self, parent=None):
-        QtGui.QStandardItemModel.__init__(self,parent)
+        QtGui.QStandardItemModel.__init__(self, parent)
 
     def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
@@ -153,5 +152,5 @@
     def logic(self):
         # make sure the logic contains at least one element
-        assert(self._logic)
+        assert self._logic
         # logic connected to the currently shown data
         return self._logic[self.data_index]
@@ -208,5 +207,5 @@
         self.is_chain_fitting = False
         # Is the fit job running?
-        self.fit_started=False
+        self.fit_started = False
         # The current fit thread
         self.calc_fit = None
@@ -324,4 +323,9 @@
             }
 
+            QTreeView::item {
+                border: 1px;
+                padding: 2px 15px;
+            }
+
             QTreeView::item:hover {
                 background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #e7effd, stop: 1 #cbdaf1);
@@ -517,5 +521,6 @@
         self.communicate.saveAnalysisSignal.connect(self.savePageState)
         self.smearing_widget.smearingChangedSignal.connect(self.onSmearingOptionsUpdate)
-        #self.communicate.saveReportSignal.connect(self.saveReport)
+        self.communicate.copyFitParamsSignal.connect(self.onParameterCopy)
+        self.communicate.pasteFitParamsSignal.connect(self.onParameterPaste)
 
     def modelName(self):
@@ -557,6 +562,6 @@
             return menu
         # Select for fitting
-        param_string = "parameter " if num_rows==1 else "parameters "
-        to_string = "to its current value" if num_rows==1 else "to their current values"
+        param_string = "parameter " if num_rows == 1 else "parameters "
+        to_string = "to its current value" if num_rows == 1 else "to their current values"
         has_constraints = any([self.rowHasConstraint(i) for i in rows])
 
@@ -613,5 +618,5 @@
         # There have to be only two rows selected. The caller takes care of that
         # but let's check the correctness.
-        assert(len(selected_rows)==2)
+        assert len(selected_rows) == 2
 
         params_list = [s.data() for s in selected_rows]
@@ -680,6 +685,6 @@
         """
         # Create a new item and add the Constraint object as a child
-        assert(isinstance(constraint, Constraint))
-        assert(0<=row<=self._model_model.rowCount())
+        assert isinstance(constraint, Constraint)
+        assert 0 <= row <= self._model_model.rowCount()
 
         item = QtGui.QStandardItem()
@@ -732,5 +737,5 @@
         Delete constraints from selected parameters.
         """
-        params =  [s.data() for s in self.lstParams.selectionModel().selectedRows()
+        params = [s.data() for s in self.lstParams.selectionModel().selectedRows()
                    if self.isCheckable(s.row())]
         for param in params:
@@ -783,5 +788,5 @@
         Finds out if row of the main model has a constraint child
         """
-        item = self._model_model.item(row,1)
+        item = self._model_model.item(row, 1)
         if item.hasChildren():
             c = item.child(0).data()
@@ -794,5 +799,5 @@
         Finds out if row of the main model has an active constraint child
         """
-        item = self._model_model.item(row,1)
+        item = self._model_model.item(row, 1)
         if item.hasChildren():
             c = item.child(0).data()
@@ -805,5 +810,5 @@
         Finds out if row of the main model has an active, nontrivial constraint child
         """
-        item = self._model_model.item(row,1)
+        item = self._model_model.item(row, 1)
         if item.hasChildren():
             c = item.child(0).data()
@@ -1443,5 +1448,5 @@
         qmax = self.q_range_max
         params_to_fit = self.parameters_to_fit
-        if (not params_to_fit):
+        if not params_to_fit:
             raise ValueError('Fitting requires at least one parameter to optimize.')
 
@@ -1562,4 +1567,11 @@
         self.has_error_column = True
 
+    def iterateOverPolyModel(self, func):
+        """
+        Take func and throw it inside the poly model row loop
+        """
+        for row_i in range(self._poly_model.rowCount()):
+            func(row_i)
+
     def updatePolyModelFromList(self, param_dict):
         """
@@ -1569,11 +1581,4 @@
         if not dict:
             return
-
-        def iterateOverPolyModel(func):
-            """
-            Take func and throw it inside the poly model row loop
-            """
-            for row_i in range(self._poly_model.rowCount()):
-                func(row_i)
 
         def updateFittedValues(row_i):
@@ -1613,5 +1618,5 @@
         # updating charts with every single model change on the end of fitting
         self._poly_model.blockSignals(True)
-        iterateOverPolyModel(updateFittedValues)
+        self.iterateOverPolyModel(updateFittedValues)
         self._poly_model.blockSignals(False)
 
@@ -1621,5 +1626,5 @@
         self.lstPoly.itemDelegate().addErrorColumn()
         error_column = []
-        iterateOverPolyModel(createErrorColumn)
+        self.iterateOverPolyModel(createErrorColumn)
 
         # switch off reponse to model change
@@ -1630,4 +1635,11 @@
 
         self.has_poly_error_column = True
+
+    def iterateOverMagnetModel(self, func):
+        """
+        Take func and throw it inside the magnet model row loop
+        """
+        for row_i in range(self._model_model.rowCount()):
+            func(row_i)
 
     def updateMagnetModelFromList(self, param_dict):
@@ -1679,5 +1691,5 @@
         # updating charts with every single model change on the end of fitting
         self._magnet_model.blockSignals(True)
-        iterateOverMagnetModel(updateFittedValues)
+        self.iterateOverMagnetModel(updateFittedValues)
         self._magnet_model.blockSignals(False)
 
@@ -1687,5 +1699,5 @@
         self.lstMagnetic.itemDelegate().addErrorColumn()
         error_column = []
-        iterateOverMagnetModel(createErrorColumn)
+        self.iterateOverMagnetModel(createErrorColumn)
 
         # switch off reponse to model change
@@ -2803,3 +2815,217 @@
         #self._copy_parameters_state(self.fixed_param, self.state.fixed_param)
 
-
+    def onParameterCopy(self, format=None):
+        """
+        Copy current parameters into the clipboard
+        """
+        # run a loop over all parameters and pull out
+        # first - regular params
+        param_list = []
+        def gatherParams(row):
+            """
+            Create list of main parameters based on _model_model
+            """
+            param_name = str(self._model_model.item(row, 0).text())
+            param_checked = str(self._model_model.item(row, 0).checkState() == QtCore.Qt.Checked)
+            param_value = str(self._model_model.item(row, 1).text())
+            param_error = None
+            column_offset = 0
+            if self.has_error_column:
+                param_error = str(self._model_model.item(row, 2).text())
+                column_offset = 1
+            param_min = str(self._model_model.item(row, 2+column_offset).text())
+            param_max = str(self._model_model.item(row, 3+column_offset).text())
+            param_list.append([param_name, param_checked, param_value, param_error, param_min, param_max])
+
+        def gatherPolyParams(row):
+            """
+            Create list of polydisperse parameters based on _poly_model
+            """
+            param_name = str(self._poly_model.item(row, 0).text()).split()[-1]
+            param_checked = str(self._poly_model.item(row, 0).checkState() == QtCore.Qt.Checked)
+            param_value = str(self._poly_model.item(row, 1).text())
+            param_error = None
+            column_offset = 0
+            if self.has_poly_error_column:
+                param_error = str(self._poly_model.item(row, 2).text())
+                column_offset = 1
+            param_min   = str(self._poly_model.item(row, 2+column_offset).text())
+            param_max   = str(self._poly_model.item(row, 3+column_offset).text())
+            param_npts  = str(self._poly_model.item(row, 4+column_offset).text())
+            param_nsigs = str(self._poly_model.item(row, 5+column_offset).text())
+            param_fun   = str(self._poly_model.item(row, 6+column_offset).text()).rstrip()
+            # width
+            name = param_name+".width"
+            param_list.append([name, param_checked, param_value, param_error,
+                                param_npts, param_nsigs, param_min, param_max, param_fun])
+
+        def gatherMagnetParams(row):
+            """
+            Create list of magnetic parameters based on _magnet_model
+            """
+            param_name = str(self._magnet_model.item(row, 0).text())
+            param_checked = str(self._magnet_model.item(row, 0).checkState() == QtCore.Qt.Checked)
+            param_value = str(self._magnet_model.item(row, 1).text())
+            param_error = None
+            column_offset = 0
+            if self.has_magnet_error_column:
+                param_error = str(self._magnet_model.item(row, 2).text())
+                column_offset = 1
+            param_min = str(self._magnet_model.item(row, 2+column_offset).text())
+            param_max = str(self._magnet_model.item(row, 3+column_offset).text())
+            param_list.append([param_name, param_checked, param_value, param_error, param_min, param_max])
+
+        self.iterateOverModel(gatherParams)
+        if self.chkPolydispersity.isChecked():
+            self.iterateOverPolyModel(gatherPolyParams)
+        if self.chkMagnetism.isChecked() and self.chkMagnetism.isEnabled():
+            self.iterateOverMagnetModel(sgatherMagnetParams)
+
+        if format=="":
+            formatted_output = FittingUtilities.formatParameters(param_list)
+        elif format == "Excel":
+            formatted_output = FittingUtilities.formatParametersExcel(param_list)
+        elif format == "Latex":
+            formatted_output = FittingUtilities.formatParametersLatex(param_list)
+        else:
+            raise AttributeError("Bad format specifier.")
+
+        # Dump formatted_output to the clipboard
+        cb = QtWidgets.QApplication.clipboard()
+        cb.setText(formatted_output)
+
+    def onParameterPaste(self):
+        """
+        Use the clipboard to update fit state
+        """
+        # Check if the clipboard contains right stuff
+        cb = QtWidgets.QApplication.clipboard()
+        cb_text = cb.text()
+
+        context = {}
+        # put the text into dictionary
+        lines = cb_text.split(':')
+        if lines[0] != 'sasview_parameter_values':
+            return False
+        for line in lines[1:-1]:
+            if len(line) != 0:
+                item = line.split(',')
+                check = item[1]
+                name = item[0]
+                value = item[2]
+                # Transfer the text to content[dictionary]
+                context[name] = [check, value]
+
+                # limits
+                limit_lo = item[3]
+                context[name].append(limit_lo)
+                limit_hi = item[4]
+                context[name].append(limit_hi)
+
+                # Polydisp
+                if len(item) > 5:
+                    value = item[5]
+                    context[name].append(value)
+                    try:
+                        value = item[6]
+                        context[name].append(value)
+                        value = item[7]
+                        context[name].append(value)
+                    except IndexError:
+                        pass
+
+        self.updateFullModel(context)
+        self.updateFullPolyModel(context)
+
+    def updateFullModel(self, param_dict):
+        """
+        Update the model with new parameters
+        """
+        assert isinstance(param_dict, dict)
+        if not dict:
+            return
+
+        def updateFittedValues(row):
+            # Utility function for main model update
+            # internal so can use closure for param_dict
+            param_name = str(self._model_model.item(row, 0).text())
+            if param_name not in list(param_dict.keys()):
+                return
+            # checkbox state
+            param_checked = QtCore.Qt.Checked if param_dict[param_name][0] == "True" else QtCore.Qt.Unchecked
+            self._model_model.item(row, 0).setCheckState(param_checked)
+
+            # modify the param value
+            param_repr = GuiUtils.formatNumber(param_dict[param_name][1], high=True)
+            self._model_model.item(row, 1).setText(param_repr)
+
+            # Potentially the error column
+            ioffset = 0
+            if len(param_dict[param_name])>4 and self.has_error_column:
+                # error values are not editable - no need to update
+                #error_repr = GuiUtils.formatNumber(param_dict[param_name][2], high=True)
+                #self._model_model.item(row, 2).setText(error_repr)
+                ioffset = 1
+            # min/max
+            param_repr = GuiUtils.formatNumber(param_dict[param_name][2+ioffset], high=True)
+            self._model_model.item(row, 2+ioffset).setText(param_repr)
+            param_repr = GuiUtils.formatNumber(param_dict[param_name][3+ioffset], high=True)
+            self._model_model.item(row, 3+ioffset).setText(param_repr)
+
+        # block signals temporarily, so we don't end up
+        # updating charts with every single model change on the end of fitting
+        self._model_model.blockSignals(True)
+        self.iterateOverModel(updateFittedValues)
+        self._model_model.blockSignals(False)
+
+    def updateFullPolyModel(self, param_dict):
+        """
+        Update the polydispersity model with new parameters, create the errors column
+        """
+        assert isinstance(param_dict, dict)
+        if not dict:
+            return
+
+        def updateFittedValues(row):
+            # Utility function for main model update
+            # internal so can use closure for param_dict
+            if row >= self._poly_model.rowCount():
+                return
+            param_name = str(self._poly_model.item(row, 0).text()).rsplit()[-1] + '.width'
+            if param_name not in list(param_dict.keys()):
+                return
+            # checkbox state
+            param_checked = QtCore.Qt.Checked if param_dict[param_name][0] == "True" else QtCore.Qt.Unchecked
+            self._poly_model.item(row,0).setCheckState(param_checked)
+
+            # modify the param value
+            param_repr = GuiUtils.formatNumber(param_dict[param_name][1], high=True)
+            self._poly_model.item(row, 1).setText(param_repr)
+
+            # Potentially the error column
+            ioffset = 0
+            if len(param_dict[param_name])>4 and self.has_poly_error_column:
+                ioffset = 1
+            # min
+            param_repr = GuiUtils.formatNumber(param_dict[param_name][2+ioffset], high=True)
+            self._poly_model.item(row, 2+ioffset).setText(param_repr)
+            # max
+            param_repr = GuiUtils.formatNumber(param_dict[param_name][3+ioffset], high=True)
+            self._poly_model.item(row, 3+ioffset).setText(param_repr)
+            # Npts
+            param_repr = GuiUtils.formatNumber(param_dict[param_name][4+ioffset], high=True)
+            self._poly_model.item(row, 4+ioffset).setText(param_repr)
+            # Nsigs
+            param_repr = GuiUtils.formatNumber(param_dict[param_name][5+ioffset], high=True)
+            self._poly_model.item(row, 5+ioffset).setText(param_repr)
+
+            param_repr = GuiUtils.formatNumber(param_dict[param_name][5+ioffset], high=True)
+            self._poly_model.item(row, 5+ioffset).setText(param_repr)
+
+        # block signals temporarily, so we don't end up
+        # updating charts with every single model change on the end of fitting
+        self._poly_model.blockSignals(True)
+        self.iterateOverPolyModel(updateFittedValues)
+        self._poly_model.blockSignals(False)
+
+
Index: src/sas/qtgui/Perspectives/Fitting/ViewDelegate.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/ViewDelegate.py	(revision cf8d6c936b761c57c471091fdda6fc4c8f761127)
+++ src/sas/qtgui/Perspectives/Fitting/ViewDelegate.py	(revision 97df8a93fcfe23a3b54f1f4b5365775da012c4a2)
@@ -180,5 +180,5 @@
         Overwrite generic painter for certain columns
         """
-        if index.column() in (self.poly_min, self.poly_max):
+        if index.column() in (self.poly_pd, self.poly_min, self.poly_max):
             # Units - present in nice HTML
             options = QtWidgets.QStyleOptionViewItem(option)
@@ -263,5 +263,5 @@
         Overwrite generic painter for certain columns
         """
-        if index.column() in (self.mag_min, self.mag_max, self.mag_unit):
+        if index.column() in (self.mag_value, self.mag_min, self.mag_max, self.mag_unit):
             # Units - present in nice HTML
             options = QtWidgets.QStyleOptionViewItem(option)
@@ -288,5 +288,5 @@
             rect = textRect.topLeft()
             y = rect.y()
-            y += 5.0 # magic value for rendering nice display in the table
+            y += 6.0 # magic value for rendering nice display in the table
             rect.setY(y)
             painter.translate(rect)
Index: src/sas/qtgui/Utilities/GuiUtils.py
===================================================================
--- src/sas/qtgui/Utilities/GuiUtils.py	(revision 6ff103a20debc0d53a518340970788a2c7b2ad65)
+++ src/sas/qtgui/Utilities/GuiUtils.py	(revision 8e2cd7908505a021dacf288aaa6c69c2d7d505e5)
@@ -254,4 +254,10 @@
     # Mask Editor requested
     maskEditorSignal = QtCore.pyqtSignal(Data2D)
+
+    # Fitting parameter copy to clipboard
+    copyFitParamsSignal = QtCore.pyqtSignal(str)
+
+    # Fitting parameter paste from clipboard
+    pasteFitParamsSignal = QtCore.pyqtSignal()
 
 def updateModelItemWithPlot(item, update_data, name=""):
