Changeset dd2c2a31 in sasview for src/sas/qtgui/Perspectives
- Timestamp:
- Sep 8, 2018 9:51:20 AM (6 years ago)
- Parents:
- 5e0891b (diff), e0da307 (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. - git-author:
- Ingo Breßler <dev@…> (09/08/18 09:51:20)
- git-committer:
- GitHub <noreply@…> (09/08/18 09:51:20)
- Location:
- src/sas/qtgui/Perspectives/Fitting
- Files:
-
- 1 added
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
src/sas/qtgui/Perspectives/Fitting/FittingLogic.py
rb4d05bd r9ba91b7 161 161 Create a new 1D data instance based on fitting results 162 162 """ 163 # Unpack return data from Calc1D 164 x, y, page_id, state, weight,\ 165 fid, toggle_mode_on, \ 166 elapsed, index, model, \ 167 data, update_chisqr, source, \ 168 unsmeared_output, unsmeared_data, unsmeared_error, \ 169 pq_values, sq_values = return_data 170 171 return self._create1DPlot(tab_id, x, y, model, data) 163 return self._create1DPlot(tab_id, return_data['x'], return_data['y'], 164 return_data['model'], return_data['data']) 172 165 173 166 def new2DPlot(self, return_data): … … 175 168 Create a new 2D data instance based on fitting results 176 169 """ 177 image , data, page_id, model, state, toggle_mode_on,\178 elapsed, index, fid, qmin, qmax, weight, \179 update_chisqr, source = return_data170 image = return_data['image'] 171 data = return_data['data'] 172 model = return_data['model'] 180 173 181 174 np.nan_to_num(image) … … 183 176 new_plot.name = model.name + '2d' 184 177 new_plot.title = "Analytical model 2D " 185 new_plot.id = str( page_id) + " " + data.name186 new_plot.group_id = str( page_id) + " Model2D"178 new_plot.id = str(return_data['page_id']) + " " + data.name 179 new_plot.group_id = str(return_data['page_id']) + " Model2D" 187 180 new_plot.detector = data.detector 188 181 new_plot.source = data.source … … 218 211 (pq_plot, sq_plot). If either are unavailable, the corresponding plot is None. 219 212 """ 220 # Unpack return data from Calc1D 221 x, y, page_id, state, weight, \ 222 fid, toggle_mode_on, \ 223 elapsed, index, model, \ 224 data, update_chisqr, source, \ 225 unsmeared_output, unsmeared_data, unsmeared_error, \ 226 pq_values, sq_values = return_data 227 228 pq_plot = None 229 sq_plot = None 230 231 if pq_values is not None: 232 pq_plot = self._create1DPlot(tab_id, x, pq_values, model, data, component="P(Q)") 233 if sq_values is not None: 234 sq_plot = self._create1DPlot(tab_id, x, sq_values, model, data, component="S(Q)") 235 236 return pq_plot, sq_plot 213 plots = [] 214 for name, result in return_data['intermediate_results'].items(): 215 plots.append(self._create1DPlot(tab_id, return_data['x'], result, 216 return_data['model'], return_data['data'], 217 component=name)) 218 return plots 237 219 238 220 def computeDataRange(self): -
src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py
rb764ae5 r01b4877 8 8 from sas.qtgui.Plotting.PlotterData import Data1D 9 9 from sas.qtgui.Plotting.PlotterData import Data2D 10 11 from sas.qtgui.Perspectives.Fitting.AssociatedComboBox import AssociatedComboBox 10 12 11 13 model_header_captions = ['Parameter', 'Value', 'Min', 'Max', 'Units'] … … 61 63 return (param_name, param_length) 62 64 63 def addParametersToModel(parameters, kernel_module, is2D): 64 """ 65 Update local ModelModel with sasmodel parameters 65 def createFixedChoiceComboBox(param, item_row): 66 """ 67 Determines whether param is a fixed-choice parameter, modifies items in item_row appropriately and returns a combo 68 box containing the fixed choices. Returns None if param is not fixed-choice. 69 70 item_row is a list of QStandardItem objects for insertion into the parameter table. 71 """ 72 73 # Determine whether this is a fixed-choice parameter. There are lots of conditionals, simply because the 74 # implementation is not yet concrete; there are several possible indicators that the parameter is fixed-choice. 75 # TODO: (when the sasmodels implementation is concrete, clean this up) 76 choices = None 77 if isinstance(param.choices, (list, tuple)) and len(param.choices) > 0: 78 # The choices property is concrete in sasmodels, probably will use this 79 choices = param.choices 80 elif isinstance(param.units, (list, tuple)): 81 choices = [str(x) for x in param.units] 82 83 cbox = None 84 if choices is not None: 85 # Use combo box for input, if it is fixed-choice 86 cbox = AssociatedComboBox(item_row[1], idx_as_value=True) 87 cbox.addItems(choices) 88 item_row[2].setEditable(False) 89 item_row[3].setEditable(False) 90 91 return cbox 92 93 def addParametersToModel(parameters, kernel_module, is2D, model=None, view=None): 94 """ 95 Update local ModelModel with sasmodel parameters. 96 Actually appends to model, if model and view params are not None. 97 Always returns list of lists of QStandardItems. 66 98 """ 67 99 multishell_parameters = getIterParams(parameters) … … 72 104 else: 73 105 params = parameters.iq_parameters 74 item = [] 106 107 rows = [] 75 108 for param in params: 76 109 # don't include shell parameters 77 110 if param.name == multishell_param_name: 78 111 continue 112 79 113 # Modify parameter name from <param>[n] to <param>1 80 114 item_name = param.name 81 115 if param in multishell_parameters: 82 116 continue 83 # item_name = replaceShellName(param.name, 1)84 117 85 118 item1 = QtGui.QStandardItem(item_name) 86 119 item1.setCheckable(True) 87 120 item1.setEditable(False) 88 # item_err = QtGui.QStandardItem() 121 89 122 # check for polydisp params 90 123 if param.polydisperse: … … 93 126 item1_1 = QtGui.QStandardItem("Distribution") 94 127 item1_1.setEditable(False) 128 95 129 # Find param in volume_params 96 130 for p in parameters.form_volume_parameters: … … 99 133 width = kernel_module.getParam(p.name+'.width') 100 134 ptype = kernel_module.getParam(p.name+'.type') 101 102 135 item1_2 = QtGui.QStandardItem(str(width)) 103 136 item1_2.setEditable(False) … … 110 143 poly_item.appendRow([item1_1, item1_2, item1_3, item1_4, item1_5]) 111 144 break 145 112 146 # Add the polydisp item as a child 113 147 item1.appendRow([poly_item]) 148 114 149 # Param values 115 150 item2 = QtGui.QStandardItem(str(param.default)) 116 # TODO: the error column.117 # Either add a proxy model or a custom view delegate118 #item_err = QtGui.QStandardItem()119 151 item3 = QtGui.QStandardItem(str(param.limits[0])) 120 152 item4 = QtGui.QStandardItem(str(param.limits[1])) 121 item5 = QtGui.QStandardItem( param.units)153 item5 = QtGui.QStandardItem(str(param.units)) 122 154 item5.setEditable(False) 123 item.append([item1, item2, item3, item4, item5]) 124 return item 125 126 def addSimpleParametersToModel(parameters, is2D): 127 """ 128 Update local ModelModel with sasmodel parameters 155 156 # Check if fixed-choice (returns combobox, if so, also makes some items uneditable) 157 row = [item1, item2, item3, item4, item5] 158 cbox = createFixedChoiceComboBox(param, row) 159 160 # Append to the model and use the combobox, if required 161 if None not in (model, view): 162 model.appendRow(row) 163 if cbox: 164 view.setIndexWidget(item2.index(), cbox) 165 rows.append(row) 166 167 return rows 168 169 def addSimpleParametersToModel(parameters, is2D, parameters_original=None, model=None, view=None, row_num=None): 170 """ 171 Update local ModelModel with sasmodel parameters (non-dispersed, non-magnetic) 172 Actually appends to model, if model and view params are not None. 173 Always returns list of lists of QStandardItems. 174 175 parameters_original: list of parameters before any tagging on their IDs, e.g. for product model (so that those are 176 the display names; see below) 129 177 """ 130 178 if is2D: … … 132 180 else: 133 181 params = parameters.iq_parameters 134 item = [] 135 for param in params: 182 183 if parameters_original: 184 # 'parameters_original' contains the parameters as they are to be DISPLAYED, while 'parameters' 185 # contains the parameters as they were renamed; this is for handling name collisions in product model. 186 # The 'real name' of the parameter will be stored in the item's user data. 187 if is2D: 188 params_orig = [p for p in parameters_original.kernel_parameters if p.type != 'magnetic'] 189 else: 190 params_orig = parameters_original.iq_parameters 191 else: 192 # no difference in names anyway 193 params_orig = params 194 195 rows = [] 196 for param, param_orig in zip(params, params_orig): 136 197 # Create the top level, checkable item 137 item_name = param .name198 item_name = param_orig.name 138 199 item1 = QtGui.QStandardItem(item_name) 200 item1.setData(param.name, QtCore.Qt.UserRole) 139 201 item1.setCheckable(True) 140 202 item1.setEditable(False) 203 141 204 # Param values 142 205 # TODO: add delegate for validation of cells 143 206 item2 = QtGui.QStandardItem(str(param.default)) 144 item4 = QtGui.QStandardItem(str(param.limits[0])) 145 item5 = QtGui.QStandardItem(str(param.limits[1])) 146 item6 = QtGui.QStandardItem(param.units) 147 item6.setEditable(False) 148 item.append([item1, item2, item4, item5, item6]) 149 return item 207 item3 = QtGui.QStandardItem(str(param.limits[0])) 208 item4 = QtGui.QStandardItem(str(param.limits[1])) 209 item5 = QtGui.QStandardItem(str(param.units)) 210 item5.setEditable(False) 211 212 # Check if fixed-choice (returns combobox, if so, also makes some items uneditable) 213 row = [item1, item2, item3, item4, item5] 214 cbox = createFixedChoiceComboBox(param, row) 215 216 # Append to the model and use the combobox, if required 217 if None not in (model, view): 218 if row_num is None: 219 model.appendRow(row) 220 else: 221 model.insertRow(row_num, row) 222 row_num += 1 223 224 if cbox: 225 view.setIndexWidget(item2.index(), cbox) 226 227 rows.append(row) 228 229 return rows 150 230 151 231 def markParameterDisabled(model, row): … … 182 262 model.appendRow(item_list) 183 263 264 def addHeadingRowToModel(model, name): 265 """adds a non-interactive top-level row to the model""" 266 header_row = [QtGui.QStandardItem() for i in range(5)] 267 header_row[0].setText(name) 268 269 font = header_row[0].font() 270 font.setBold(True) 271 header_row[0].setFont(font) 272 273 for item in header_row: 274 item.setEditable(False) 275 item.setCheckable(False) 276 item.setSelectable(False) 277 278 model.appendRow(header_row) 279 184 280 def addHeadersToModel(model): 185 281 """ … … 227 323 model.header_tooltips = copy.copy(poly_header_error_tooltips) 228 324 229 def addShellsToModel(parameters, model, index): 230 """ 231 Find out multishell parameters and update the model with the requested number of them 325 def addShellsToModel(parameters, model, index, row_num=None, view=None): 326 """ 327 Find out multishell parameters and update the model with the requested number of them. 328 Inserts them after the row at row_num, if not None; otherwise, appends to end. 329 If view param is not None, supports fixed-choice params. 330 Returns a list of lists of QStandardItem objects. 232 331 """ 233 332 multishell_parameters = getIterParams(parameters) 234 333 334 rows = [] 235 335 for i in range(index): 236 336 for par in multishell_parameters: … … 250 350 item1_3 = QtGui.QStandardItem(str(p.limits[0])) 251 351 item1_4 = QtGui.QStandardItem(str(p.limits[1])) 252 item1_5 = QtGui.QStandardItem( p.units)352 item1_5 = QtGui.QStandardItem(str(p.units)) 253 353 poly_item.appendRow([item1_1, item1_2, item1_3, item1_4, item1_5]) 254 354 break … … 258 358 item3 = QtGui.QStandardItem(str(par.limits[0])) 259 359 item4 = QtGui.QStandardItem(str(par.limits[1])) 260 item5 = QtGui.QStandardItem(par.units) 261 model.appendRow([item1, item2, item3, item4, item5]) 360 item5 = QtGui.QStandardItem(str(par.units)) 361 item5.setEditable(False) 362 363 # Check if fixed-choice (returns combobox, if so, also makes some items uneditable) 364 row = [item1, item2, item3, item4, item5] 365 cbox = createFixedChoiceComboBox(par, row) 366 367 # Always add to the model 368 if row_num is None: 369 model.appendRow(row) 370 else: 371 model.insertRow(row_num, row) 372 row_num += 1 373 374 # Apply combobox if required 375 if None not in (view, cbox): 376 view.setIndexWidget(item2.index(), cbox) 377 378 rows.append(row) 379 380 return rows 262 381 263 382 def calculateChi2(reference_data, current_data): -
src/sas/qtgui/Perspectives/Fitting/FittingWidget.py
rf84d793 r5e0891b 48 48 from sas.qtgui.Perspectives.Fitting.ReportPageLogic import ReportPageLogic 49 49 50 51 50 TAB_MAGNETISM = 4 52 51 TAB_POLY = 3 … … 91 90 fittingFinishedSignal = QtCore.pyqtSignal(tuple) 92 91 batchFittingFinishedSignal = QtCore.pyqtSignal(tuple) 93 Calc1DFinishedSignal = QtCore.pyqtSignal( tuple)94 Calc2DFinishedSignal = QtCore.pyqtSignal( tuple)92 Calc1DFinishedSignal = QtCore.pyqtSignal(dict) 93 Calc2DFinishedSignal = QtCore.pyqtSignal(dict) 95 94 96 95 def __init__(self, parent=None, data=None, tab_id=1): … … 188 187 189 188 # Overwrite data type descriptor 189 190 190 self.is2D = True if isinstance(self.logic.data, Data2D) else False 191 191 … … 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 … … 558 563 When clicked on white space: model description 559 564 """ 560 rows = [s.row() for s in self.lstParams.selectionModel().selectedRows()] 565 rows = [s.row() for s in self.lstParams.selectionModel().selectedRows() 566 if self.isCheckable(s.row())] 561 567 menu = self.showModelDescription() if not rows else self.modelContextMenu(rows) 562 568 try: … … 672 678 Return list of all parameters for the current model 673 679 """ 674 return [self._model_model.item(row).text() for row in range(self._model_model.rowCount())] 680 return [self._model_model.item(row).text() 681 for row in range(self._model_model.rowCount()) 682 if self.isCheckable(row)] 675 683 676 684 def modifyViewOnRow(self, row, font=None, brush=None): … … 700 708 assert isinstance(constraint, Constraint) 701 709 assert 0 <= row <= self._model_model.rowCount() 710 assert self.isCheckable(row) 702 711 703 712 item = QtGui.QStandardItem() … … 720 729 max_col = self.lstParams.itemDelegate().param_max 721 730 for row in self.selectedParameters(): 731 assert(self.isCheckable(row)) 722 732 param = self._model_model.item(row, 0).text() 723 733 value = self._model_model.item(row, 1).text() … … 762 772 max_col = self.lstParams.itemDelegate().param_max 763 773 for row in range(self._model_model.rowCount()): 774 if not self.isCheckable(row): 775 continue 764 776 if not self.rowHasConstraint(row): 765 777 continue … … 788 800 def getConstraintForRow(self, row): 789 801 """ 790 For the given row, return its constraint, if any 791 """ 802 For the given row, return its constraint, if any (otherwise None) 803 """ 804 if not self.isCheckable(row): 805 return None 806 item = self._model_model.item(row, 1) 792 807 try: 793 item = self._model_model.item(row, 1)794 808 return item.child(0).data() 795 809 except AttributeError: 796 # return none when no constraints797 810 return None 798 811 … … 801 814 Finds out if row of the main model has a constraint child 802 815 """ 816 if not self.isCheckable(row): 817 return False 803 818 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 819 if not item.hasChildren(): 820 return False 821 c = item.child(0).data() 822 if isinstance(c, Constraint): 823 return True 808 824 return False 809 825 … … 812 828 Finds out if row of the main model has an active constraint child 813 829 """ 830 if not self.isCheckable(row): 831 return False 814 832 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 833 if not item.hasChildren(): 834 return False 835 c = item.child(0).data() 836 if isinstance(c, Constraint) and c.active: 837 return True 819 838 return False 820 839 … … 823 842 Finds out if row of the main model has an active, nontrivial constraint child 824 843 """ 844 if not self.isCheckable(row): 845 return False 825 846 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 847 if not item.hasChildren(): 848 return False 849 c = item.child(0).data() 850 if isinstance(c, Constraint) and c.func and c.active: 851 return True 830 852 return False 831 853 … … 1037 1059 # Show constraint, if present 1038 1060 row = rows[0].row() 1039 if self.rowHasConstraint(row): 1040 func = self.getConstraintForRow(row).func 1041 if func is not None: 1042 self.communicate.statusBarUpdateSignal.emit("Active constrain: "+func) 1061 if not self.rowHasConstraint(row): 1062 return 1063 func = self.getConstraintForRow(row).func 1064 if func is not None: 1065 self.communicate.statusBarUpdateSignal.emit("Active constrain: "+func) 1043 1066 1044 1067 def replaceConstraintName(self, old_name, new_name=""): … … 1186 1209 # Update the sasmodel 1187 1210 # PD[ratio] -> width, npts -> npts, nsigs -> nsigmas 1188 self.kernel_module.setParam(parameter_name + '.' + delegate.columnDict()[model_column], value) 1211 #self.kernel_module.setParam(parameter_name + '.' + delegate.columnDict()[model_column], value) 1212 key = parameter_name + '.' + delegate.columnDict()[model_column] 1213 self.poly_params[key] = value 1189 1214 1190 1215 # Update plot … … 1195 1220 row = self.getRowFromName(parameter_name) 1196 1221 param_item = self._model_model.item(row) 1222 self._model_model.blockSignals(True) 1197 1223 param_item.child(0).child(0, model_column).setText(item.text()) 1224 self._model_model.blockSignals(False) 1198 1225 1199 1226 def onMagnetModelChange(self, item): … … 1224 1251 # Unparsable field 1225 1252 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: 1253 delegate = self.lstMagnetic.itemDelegate() 1254 1255 if model_column > 1: 1256 if model_column == delegate.mag_min: 1257 pos = 1 1258 elif model_column == delegate.mag_max: 1259 pos = 2 1260 elif model_column == delegate.mag_unit: 1261 pos = 0 1262 else: 1263 raise AttributeError("Wrong column in magnetism table.") 1264 # min/max to be changed in self.kernel_module.details[parameter_name] = ['Ang', 0.0, inf] 1265 self.kernel_module.details[parameter_name][pos] = value 1266 else: 1267 self.magnet_params[parameter_name] = value 1268 #self.kernel_module.setParam(parameter_name) = value 1269 # Force the chart update when actual parameters changed 1237 1270 self.recalculatePlotData() 1238 1271 … … 1481 1514 # update charts 1482 1515 self.onPlot() 1516 #self.recalculatePlotData() 1517 1483 1518 1484 1519 # Read only value - we can get away by just printing it here … … 1495 1530 # Data going in 1496 1531 data = self.logic.data 1497 model = self.kernel_module1532 model = copy.deepcopy(self.kernel_module) 1498 1533 qmin = self.q_range_min 1499 1534 qmax = self.q_range_max 1535 # add polydisperse/magnet parameters if asked 1536 self.updateKernelModelWithExtraParams(model) 1500 1537 1501 1538 params_to_fit = self.main_params_to_fit … … 1561 1598 # internal so can use closure for param_dict 1562 1599 param_name = str(self._model_model.item(row, 0).text()) 1563 if param_name not in list(param_dict.keys()):1600 if not self.isCheckable(row) or param_name not in list(param_dict.keys()): 1564 1601 return 1565 1602 # modify the param value 1566 1603 param_repr = GuiUtils.formatNumber(param_dict[param_name][0], high=True) 1567 1604 self._model_model.item(row, 1).setText(param_repr) 1605 self.kernel_module.setParam(param_name, param_dict[param_name][0]) 1568 1606 if self.has_error_column: 1569 1607 error_repr = GuiUtils.formatNumber(param_dict[param_name][1], high=True) … … 1573 1611 # Utility function for updateof polydispersity part of the main model 1574 1612 param_name = str(self._model_model.item(row, 0).text())+'.width' 1575 if param_name not in list(param_dict.keys()):1613 if not self.isCheckable(row) or param_name not in list(param_dict.keys()): 1576 1614 return 1577 1615 # modify the param value … … 1607 1645 poly_item.insertColumn(2, [QtGui.QStandardItem("")]) 1608 1646 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 1647 if not self.has_error_column: 1614 1648 # create top-level error column … … 1617 1651 self.iterateOverModel(createErrorColumn) 1618 1652 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 1653 self._model_model.insertColumn(2, error_column) 1623 self._model_model.blockSignals(True)1624 1654 1625 1655 FittingUtilities.addErrorHeadersToModel(self._model_model) … … 1630 1660 self.has_error_column = True 1631 1661 1662 # block signals temporarily, so we don't end up 1663 # updating charts with every single model change on the end of fitting 1664 self._model_model.itemChanged.disconnect() 1632 1665 self.iterateOverModel(updateFittedValues) 1633 1666 self.iterateOverModel(updatePolyValues) 1634 1635 self._model_model.blockSignals(False) 1667 self._model_model.itemChanged.connect(self.onMainParamsChange) 1636 1668 1637 1669 # Adjust the table cells width. … … 1668 1700 param_repr = GuiUtils.formatNumber(param_dict[param_name][0], high=True) 1669 1701 self._poly_model.item(row_i, 1).setText(param_repr) 1702 self.kernel_module.setParam(param_name, param_dict[param_name][0]) 1670 1703 if self.has_poly_error_column: 1671 1704 error_repr = GuiUtils.formatNumber(param_dict[param_name][1], high=True) 1672 1705 self._poly_model.item(row_i, 2).setText(error_repr) 1673 1674 1706 1675 1707 def createErrorColumn(row_i): … … 1692 1724 # block signals temporarily, so we don't end up 1693 1725 # updating charts with every single model change on the end of fitting 1694 self._poly_model. blockSignals(True)1726 self._poly_model.itemChanged.disconnect() 1695 1727 self.iterateOverPolyModel(updateFittedValues) 1696 self._poly_model. blockSignals(False)1728 self._poly_model.itemChanged.connect(self.onPolyModelChange) 1697 1729 1698 1730 if self.has_poly_error_column: … … 1704 1736 1705 1737 # switch off reponse to model change 1706 self._poly_model.blockSignals(True)1707 1738 self._poly_model.insertColumn(2, error_column) 1708 self._poly_model.blockSignals(False)1709 1739 FittingUtilities.addErrorPolyHeadersToModel(self._poly_model) 1710 1740 … … 1739 1769 param_repr = GuiUtils.formatNumber(param_dict[param_name][0], high=True) 1740 1770 self._magnet_model.item(row, 1).setText(param_repr) 1771 self.kernel_module.setParam(param_name, param_dict[param_name][0]) 1741 1772 if self.has_magnet_error_column: 1742 1773 error_repr = GuiUtils.formatNumber(param_dict[param_name][1], high=True) … … 1758 1789 # block signals temporarily, so we don't end up 1759 1790 # updating charts with every single model change on the end of fitting 1760 self._magnet_model. blockSignals(True)1791 self._magnet_model.itemChanged.disconnect() 1761 1792 self.iterateOverMagnetModel(updateFittedValues) 1762 self._magnet_model. blockSignals(False)1793 self._magnet_model.itemChanged.connect(self.onMagnetModelChange) 1763 1794 1764 1795 if self.has_magnet_error_column: … … 1770 1801 1771 1802 # switch off reponse to model change 1772 self._magnet_model.blockSignals(True)1773 1803 self._magnet_model.insertColumn(2, error_column) 1774 self._magnet_model.blockSignals(False)1775 1804 FittingUtilities.addErrorHeadersToModel(self._magnet_model) 1776 1805 … … 1784 1813 self.cmdPlot.setText("Show Plot") 1785 1814 # Force data recalculation so existing charts are updated 1815 self.showPlot() 1816 # This is an important processEvent. 1817 # This allows charts to be properly updated in order 1818 # of plots being applied. 1819 QtWidgets.QApplication.processEvents() 1786 1820 self.recalculatePlotData() 1787 self.showPlot()1788 1821 1789 1822 def onSmearingOptionsUpdate(self): … … 1950 1983 # Crete/overwrite model items 1951 1984 self._model_model.clear() 1952 1953 # First, add parameters from the main model 1954 if model_name is not None: 1985 self._poly_model.clear() 1986 self._magnet_model.clear() 1987 1988 if model_name is None: 1989 if structure_factor not in (None, "None"): 1990 # S(Q) on its own, treat the same as a form factor 1991 self.kernel_module = None 1992 self.fromStructureFactorToQModel(structure_factor) 1993 else: 1994 # No models selected 1995 return 1996 else: 1955 1997 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: 1998 self.addExtraShells() 1999 1964 2000 # Allow the SF combobox visibility for the given sasmodel 1965 2001 self.enableStructureFactorControl(structure_factor) 2002 2003 # Add S(Q) 1966 2004 if self.cbStructureFactor.isEnabled(): 1967 2005 structure_factor = self.cbStructureFactor.currentText() 1968 2006 self.fromStructureFactorToQModel(structure_factor) 1969 2007 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() 2008 # Add polydispersity to the model 2009 self.poly_params = {} 2010 self.setPolyModel() 2011 # Add magnetic parameters to the model 2012 self.magnet_params = {} 2013 self.setMagneticModel() 1979 2014 1980 2015 # Adjust the table cells width … … 2049 2084 self.shell_names = self.shellNamesList() 2050 2085 2086 # Add heading row 2087 FittingUtilities.addHeadingRowToModel(self._model_model, model_name) 2088 2051 2089 # 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()2090 FittingUtilities.addParametersToModel( 2091 self.model_parameters, 2092 self.kernel_module, 2093 self.is2D, 2094 self._model_model, 2095 self.lstParams) 2058 2096 2059 2097 def fromStructureFactorToQModel(self, structure_factor): … … 2063 2101 if structure_factor is None or structure_factor=="None": 2064 2102 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() 2103 2104 product_params = None 2105 2106 if self.kernel_module is None: 2107 # Structure factor is the only selected model; build it and show all its params 2108 self.kernel_module = self.models[structure_factor]() 2109 s_params = self.kernel_module._model_info.parameters 2110 s_params_orig = s_params 2111 else: 2112 s_kernel = self.models[structure_factor]() 2113 p_kernel = self.kernel_module 2114 2115 p_pars_len = len(p_kernel._model_info.parameters.kernel_parameters) 2116 s_pars_len = len(s_kernel._model_info.parameters.kernel_parameters) 2117 2118 self.kernel_module = MultiplicationModel(p_kernel, s_kernel) 2119 all_params = self.kernel_module._model_info.parameters.kernel_parameters 2120 all_param_names = [param.name for param in all_params] 2121 2122 # S(Q) params from the product model are not necessarily the same as those from the S(Q) model; any 2123 # conflicting names with P(Q) params will cause a rename 2124 2125 if "radius_effective_mode" in all_param_names: 2126 # Show all parameters 2127 # In this case, radius_effective is NOT pruned by sasmodels.product 2128 s_params = modelinfo.ParameterTable(all_params[p_pars_len:p_pars_len+s_pars_len]) 2129 s_params_orig = modelinfo.ParameterTable(s_kernel._model_info.parameters.kernel_parameters) 2130 product_params = modelinfo.ParameterTable( 2131 self.kernel_module._model_info.parameters.kernel_parameters[p_pars_len+s_pars_len:]) 2132 else: 2133 # Ensure radius_effective is not displayed 2134 s_params_orig = modelinfo.ParameterTable(s_kernel._model_info.parameters.kernel_parameters[1:]) 2135 if "radius_effective" in all_param_names: 2136 # In this case, radius_effective is NOT pruned by sasmodels.product 2137 s_params = modelinfo.ParameterTable(all_params[p_pars_len+1:p_pars_len+s_pars_len]) 2138 product_params = modelinfo.ParameterTable( 2139 self.kernel_module._model_info.parameters.kernel_parameters[p_pars_len+s_pars_len:]) 2140 else: 2141 # In this case, radius_effective is pruned by sasmodels.product 2142 s_params = modelinfo.ParameterTable(all_params[p_pars_len:p_pars_len+s_pars_len-1]) 2143 product_params = modelinfo.ParameterTable( 2144 self.kernel_module._model_info.parameters.kernel_parameters[p_pars_len+s_pars_len-1:]) 2145 2146 # Add heading row 2147 FittingUtilities.addHeadingRowToModel(self._model_model, structure_factor) 2148 2149 # Get new rows for QModel 2150 # Any renamed parameters are stored as data in the relevant item, for later handling 2151 FittingUtilities.addSimpleParametersToModel( 2152 parameters=s_params, 2153 is2D=self.is2D, 2154 parameters_original=s_params_orig, 2155 model=self._model_model, 2156 view=self.lstParams) 2157 2158 # Insert product-only params into QModel 2159 if product_params: 2160 prod_rows = FittingUtilities.addSimpleParametersToModel( 2161 parameters=product_params, 2162 is2D=self.is2D, 2163 parameters_original=None, 2164 model=self._model_model, 2165 view=self.lstParams, 2166 row_num=2) 2167 2168 # Since this all happens after shells are dealt with and we've inserted rows, fix this counter 2169 self._n_shells_row += len(prod_rows) 2083 2170 2084 2171 def haveParamsToFit(self): … … 2106 2193 model_row = item.row() 2107 2194 name_index = self._model_model.index(model_row, 0) 2195 name_item = self._model_model.itemFromIndex(name_index) 2108 2196 2109 2197 # Extract changed value. … … 2114 2202 return 2115 2203 2116 parameter_name = str(self._model_model.data(name_index)) # sld, background etc. 2204 # if the item has user data, this is the actual parameter name (e.g. to handle duplicate names) 2205 if name_item.data(QtCore.Qt.UserRole): 2206 parameter_name = str(name_item.data(QtCore.Qt.UserRole)) 2207 else: 2208 parameter_name = str(self._model_model.data(name_index)) 2117 2209 2118 2210 # Update the parameter value - note: this supports +/-inf as well … … 2237 2329 return self.completed1D if isinstance(self.data, Data1D) else self.completed2D 2238 2330 2331 def updateKernelModelWithExtraParams(self, model=None): 2332 """ 2333 Updates kernel model 'model' with extra parameters from 2334 the polydisp and magnetism tab, if the tabs are enabled 2335 """ 2336 if model is None: return 2337 if not hasattr(model, 'setParam'): return 2338 2339 # add polydisperse parameters if asked 2340 if self.chkPolydispersity.isChecked(): 2341 for key, value in self.poly_params.items(): 2342 model.setParam(key, value) 2343 # add magnetic params if asked 2344 if self.chkMagnetism.isChecked(): 2345 for key, value in self.magnet_params.items(): 2346 model.setParam(key, value) 2347 2239 2348 def calculateQGridForModelExt(self, data=None, model=None, completefn=None, use_threads=True): 2240 2349 """ … … 2244 2353 data = self.data 2245 2354 if model is None: 2246 model = self.kernel_module 2355 model = copy.deepcopy(self.kernel_module) 2356 self.updateKernelModelWithExtraParams(model) 2357 2247 2358 if completefn is None: 2248 2359 completefn = self.methodCompleteForData() … … 2317 2428 2318 2429 # Create plots for intermediate product data 2319 pq_data, sq_data = self.logic.new1DProductPlots(return_data, self.tab_id) 2320 if pq_data is not None: 2321 pq_data.symbol = "Line" 2322 self.createNewIndex(pq_data) 2323 # self.communicate.plotUpdateSignal.emit([pq_data]) 2324 new_plots.append(pq_data) 2325 if sq_data is not None: 2326 sq_data.symbol = "Line" 2327 self.createNewIndex(sq_data) 2328 # self.communicate.plotUpdateSignal.emit([sq_data]) 2329 new_plots.append(sq_data) 2430 plots = self.logic.new1DProductPlots(return_data, self.tab_id) 2431 for plot in plots: 2432 plot.symbol = "Line" 2433 self.createNewIndex(plot) 2434 new_plots.append(plot) 2435 2436 for plot in new_plots: 2437 self.communicate.plotUpdateSignal.emit([plot]) 2438 2439 def complete2D(self, return_data): 2440 """ 2441 Plot the current 2D data 2442 """ 2443 fitted_data = self.logic.new2DPlot(return_data) 2444 residuals = self.calculateResiduals(fitted_data) 2445 self.model_data = fitted_data 2446 new_plots = [fitted_data] 2447 if residuals is not None: 2448 new_plots.append(residuals) 2330 2449 2331 2450 # Update/generate plots 2332 2451 for plot in new_plots: 2333 2452 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 2453 2343 2454 def calculateResiduals(self, fitted_data): … … 2470 2581 _, min, max = self.kernel_module.details[param_name] 2471 2582 2583 # Update local param dict 2584 self.poly_params[param_name + '.width'] = width 2585 self.poly_params[param_name + '.npts'] = npts 2586 self.poly_params[param_name + '.nsigmas'] = nsigs 2587 2472 2588 # Construct a row with polydisp. related variable. 2473 2589 # This will get added to the polydisp. model … … 2517 2633 def updateFunctionCaption(row): 2518 2634 # Utility function for update of polydispersity function name in the main model 2635 if not self.isCheckable(row): 2636 return 2637 self._model_model.blockSignals(True) 2519 2638 param_name = str(self._model_model.item(row, 0).text()) 2639 self._model_model.blockSignals(False) 2520 2640 if param_name != param.name: 2521 2641 return 2522 2642 # Modify the param value 2643 self._model_model.blockSignals(True) 2523 2644 if self.has_error_column: 2524 2645 # err column changes the indexing … … 2526 2647 else: 2527 2648 self._model_model.item(row, 0).child(0).child(0,4).setText(combo_string) 2649 self._model_model.blockSignals(False) 2528 2650 2529 2651 if combo_string == 'array': … … 2644 2766 param.units] 2645 2767 2768 self.magnet_params[param.name] = param.default 2769 2646 2770 FittingUtilities.addCheckedListToModel(model, checked_list) 2647 2771 … … 2683 2807 2684 2808 self.lstParams.setIndexWidget(shell_index, func) 2685 self._ last_model_row = self._model_model.rowCount()2809 self._n_shells_row = shell_row - 1 2686 2810 2687 2811 # Set the index to the state-kept value … … 2694 2818 """ 2695 2819 # Find row location of the combobox 2696 last_row = self._last_model_row2697 remove_rows = self._ model_model.rowCount() - last_row2820 first_row = self._n_shells_row + 1 2821 remove_rows = self._num_shell_params 2698 2822 2699 2823 if remove_rows > 1: 2700 self._model_model.removeRows(last_row, remove_rows) 2701 2702 FittingUtilities.addShellsToModel(self.model_parameters, self._model_model, index) 2824 self._model_model.removeRows(first_row, remove_rows) 2825 2826 new_rows = FittingUtilities.addShellsToModel( 2827 self.model_parameters, 2828 self._model_model, 2829 index, 2830 first_row, 2831 self.lstParams) 2832 2833 self._num_shell_params = len(new_rows) 2703 2834 self.current_shell_displayed = index 2835 2836 # Change 'n' in the parameter model, thereby updating the underlying model 2837 self._model_model.item(self._n_shells_row, 1).setText(str(index)) 2704 2838 2705 2839 # Update relevant models … … 3046 3180 formatted_output = FittingUtilities.formatParameters(param_list) 3047 3181 elif format == "Excel": 3048 formatted_output = FittingUtilities.formatParametersExcel(param_list )3182 formatted_output = FittingUtilities.formatParametersExcel(param_list[1:]) 3049 3183 elif format == "Latex": 3050 formatted_output = FittingUtilities.formatParametersLatex(param_list )3184 formatted_output = FittingUtilities.formatParametersLatex(param_list[1:]) 3051 3185 else: 3052 3186 raise AttributeError("Bad format specifier.") … … 3213 3347 self._poly_model.blockSignals(False) 3214 3348 3349 3350 -
src/sas/qtgui/Perspectives/Fitting/ModelThread.py
r2df558e r5181e9b 101 101 elapsed = time.time() - self.starttime 102 102 103 res = dict(image = output, data = self.data, page_id = self.page_id, 104 model = self.model, state = self.state, 105 toggle_mode_on = self.toggle_mode_on, elapsed = elapsed, 106 index = index_model, fid = self.fid, 107 qmin = self.qmin, qmax = self.qmax, 108 weight = self.weight, update_chisqr = self.update_chisqr, 109 source = self.source) 110 103 111 if LocalConfig.USING_TWISTED: 104 return (output, 105 self.data, 106 self.page_id, 107 self.model, 108 self.state, 109 self.toggle_mode_on, 110 elapsed, 111 index_model, 112 self.fid, 113 self.qmin, 114 self.qmax, 115 self.weight, 116 self.update_chisqr, 117 self.source) 118 else: 119 self.completefn((output, 120 self.data, 121 self.page_id, 122 self.model, 123 self.state, 124 self.toggle_mode_on, 125 elapsed, 126 index_model, 127 self.fid, 128 self.qmin, 129 self.qmax, 130 self.weight, 131 #qstep=self.qstep, 132 self.update_chisqr, 133 self.source)) 134 112 return res 113 else: 114 self.completefn(res) 135 115 136 116 class Calc1D(CalcThread): … … 184 164 index = (self.qmin <= self.data.x) & (self.data.x <= self.qmax) 185 165 166 intermediate_results = None 167 186 168 # If we use a smearer, also return the unsmeared model 187 169 unsmeared_output = None … … 194 176 mask = self.data.x[first_bin:last_bin+1] 195 177 unsmeared_output = numpy.zeros((len(self.data.x))) 196 unsmeared_output[first_bin:last_bin+1] = self.model.evalDistribution(mask) 178 179 return_data = self.model.calculate_Iq(mask) 180 if isinstance(return_data, tuple): 181 # see sasmodels beta_approx: SasviewModel.calculate_Iq 182 # TODO: implement intermediate results in smearers 183 return_data, _ = return_data 184 unsmeared_output[first_bin:last_bin+1] = return_data 197 185 output = self.smearer(unsmeared_output, first_bin, last_bin) 198 186 … … 213 201 unsmeared_error=unsmeared_error 214 202 else: 215 output[index] = self.model.evalDistribution(self.data.x[index]) 216 217 sq_values = None 218 pq_values = None 219 s_model = None 220 p_model = None 221 if isinstance(self.model, MultiplicationModel): 222 s_model = self.model.s_model 223 p_model = self.model.p_model 224 elif hasattr(self.model, "calc_composition_models"): 225 results = self.model.calc_composition_models(self.data.x[index]) 226 if results is not None: 227 pq_values, sq_values = results 228 229 if pq_values is None or sq_values is None: 230 if p_model is not None and s_model is not None: 231 sq_values = numpy.zeros((len(self.data.x))) 232 pq_values = numpy.zeros((len(self.data.x))) 233 sq_values[index] = s_model.evalDistribution(self.data.x[index]) 234 pq_values[index] = p_model.evalDistribution(self.data.x[index]) 203 return_data = self.model.calculate_Iq(self.data.x[index]) 204 if isinstance(return_data, tuple): 205 # see sasmodels beta_approx: SasviewModel.calculate_Iq 206 return_data, intermediate_results = return_data 207 output[index] = return_data 208 209 if intermediate_results: 210 # the model returns a callable which is then used to retrieve the data 211 intermediate_results = intermediate_results() 212 else: 213 # TODO: this conditional branch needs refactoring 214 sq_values = None 215 pq_values = None 216 s_model = None 217 p_model = None 218 219 if isinstance(self.model, MultiplicationModel): 220 s_model = self.model.s_model 221 p_model = self.model.p_model 222 223 elif hasattr(self.model, "calc_composition_models"): 224 results = self.model.calc_composition_models(self.data.x[index]) 225 if results is not None: 226 pq_values, sq_values = results 227 228 if pq_values is None or sq_values is None: 229 if p_model is not None and s_model is not None: 230 sq_values = numpy.zeros((len(self.data.x))) 231 pq_values = numpy.zeros((len(self.data.x))) 232 sq_values[index] = s_model.evalDistribution(self.data.x[index]) 233 pq_values[index] = p_model.evalDistribution(self.data.x[index]) 234 235 if pq_values is not None and sq_values is not None: 236 intermediate_results = { 237 "P(Q)": pq_values, 238 "S(Q)": sq_values 239 } 240 else: 241 intermediate_results = {} 235 242 236 243 elapsed = time.time() - self.starttime 237 244 245 res = dict(x = self.data.x[index], y = output[index], 246 page_id = self.page_id, state = self.state, weight = self.weight, 247 fid = self.fid, toggle_mode_on = self.toggle_mode_on, 248 elapsed = elapsed, index = index, model = self.model, 249 data = self.data, update_chisqr = self.update_chisqr, 250 source = self.source, unsmeared_output = unsmeared_output, 251 unsmeared_data = unsmeared_data, unsmeared_error = unsmeared_error, 252 intermediate_results = intermediate_results) 253 238 254 if LocalConfig.USING_TWISTED: 239 return (self.data.x[index], output[index], 240 self.page_id, 241 self.state, 242 self.weight, 243 self.fid, 244 self.toggle_mode_on, 245 elapsed, index, self.model, 246 self.data, 247 self.update_chisqr, 248 self.source, 249 unsmeared_output, unsmeared_data, unsmeared_error, 250 pq_values, sq_values) 251 else: 252 self.completefn((self.data.x[index], output[index], 253 self.page_id, 254 self.state, 255 self.weight, 256 self.fid, 257 self.toggle_mode_on, 258 elapsed, index, self.model, 259 self.data, 260 self.update_chisqr, 261 self.source, 262 unsmeared_output, unsmeared_data, unsmeared_error, 263 pq_values, sq_values)) 255 return res 256 else: 257 self.completefn(res) 264 258 265 259 def results(self): -
src/sas/qtgui/Perspectives/Fitting/UI/OptionsWidgetUI.ui
r79bd268 r309fa1b 32 32 <item row="0" column="1"> 33 33 <widget class="QLineEdit" name="txtMinRange"> 34 <property name="minimumSize"> 35 <size> 36 <width>80</width> 37 <height>0</height> 38 </size> 39 </property> 34 40 <property name="toolTip"> 35 41 <string><html><head/><body><p>Minimum value of Q.</p></body></html></string> … … 54 60 <item row="1" column="1"> 55 61 <widget class="QLineEdit" name="txtMaxRange"> 62 <property name="minimumSize"> 63 <size> 64 <width>80</width> 65 <height>0</height> 66 </size> 67 </property> 56 68 <property name="toolTip"> 57 69 <string><html><head/><body><p>Maximum value of Q.</p></body></html></string> -
src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingLogicTest.py
re752ab8 rbfb5d9e 99 99 data.name = "boop" 100 100 data.id = "poop" 101 return_data = (data.x,data.y, 7, None, None, 102 0, True, 0.0, 1, data, 103 data, False, None, 104 None, None, None, 105 None, None) 101 # Condensed return data (new1DPlot only uses these fields) 102 return_data = dict(x = data.x, 103 y = data.y, 104 model = data, 105 data = data) 106 # return_data = (data.x,data.y, 7, None, None, 107 # 0, True, 0.0, 1, data, 108 # data, False, None, 109 # None, None, None, 110 # None, None) 106 111 107 112 new_plot = self.logic.new1DPlot(return_data=return_data, tab_id=0) … … 139 144 qmin, qmax, npts = self.logic.computeDataRange() 140 145 141 return_data = (x_0, data, 7, data, None, 142 True, 0.0, 1, 0, qmin, qmax, 143 0.1, False, None) 146 # Condensed return data (new2DPlot only uses these fields) 147 return_data = dict(image = x_0, 148 data = data, 149 page_id = 7, 150 model = data) 151 # return_data = (x_0, data, 7, data, None, 152 # True, 0.0, 1, 0, qmin, qmax, 153 # 0.1, False, None) 144 154 145 155 new_plot = self.logic.new2DPlot(return_data=return_data) -
src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingOptionsTest.py
r725d9c06 rbfb5d9e 38 38 # The combo box 39 39 self.assertIsInstance(self.widget.cbAlgorithm, QtWidgets.QComboBox) 40 self.assertEqual(self.widget.cbAlgorithm.count(), 5)40 self.assertEqual(self.widget.cbAlgorithm.count(), 6) 41 41 self.assertEqual(self.widget.cbAlgorithm.itemText(0), 'Nelder-Mead Simplex') 42 42 self.assertEqual(self.widget.cbAlgorithm.itemText(4), 'Levenberg-Marquardt') -
src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingWidgetTest.py
r605d944 r4ea8020 256 256 self.widget.cbStructureFactor.setCurrentIndex(structure_index) 257 257 258 # We have 4 more rows now258 # We have 3 more param rows now (radius_effective is removed), and a new heading 259 259 self.assertEqual(self.widget._model_model.rowCount(), rowcount+4) 260 260 … … 276 276 last_index = self.widget.cbStructureFactor.count() 277 277 self.widget.cbStructureFactor.setCurrentIndex(last_index-1) 278 # Do we have all the rows ?279 self.assertEqual(self.widget._model_model.rowCount(), 4)278 # Do we have all the rows (incl. radius_effective & heading row)? 279 self.assertEqual(self.widget._model_model.rowCount(), 5) 280 280 281 281 # Are the command buttons properly enabled? … … 445 445 self.assertEqual(self.widget.kernel_module.details['radius_bell'][1], 1.0) 446 446 447 #self.widget.show() 448 #QtWidgets.QApplication.exec_() 449 447 450 # Change the number of points 448 self.assertEqual(self.widget. kernel_module.getParam('radius_bell.npts'), 35)451 self.assertEqual(self.widget.poly_params['radius_bell.npts'], 35) 449 452 self.widget._poly_model.item(0,4).setText("22") 450 self.assertEqual(self.widget. kernel_module.getParam('radius_bell.npts'), 22)453 self.assertEqual(self.widget.poly_params['radius_bell.npts'], 22) 451 454 # try something stupid 452 455 self.widget._poly_model.item(0,4).setText("butt") 453 456 # see that this didn't annoy the control at all 454 self.assertEqual(self.widget. kernel_module.getParam('radius_bell.npts'), 22)457 self.assertEqual(self.widget.poly_params['radius_bell.npts'], 22) 455 458 456 459 # Change the number of sigmas 457 self.assertEqual(self.widget. kernel_module.getParam('radius_bell.nsigmas'), 3)460 self.assertEqual(self.widget.poly_params['radius_bell.nsigmas'], 3) 458 461 self.widget._poly_model.item(0,5).setText("222") 459 self.assertEqual(self.widget. kernel_module.getParam('radius_bell.nsigmas'), 222)462 self.assertEqual(self.widget.poly_params['radius_bell.nsigmas'], 222) 460 463 # try something stupid again 461 464 self.widget._poly_model.item(0,4).setText("beer") 462 465 # no efect 463 self.assertEqual(self.widget. kernel_module.getParam('radius_bell.nsigmas'), 222)466 self.assertEqual(self.widget.poly_params['radius_bell.nsigmas'], 222) 464 467 465 468 def testOnPolyComboIndexChange(self): … … 482 485 self.widget.onPolyComboIndexChange('rectangle', 0) 483 486 # check values 484 self.assertEqual(self.widget. kernel_module.getParam('radius_bell.npts'), 35)485 self.assertAlmostEqual(self.widget. kernel_module.getParam('radius_bell.nsigmas'), 1.73205, 5)487 self.assertEqual(self.widget.poly_params['radius_bell.npts'], 35) 488 self.assertAlmostEqual(self.widget.poly_params['radius_bell.nsigmas'], 1.73205, 5) 486 489 # Change the index 487 490 self.widget.onPolyComboIndexChange('lognormal', 0) 488 491 # check values 489 self.assertEqual(self.widget. kernel_module.getParam('radius_bell.npts'), 80)490 self.assertEqual(self.widget. kernel_module.getParam('radius_bell.nsigmas'), 8)492 self.assertEqual(self.widget.poly_params['radius_bell.npts'], 80) 493 self.assertEqual(self.widget.poly_params['radius_bell.nsigmas'], 8) 491 494 # Change the index 492 495 self.widget.onPolyComboIndexChange('schulz', 0) 493 496 # check values 494 self.assertEqual(self.widget. kernel_module.getParam('radius_bell.npts'), 80)495 self.assertEqual(self.widget. kernel_module.getParam('radius_bell.nsigmas'), 8)497 self.assertEqual(self.widget.poly_params['radius_bell.npts'], 80) 498 self.assertEqual(self.widget.poly_params['radius_bell.nsigmas'], 8) 496 499 497 500 # mock up file load … … 506 509 Test opening of the load file dialog for 'array' polydisp. function 507 510 """ 511 512 # open a non-existent file 508 513 filename = os.path.join("UnitTesting", "testdata_noexist.txt") 514 with self.assertRaises(OSError, msg="testdata_noexist.txt should be a non-existent file"): 515 os.stat(filename) 509 516 QtWidgets.QFileDialog.getOpenFileName = MagicMock(return_value=(filename,'')) 510 517 self.widget.show() … … 522 529 523 530 # good file 531 # TODO: this depends on the working directory being src/sas/qtgui, 532 # TODO: which isn't convenient if you want to run this test suite 533 # TODO: individually 524 534 filename = os.path.join("UnitTesting", "testdata.txt") 535 try: 536 os.stat(filename) 537 except OSError: 538 self.assertTrue(False, "testdata.txt does not exist") 525 539 QtWidgets.QFileDialog.getOpenFileName = MagicMock(return_value=(filename,'')) 526 540 … … 588 602 589 603 # Assure we have the combobox available 590 last_row = self.widget._last_model_row591 func_index = self.widget._model_model.index( last_row-1, 1)604 cbox_row = self.widget._n_shells_row 605 func_index = self.widget._model_model.index(cbox_row, 1) 592 606 self.assertIsInstance(self.widget.lstParams.indexWidget(func_index), QtWidgets.QComboBox) 607 608 # get number of rows before changing shell count 609 last_row = self.widget._model_model.rowCount() 593 610 594 611 # Change the combo box index … … 1024 1041 1025 1042 # Check the model 1026 self.assertEqual(self.widget._model_model.rowCount(), 6)1043 self.assertEqual(self.widget._model_model.rowCount(), 7) 1027 1044 self.assertEqual(self.widget._model_model.columnCount(), 5) 1028 1045 … … 1140 1157 # two rows selected 1141 1158 index1 = self.widget.lstParams.model().index(1, 0, QtCore.QModelIndex()) 1142 index2 = self.widget.lstParams.model().index( 2, 0, QtCore.QModelIndex())1159 index2 = self.widget.lstParams.model().index(3, 0, QtCore.QModelIndex()) 1143 1160 selection_model = self.widget.lstParams.selectionModel() 1144 1161 selection_model.select(index1, selection_model.Select | selection_model.Rows) … … 1176 1193 # several random parameters 1177 1194 self.assertEqual(self.widget.getRowFromName('scale'), 0) 1178 self.assertEqual(self.widget.getRowFromName('length'), 5)1195 self.assertEqual(self.widget.getRowFromName('length'), 6) 1179 1196 1180 1197 def testGetParamNames(self): … … 1213 1230 # Create a constraint object 1214 1231 const = Constraint(parent=None, value=7.0) 1215 row = 21232 row = 3 1216 1233 1217 1234 spy = QtSignalSpy(self.widget, self.widget.constraintAddedSignal) … … 1232 1249 # assign complex constraint now 1233 1250 const = Constraint(parent=None, param='radius', func='5*sld') 1234 row = 41251 row = 5 1235 1252 # call the method tested 1236 1253 self.widget.addConstraintToRow(constraint=const, row=row) … … 1291 1308 self.widget.cbModel.setCurrentIndex(model_index) 1292 1309 1310 row1 = 1 1311 row2 = 5 1312 1313 param1 = "background" 1314 param2 = "radius" 1315 1316 #default_value1 = "0.001" 1317 default_value2 = "20" 1318 1293 1319 # select two rows 1294 row1 = 11295 row2 = 41296 1320 index1 = self.widget.lstParams.model().index(row1, 0, QtCore.QModelIndex()) 1297 1321 index2 = self.widget.lstParams.model().index(row2, 0, QtCore.QModelIndex()) … … 1310 1334 1311 1335 # delete one of the constraints 1312 self.widget.deleteConstraintOnParameter(param= 'background')1336 self.widget.deleteConstraintOnParameter(param=param1) 1313 1337 1314 1338 # see that the other constraint is still present 1315 cons = self.widget.getConstraintForRow( 4) # 4 = radius1316 self.assertEqual(cons.param, "radius")1317 self.assertEqual(cons.value, "20")1339 cons = self.widget.getConstraintForRow(row2) 1340 self.assertEqual(cons.param, param2) 1341 self.assertEqual(cons.value, default_value2) 1318 1342 1319 1343 # kill the other constraint … … 1321 1345 1322 1346 # see that the other constraint is still present 1323 self.assertEqual(self.widget.getConstraintsForModel(), [( 'radius', None)])1347 self.assertEqual(self.widget.getConstraintsForModel(), [(param2, None)]) 1324 1348 1325 1349 def testGetConstraintForRow(self): … … 1341 1365 self.widget.cbModel.setCurrentIndex(model_index) 1342 1366 1367 row1 = 1 1368 row2 = 5 1369 1343 1370 # select two rows 1344 row1 = 11345 row2 = 41346 1371 index1 = self.widget.lstParams.model().index(row1, 0, QtCore.QModelIndex()) 1347 1372 index2 = self.widget.lstParams.model().index(row2, 0, QtCore.QModelIndex()) … … 1353 1378 self.widget.addSimpleConstraint() 1354 1379 1355 con_list = [False, True, False, False, True, False]1380 con_list = [False, True, False, False, False, True, False] 1356 1381 new_list = [] 1357 1382 for row in range(self.widget._model_model.rowCount()): … … 1371 1396 self.widget.cbModel.setCurrentIndex(model_index) 1372 1397 1398 row1 = 1 1399 row2 = 5 1400 1373 1401 # select two rows 1374 row1 = 11375 row2 = 41376 1402 index1 = self.widget.lstParams.model().index(row1, 0, QtCore.QModelIndex()) 1377 1403 index2 = self.widget.lstParams.model().index(row2, 0, QtCore.QModelIndex()) … … 1387 1413 constraint_objects[0].active = False 1388 1414 1389 con_list = [False, False, False, False, True, False]1415 con_list = [False, False, False, False, False, True, False] 1390 1416 new_list = [] 1391 1417 for row in range(self.widget._model_model.rowCount()): … … 1408 1434 self.assertEqual(self.widget.getConstraintsForModel(),[]) 1409 1435 1436 row1 = 1 1437 row2 = 5 1438 1439 param1 = "background" 1440 param2 = "radius" 1441 1442 default_value1 = "0.001" 1443 default_value2 = "20" 1444 1410 1445 # select two rows 1411 row1 = 11412 row2 = 41413 1446 index1 = self.widget.lstParams.model().index(row1, 0, QtCore.QModelIndex()) 1414 1447 index2 = self.widget.lstParams.model().index(row2, 0, QtCore.QModelIndex()) … … 1422 1455 # simple constraints 1423 1456 # self.assertEqual(self.widget.getConstraintsForModel(), [('background', '0.001'), ('radius', '20')]) 1424 cons = self.widget.getConstraintForRow( 1) # 1 - background1425 self.assertEqual(cons.param, "background")1426 self.assertEqual(cons.value, "0.001")1427 cons = self.widget.getConstraintForRow( 4) # 4 = radius1428 self.assertEqual(cons.param, "radius")1429 self.assertEqual(cons.value, "20")1457 cons = self.widget.getConstraintForRow(row1) 1458 self.assertEqual(cons.param, param1) 1459 self.assertEqual(cons.value, default_value1) 1460 cons = self.widget.getConstraintForRow(row2) 1461 self.assertEqual(cons.param, param2) 1462 self.assertEqual(cons.value, default_value2) 1430 1463 1431 1464 objects = self.widget.getConstraintObjectsForModel() 1432 1465 self.assertEqual(len(objects), 2) 1433 self.assertEqual(objects[1].value, '20') 1434 self.assertEqual(objects[0].param, 'background') 1466 self.assertEqual(objects[1].value, default_value2) 1467 self.assertEqual(objects[0].param, param1) 1468 1469 row = 0 1470 param = "scale" 1471 func = "5*sld" 1435 1472 1436 1473 # add complex constraint 1437 const = Constraint(parent=None, param='scale', func='5*sld') 1438 row = 0 1474 const = Constraint(parent=None, param=param, func=func) 1439 1475 self.widget.addConstraintToRow(constraint=const, row=row) 1440 1476 #self.assertEqual(self.widget.getConstraintsForModel(),[('scale', '5*sld'), ('background', '0.001'), ('radius', None)]) 1441 cons = self.widget.getConstraintForRow( 4) # 4 = radius1442 self.assertEqual(cons.param, "radius")1443 self.assertEqual(cons.value, "20")1477 cons = self.widget.getConstraintForRow(row2) 1478 self.assertEqual(cons.param, param2) 1479 self.assertEqual(cons.value, default_value2) 1444 1480 1445 1481 objects = self.widget.getConstraintObjectsForModel() 1446 1482 self.assertEqual(len(objects), 3) 1447 self.assertEqual(objects[0].func, '5*sld')1483 self.assertEqual(objects[0].func, func) 1448 1484 1449 1485 def testReplaceConstraintName(self):
Note: See TracChangeset
for help on using the changeset viewer.