Changeset 12db2db6 in sasview for src/sas/qtgui/Perspectives/Fitting/FittingWidget.py
- Timestamp:
- Sep 7, 2018 10:25:19 AM (6 years ago)
- Branches:
- ESS_GUI, ESS_GUI_batch_fitting, ESS_GUI_bumps_abstraction, ESS_GUI_iss1116, ESS_GUI_iss879, ESS_GUI_opencl, ESS_GUI_ordering, ESS_GUI_sync_sascalc
- Children:
- 2f14b5d, 0101c9f
- Parents:
- 339e22b (diff), f0365a2e (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/sas/qtgui/Perspectives/Fitting/FittingWidget.py
r339e22b r12db2db6 90 90 fittingFinishedSignal = QtCore.pyqtSignal(tuple) 91 91 batchFittingFinishedSignal = QtCore.pyqtSignal(tuple) 92 Calc1DFinishedSignal = QtCore.pyqtSignal( tuple)93 Calc2DFinishedSignal = QtCore.pyqtSignal( tuple)92 Calc1DFinishedSignal = QtCore.pyqtSignal(dict) 93 Calc2DFinishedSignal = QtCore.pyqtSignal(dict) 94 94 95 95 def __init__(self, parent=None, data=None, tab_id=1): … … 219 219 # Utility variable to enable unselectable option in category combobox 220 220 self._previous_category_index = 0 221 # Utility variable for multishell display 222 self._last_model_row = 0 221 # Utility variables for multishell display 222 self._n_shells_row = 0 223 self._num_shell_params = 0 223 224 # Dictionary of {model name: model class} for the current category 224 225 self.models = {} … … 247 248 # copy of current kernel model 248 249 self.kernel_module_copy = None 250 251 # dictionaries of current params 252 self.poly_params = {} 253 self.magnet_params = {} 249 254 250 255 # Page id for fitting … … 672 677 Return list of all parameters for the current model 673 678 """ 674 return [self._model_model.item(row).text() for row in range(self._model_model.rowCount())] 679 return [self._model_model.item(row).text() 680 for row in range(self._model_model.rowCount()) 681 if self.isCheckable(row)] 675 682 676 683 def modifyViewOnRow(self, row, font=None, brush=None): … … 700 707 assert isinstance(constraint, Constraint) 701 708 assert 0 <= row <= self._model_model.rowCount() 709 assert self.isCheckable(row) 702 710 703 711 item = QtGui.QStandardItem() … … 720 728 max_col = self.lstParams.itemDelegate().param_max 721 729 for row in self.selectedParameters(): 730 assert(self.isCheckable(row)) 722 731 param = self._model_model.item(row, 0).text() 723 732 value = self._model_model.item(row, 1).text() … … 762 771 max_col = self.lstParams.itemDelegate().param_max 763 772 for row in range(self._model_model.rowCount()): 773 if not self.isCheckable(row): 774 continue 764 775 if not self.rowHasConstraint(row): 765 776 continue … … 790 801 For the given row, return its constraint, if any 791 802 """ 792 try:803 if self.isCheckable(row): 793 804 item = self._model_model.item(row, 1) 794 return item.child(0).data() 795 except AttributeError: 796 # return none when no constraints 797 return None 805 try: 806 return item.child(0).data() 807 except AttributeError: 808 # return none when no constraints 809 pass 810 return None 798 811 799 812 def rowHasConstraint(self, row): … … 801 814 Finds out if row of the main model has a constraint child 802 815 """ 803 item = self._model_model.item(row, 1) 804 if item.hasChildren(): 805 c = item.child(0).data() 806 if isinstance(c, Constraint): 807 return True 816 if self.isCheckable(row): 817 item = self._model_model.item(row, 1) 818 if item.hasChildren(): 819 c = item.child(0).data() 820 if isinstance(c, Constraint): 821 return True 808 822 return False 809 823 … … 812 826 Finds out if row of the main model has an active constraint child 813 827 """ 814 item = self._model_model.item(row, 1) 815 if item.hasChildren(): 816 c = item.child(0).data() 817 if isinstance(c, Constraint) and c.active: 818 return True 828 if self.isCheckable(row): 829 item = self._model_model.item(row, 1) 830 if item.hasChildren(): 831 c = item.child(0).data() 832 if isinstance(c, Constraint) and c.active: 833 return True 819 834 return False 820 835 … … 823 838 Finds out if row of the main model has an active, nontrivial constraint child 824 839 """ 825 item = self._model_model.item(row, 1) 826 if item.hasChildren(): 827 c = item.child(0).data() 828 if isinstance(c, Constraint) and c.func and c.active: 829 return True 840 if self.isCheckable(row): 841 item = self._model_model.item(row, 1) 842 if item.hasChildren(): 843 c = item.child(0).data() 844 if isinstance(c, Constraint) and c.func and c.active: 845 return True 830 846 return False 831 847 … … 1186 1202 # Update the sasmodel 1187 1203 # PD[ratio] -> width, npts -> npts, nsigs -> nsigmas 1188 self.kernel_module.setParam(parameter_name + '.' + delegate.columnDict()[model_column], value) 1204 #self.kernel_module.setParam(parameter_name + '.' + delegate.columnDict()[model_column], value) 1205 key = parameter_name + '.' + delegate.columnDict()[model_column] 1206 self.poly_params[key] = value 1189 1207 1190 1208 # Update plot … … 1195 1213 row = self.getRowFromName(parameter_name) 1196 1214 param_item = self._model_model.item(row) 1215 self._model_model.blockSignals(True) 1197 1216 param_item.child(0).child(0, model_column).setText(item.text()) 1217 self._model_model.blockSignals(False) 1198 1218 1199 1219 def onMagnetModelChange(self, item): … … 1224 1244 # Unparsable field 1225 1245 return 1226 1227 property_index = self._magnet_model.headerData(1, model_column)-1 # Value, min, max, etc. 1228 1229 # Update the parameter value - note: this supports +/-inf as well 1230 self.kernel_module.params[parameter_name] = value 1231 1232 # min/max to be changed in self.kernel_module.details[parameter_name] = ['Ang', 0.0, inf] 1233 self.kernel_module.details[parameter_name][property_index] = value 1234 1235 # Force the chart update when actual parameters changed 1236 if model_column == 1: 1246 delegate = self.lstMagnetic.itemDelegate() 1247 1248 if model_column > 1: 1249 if model_column == delegate.mag_min: 1250 pos = 1 1251 elif model_column == delegate.mag_max: 1252 pos = 2 1253 elif model_column == delegate.mag_unit: 1254 pos = 0 1255 else: 1256 raise AttributeError("Wrong column in magnetism table.") 1257 # min/max to be changed in self.kernel_module.details[parameter_name] = ['Ang', 0.0, inf] 1258 self.kernel_module.details[parameter_name][pos] = value 1259 else: 1260 self.magnet_params[parameter_name] = value 1261 #self.kernel_module.setParam(parameter_name) = value 1262 # Force the chart update when actual parameters changed 1237 1263 self.recalculatePlotData() 1238 1264 … … 1481 1507 # update charts 1482 1508 self.onPlot() 1509 #self.recalculatePlotData() 1510 1483 1511 1484 1512 # Read only value - we can get away by just printing it here … … 1495 1523 # Data going in 1496 1524 data = self.logic.data 1497 model = self.kernel_module1525 model = copy.deepcopy(self.kernel_module) 1498 1526 qmin = self.q_range_min 1499 1527 qmax = self.q_range_max 1528 # add polydisperse/magnet parameters if asked 1529 self.updateKernelModelWithExtraParams(model) 1500 1530 1501 1531 params_to_fit = self.main_params_to_fit … … 1561 1591 # internal so can use closure for param_dict 1562 1592 param_name = str(self._model_model.item(row, 0).text()) 1563 if param_name not in list(param_dict.keys()):1593 if not self.isCheckable(row) or param_name not in list(param_dict.keys()): 1564 1594 return 1565 1595 # modify the param value 1566 1596 param_repr = GuiUtils.formatNumber(param_dict[param_name][0], high=True) 1567 1597 self._model_model.item(row, 1).setText(param_repr) 1598 self.kernel_module.setParam(param_name, param_dict[param_name][0]) 1568 1599 if self.has_error_column: 1569 1600 error_repr = GuiUtils.formatNumber(param_dict[param_name][1], high=True) … … 1573 1604 # Utility function for updateof polydispersity part of the main model 1574 1605 param_name = str(self._model_model.item(row, 0).text())+'.width' 1575 if param_name not in list(param_dict.keys()):1606 if not self.isCheckable(row) or param_name not in list(param_dict.keys()): 1576 1607 return 1577 1608 # modify the param value … … 1607 1638 poly_item.insertColumn(2, [QtGui.QStandardItem("")]) 1608 1639 1609 # block signals temporarily, so we don't end up1610 # updating charts with every single model change on the end of fitting1611 self._model_model.blockSignals(True)1612 1613 1640 if not self.has_error_column: 1614 1641 # create top-level error column … … 1617 1644 self.iterateOverModel(createErrorColumn) 1618 1645 1619 # we need to enable signals for this, otherwise the final column mysteriously disappears (don't ask, I don't1620 # know)1621 self._model_model.blockSignals(False)1622 1646 self._model_model.insertColumn(2, error_column) 1623 self._model_model.blockSignals(True)1624 1647 1625 1648 FittingUtilities.addErrorHeadersToModel(self._model_model) … … 1630 1653 self.has_error_column = True 1631 1654 1655 # block signals temporarily, so we don't end up 1656 # updating charts with every single model change on the end of fitting 1657 self._model_model.itemChanged.disconnect() 1632 1658 self.iterateOverModel(updateFittedValues) 1633 1659 self.iterateOverModel(updatePolyValues) 1634 1635 self._model_model.blockSignals(False) 1660 self._model_model.itemChanged.connect(self.onMainParamsChange) 1636 1661 1637 1662 # Adjust the table cells width. … … 1668 1693 param_repr = GuiUtils.formatNumber(param_dict[param_name][0], high=True) 1669 1694 self._poly_model.item(row_i, 1).setText(param_repr) 1695 self.kernel_module.setParam(param_name, param_dict[param_name][0]) 1670 1696 if self.has_poly_error_column: 1671 1697 error_repr = GuiUtils.formatNumber(param_dict[param_name][1], high=True) 1672 1698 self._poly_model.item(row_i, 2).setText(error_repr) 1673 1674 1699 1675 1700 def createErrorColumn(row_i): … … 1692 1717 # block signals temporarily, so we don't end up 1693 1718 # updating charts with every single model change on the end of fitting 1694 self._poly_model. blockSignals(True)1719 self._poly_model.itemChanged.disconnect() 1695 1720 self.iterateOverPolyModel(updateFittedValues) 1696 self._poly_model. blockSignals(False)1721 self._poly_model.itemChanged.connect(self.onPolyModelChange) 1697 1722 1698 1723 if self.has_poly_error_column: … … 1704 1729 1705 1730 # switch off reponse to model change 1706 self._poly_model.blockSignals(True)1707 1731 self._poly_model.insertColumn(2, error_column) 1708 self._poly_model.blockSignals(False)1709 1732 FittingUtilities.addErrorPolyHeadersToModel(self._poly_model) 1710 1733 … … 1739 1762 param_repr = GuiUtils.formatNumber(param_dict[param_name][0], high=True) 1740 1763 self._magnet_model.item(row, 1).setText(param_repr) 1764 self.kernel_module.setParam(param_name, param_dict[param_name][0]) 1741 1765 if self.has_magnet_error_column: 1742 1766 error_repr = GuiUtils.formatNumber(param_dict[param_name][1], high=True) … … 1758 1782 # block signals temporarily, so we don't end up 1759 1783 # updating charts with every single model change on the end of fitting 1760 self._magnet_model. blockSignals(True)1784 self._magnet_model.itemChanged.disconnect() 1761 1785 self.iterateOverMagnetModel(updateFittedValues) 1762 self._magnet_model. blockSignals(False)1786 self._magnet_model.itemChanged.connect(self.onMagnetModelChange) 1763 1787 1764 1788 if self.has_magnet_error_column: … … 1770 1794 1771 1795 # switch off reponse to model change 1772 self._magnet_model.blockSignals(True)1773 1796 self._magnet_model.insertColumn(2, error_column) 1774 self._magnet_model.blockSignals(False)1775 1797 FittingUtilities.addErrorHeadersToModel(self._magnet_model) 1776 1798 … … 1784 1806 self.cmdPlot.setText("Show Plot") 1785 1807 # Force data recalculation so existing charts are updated 1808 self.showPlot() 1786 1809 self.recalculatePlotData() 1787 self.showPlot()1788 1810 1789 1811 def onSmearingOptionsUpdate(self): … … 1950 1972 # Crete/overwrite model items 1951 1973 self._model_model.clear() 1952 1953 # First, add parameters from the main model 1954 if model_name is not None: 1974 self._poly_model.clear() 1975 self._magnet_model.clear() 1976 1977 if model_name is None: 1978 if structure_factor not in (None, "None"): 1979 # S(Q) on its own, treat the same as a form factor 1980 self.kernel_module = None 1981 self.fromStructureFactorToQModel(structure_factor) 1982 else: 1983 # No models selected 1984 return 1985 else: 1955 1986 self.fromModelToQModel(model_name) 1956 1957 # Then, add structure factor derived parameters 1958 if structure_factor is not None and structure_factor != "None": 1959 if model_name is None: 1960 # Instantiate the current sasmodel for SF-only models 1961 self.kernel_module = self.models[structure_factor]() 1962 self.fromStructureFactorToQModel(structure_factor) 1963 else: 1987 self.addExtraShells() 1988 1964 1989 # Allow the SF combobox visibility for the given sasmodel 1965 1990 self.enableStructureFactorControl(structure_factor) 1991 1992 # Add S(Q) 1966 1993 if self.cbStructureFactor.isEnabled(): 1967 1994 structure_factor = self.cbStructureFactor.currentText() 1968 1995 self.fromStructureFactorToQModel(structure_factor) 1969 1996 1970 # Then, add multishells 1971 if model_name is not None: 1972 # Multishell models need additional treatment 1973 self.addExtraShells() 1974 1975 # Add polydispersity to the model 1976 self.setPolyModel() 1977 # Add magnetic parameters to the model 1978 self.setMagneticModel() 1997 # Add polydispersity to the model 1998 self.poly_params = {} 1999 self.setPolyModel() 2000 # Add magnetic parameters to the model 2001 self.magnet_params = {} 2002 self.setMagneticModel() 1979 2003 1980 2004 # Adjust the table cells width … … 2049 2073 self.shell_names = self.shellNamesList() 2050 2074 2075 # Add heading row 2076 FittingUtilities.addHeadingRowToModel(self._model_model, model_name) 2077 2051 2078 # Update the QModel 2052 new_rows = FittingUtilities.addParametersToModel(self.model_parameters, self.kernel_module, self.is2D)2053 2054 for row in new_rows:2055 self._model_model.appendRow(row)2056 # Update the counter used for multishell display2057 self._last_model_row = self._model_model.rowCount()2079 FittingUtilities.addParametersToModel( 2080 self.model_parameters, 2081 self.kernel_module, 2082 self.is2D, 2083 self._model_model, 2084 self.lstParams) 2058 2085 2059 2086 def fromStructureFactorToQModel(self, structure_factor): … … 2063 2090 if structure_factor is None or structure_factor=="None": 2064 2091 return 2065 structure_module = generate.load_kernel_module(structure_factor) 2066 structure_parameters = modelinfo.make_parameter_table(getattr(structure_module, 'parameters', [])) 2067 2068 structure_kernel = self.models[structure_factor]() 2069 form_kernel = self.kernel_module 2070 2071 self.kernel_module = MultiplicationModel(form_kernel, structure_kernel) 2072 2073 new_rows = FittingUtilities.addSimpleParametersToModel(structure_parameters, self.is2D) 2074 for row in new_rows: 2075 self._model_model.appendRow(row) 2076 # disable fitting of parameters not listed in self.kernel_module (probably radius_effective) 2077 if row[0].text() not in self.kernel_module.params.keys(): 2078 row_num = self._model_model.rowCount() - 1 2079 FittingUtilities.markParameterDisabled(self._model_model, row_num) 2080 2081 # Update the counter used for multishell display 2082 self._last_model_row = self._model_model.rowCount() 2092 2093 if self.kernel_module is None: 2094 # Structure factor is the only selected model; build it and show all its params 2095 self.kernel_module = self.models[structure_factor]() 2096 s_params = self.kernel_module._model_info.parameters 2097 s_params_orig = s_params 2098 2099 else: 2100 s_kernel = self.models[structure_factor]() 2101 p_kernel = self.kernel_module 2102 2103 p_pars_len = len(p_kernel._model_info.parameters.kernel_parameters) 2104 s_pars_len = len(s_kernel._model_info.parameters.kernel_parameters) 2105 2106 self.kernel_module = MultiplicationModel(p_kernel, s_kernel) 2107 all_params = self.kernel_module._model_info.parameters.kernel_parameters 2108 all_param_names = [param.name for param in all_params] 2109 2110 # S(Q) params from the product model are not necessarily the same as those from the S(Q) model; any 2111 # conflicting names with P(Q) params will cause a rename 2112 2113 if "radius_effective_mode" in all_param_names: 2114 # Show all parameters 2115 s_params = modelinfo.ParameterTable(all_params[p_pars_len:p_pars_len+s_pars_len]) 2116 s_params_orig = modelinfo.ParameterTable(s_kernel._model_info.parameters.kernel_parameters) 2117 else: 2118 # Ensure radius_effective is not displayed 2119 s_params_orig = modelinfo.ParameterTable(s_kernel._model_info.parameters.kernel_parameters[1:]) 2120 if "radius_effective" in all_param_names: 2121 s_params = modelinfo.ParameterTable(all_params[p_pars_len+1:p_pars_len+s_pars_len]) 2122 else: 2123 s_params = modelinfo.ParameterTable(all_params[p_pars_len:p_pars_len+s_pars_len-1]) 2124 2125 # Add heading row 2126 FittingUtilities.addHeadingRowToModel(self._model_model, structure_factor) 2127 2128 # Get new rows for QModel 2129 # Any renamed parameters are stored as data in the relevant item, for later handling 2130 FittingUtilities.addSimpleParametersToModel( 2131 s_params, 2132 self.is2D, 2133 s_params_orig, 2134 self._model_model, 2135 self.lstParams) 2083 2136 2084 2137 def haveParamsToFit(self): … … 2106 2159 model_row = item.row() 2107 2160 name_index = self._model_model.index(model_row, 0) 2161 name_item = self._model_model.itemFromIndex(name_index) 2108 2162 2109 2163 # Extract changed value. … … 2114 2168 return 2115 2169 2116 parameter_name = str(self._model_model.data(name_index)) # sld, background etc. 2170 # if the item has user data, this is the actual parameter name (e.g. to handle duplicate names) 2171 if name_item.data(QtCore.Qt.UserRole): 2172 parameter_name = str(name_item.data(QtCore.Qt.UserRole)) 2173 else: 2174 parameter_name = str(self._model_model.data(name_index)) 2117 2175 2118 2176 # Update the parameter value - note: this supports +/-inf as well … … 2237 2295 return self.completed1D if isinstance(self.data, Data1D) else self.completed2D 2238 2296 2297 def updateKernelModelWithExtraParams(self, model=None): 2298 """ 2299 Updates kernel model 'model' with extra parameters from 2300 the polydisp and magnetism tab, if the tabs are enabled 2301 """ 2302 if model is None: return 2303 if not hasattr(model, 'setParam'): return 2304 2305 # add polydisperse parameters if asked 2306 if self.chkPolydispersity.isChecked(): 2307 for key, value in self.poly_params.items(): 2308 model.setParam(key, value) 2309 # add magnetic params if asked 2310 if self.chkMagnetism.isChecked(): 2311 for key, value in self.magnet_params.items(): 2312 model.setParam(key, value) 2313 2239 2314 def calculateQGridForModelExt(self, data=None, model=None, completefn=None, use_threads=True): 2240 2315 """ … … 2244 2319 data = self.data 2245 2320 if model is None: 2246 model = self.kernel_module 2321 model = copy.deepcopy(self.kernel_module) 2322 self.updateKernelModelWithExtraParams(model) 2323 2247 2324 if completefn is None: 2248 2325 completefn = self.methodCompleteForData() … … 2329 2406 new_plots.append(sq_data) 2330 2407 2408 for plot in new_plots: 2409 self.communicate.plotUpdateSignal.emit([plot]) 2410 2411 def complete2D(self, return_data): 2412 """ 2413 Plot the current 2D data 2414 """ 2415 fitted_data = self.logic.new2DPlot(return_data) 2416 residuals = self.calculateResiduals(fitted_data) 2417 self.model_data = fitted_data 2418 new_plots = [fitted_data] 2419 if residuals is not None: 2420 new_plots.append(residuals) 2421 2331 2422 # Update/generate plots 2332 2423 for plot in new_plots: 2333 2424 self.communicate.plotUpdateSignal.emit([plot]) 2334 2335 def complete2D(self, return_data):2336 """2337 Plot the current 2D data2338 """2339 fitted_data = self.logic.new2DPlot(return_data)2340 self.calculateResiduals(fitted_data)2341 self.model_data = fitted_data2342 2425 2343 2426 def calculateResiduals(self, fitted_data): … … 2470 2553 _, min, max = self.kernel_module.details[param_name] 2471 2554 2555 # Update local param dict 2556 self.poly_params[param_name + '.width'] = width 2557 self.poly_params[param_name + '.npts'] = npts 2558 self.poly_params[param_name + '.nsigmas'] = nsigs 2559 2472 2560 # Construct a row with polydisp. related variable. 2473 2561 # This will get added to the polydisp. model … … 2517 2605 def updateFunctionCaption(row): 2518 2606 # Utility function for update of polydispersity function name in the main model 2607 if not self.isCheckable(row): 2608 return 2609 self._model_model.blockSignals(True) 2519 2610 param_name = str(self._model_model.item(row, 0).text()) 2611 self._model_model.blockSignals(False) 2520 2612 if param_name != param.name: 2521 2613 return 2522 2614 # Modify the param value 2615 self._model_model.blockSignals(True) 2523 2616 if self.has_error_column: 2524 2617 # err column changes the indexing … … 2526 2619 else: 2527 2620 self._model_model.item(row, 0).child(0).child(0,4).setText(combo_string) 2621 self._model_model.blockSignals(False) 2528 2622 2529 2623 if combo_string == 'array': … … 2644 2738 param.units] 2645 2739 2740 self.magnet_params[param.name] = param.default 2741 2646 2742 FittingUtilities.addCheckedListToModel(model, checked_list) 2647 2743 … … 2683 2779 2684 2780 self.lstParams.setIndexWidget(shell_index, func) 2685 self._ last_model_row = self._model_model.rowCount()2781 self._n_shells_row = shell_row - 1 2686 2782 2687 2783 # Set the index to the state-kept value … … 2694 2790 """ 2695 2791 # Find row location of the combobox 2696 last_row = self._last_model_row2697 remove_rows = self._ model_model.rowCount() - last_row2792 first_row = self._n_shells_row + 1 2793 remove_rows = self._num_shell_params 2698 2794 2699 2795 if remove_rows > 1: 2700 self._model_model.removeRows(last_row, remove_rows) 2701 2702 FittingUtilities.addShellsToModel(self.model_parameters, self._model_model, index) 2796 self._model_model.removeRows(first_row, remove_rows) 2797 2798 new_rows = FittingUtilities.addShellsToModel( 2799 self.model_parameters, 2800 self._model_model, 2801 index, 2802 first_row, 2803 self.lstParams) 2804 2805 self._num_shell_params = len(new_rows) 2703 2806 self.current_shell_displayed = index 2704 2807
Note: See TracChangeset
for help on using the changeset viewer.