Changeset aca8418 in sasview for src/sas/qtgui/Perspectives
- Timestamp:
- Jun 6, 2017 7:37:52 AM (7 years ago)
- Branches:
- ESS_GUI, ESS_GUI_Docs, ESS_GUI_batch_fitting, ESS_GUI_bumps_abstraction, ESS_GUI_iss1116, ESS_GUI_iss879, ESS_GUI_iss959, ESS_GUI_opencl, ESS_GUI_ordering, ESS_GUI_sync_sascalc
- Children:
- 06b0138
- Parents:
- 358b39d
- Location:
- src/sas/qtgui/Perspectives/Fitting
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py
r6964d44 raca8418 37 37 return (param_name, param_length) 38 38 39 def addParametersToModel(parameters, is2D):39 def addParametersToModel(parameters, kernel_module, is2D): 40 40 """ 41 41 Update local ModelModel with sasmodel parameters … … 69 69 if p.name != param.name: 70 70 continue 71 item1_2 = QtGui.QStandardItem(str(p.default)) 71 width = kernel_module.getParam(p.name+'.width') 72 #npts = kernel_module.getParam(p.name+'.npts') 73 #nsigs = kernel_module.getParam(p.name+'.nsigmas') 74 type = kernel_module.getParam(p.name+'.type') 75 76 item1_2 = QtGui.QStandardItem(str(width)) 72 77 item1_2.setEditable(False) 73 item1_3 = QtGui.QStandardItem( str(p.limits[0]))78 item1_3 = QtGui.QStandardItem() 74 79 item1_3.setEditable(False) 75 item1_4 = QtGui.QStandardItem( str(p.limits[1]))80 item1_4 = QtGui.QStandardItem() 76 81 item1_4.setEditable(False) 77 item1_5 = QtGui.QStandardItem( p.units)82 item1_5 = QtGui.QStandardItem(type) 78 83 item1_5.setEditable(False) 79 84 poly_item.appendRow([item1_1, item1_2, item1_3, item1_4, item1_5]) … … 156 161 model.setHeaderData(5, QtCore.Qt.Horizontal, QtCore.QVariant("Nsigs")) 157 162 model.setHeaderData(6, QtCore.Qt.Horizontal, QtCore.QVariant("Function")) 163 164 def addErrorPolyHeadersToModel(model): 165 """ 166 Adds predefined headers to the model 167 """ 168 model.setHeaderData(0, QtCore.Qt.Horizontal, QtCore.QVariant("Parameter")) 169 model.setHeaderData(1, QtCore.Qt.Horizontal, QtCore.QVariant("PD[ratio]")) 170 model.setHeaderData(2, QtCore.Qt.Horizontal, QtCore.QVariant("Error")) 171 model.setHeaderData(3, QtCore.Qt.Horizontal, QtCore.QVariant("Min")) 172 model.setHeaderData(4, QtCore.Qt.Horizontal, QtCore.QVariant("Max")) 173 model.setHeaderData(5, QtCore.Qt.Horizontal, QtCore.QVariant("Npts")) 174 model.setHeaderData(6, QtCore.Qt.Horizontal, QtCore.QVariant("Nsigs")) 175 model.setHeaderData(7, QtCore.Qt.Horizontal, QtCore.QVariant("Function")) 158 176 159 177 def addShellsToModel(parameters, model, index): -
src/sas/qtgui/Perspectives/Fitting/FittingWidget.py
r358b39d raca8418 46 46 47 47 # Mapping between column index and relevant parameter name extension 48 POLY_COL_NAME = 0 49 POLY_COL_WIDTH = 1 50 POLY_COL_MIN = 2 51 POLY_COL_MAX = 3 52 POLY_COL_NPTS = 4 53 POLY_COL_NSIGMAS = 5 54 POLY_COL_FUNCTION = 6 48 55 POLY_COLUMN_DICT = { 49 1:'width',50 2:'min',51 3:'max',52 4:'npts',53 5: 'nsigmas'}56 POLY_COL_WIDTH: 'width', 57 POLY_COL_MIN: 'min', 58 POLY_COL_MAX: 'max', 59 POLY_COL_NPTS: 'npts', 60 POLY_COL_NSIGMAS: 'nsigmas'} 54 61 55 62 class FittingWidget(QtGui.QWidget, Ui_FittingWidgetUI): … … 175 182 self.current_shell_displayed = 0 176 183 self.has_error_column = False 184 self.has_poly_error_column = False 177 185 178 186 # signal communicator … … 251 259 self.setTableProperties(self.lstPoly) 252 260 # Delegates for custom editing and display 253 # self.lstPoly.setItemDelegate(PolyViewDelegate(self)) 261 self.lstPoly.setItemDelegate(PolyViewDelegate(self)) 262 # Polydispersity function combo response 263 self.lstPoly.itemDelegate().combo_updated.connect(self.onPolyComboIndexChange) 254 264 255 265 # Magnetism model displayed in magnetism list … … 368 378 self._model_model.itemChanged.connect(self.updateParamsFromModel) 369 379 self._poly_model.itemChanged.connect(self.onPolyModelChange) 380 370 381 # TODO after the poly_model prototype accepted 371 382 #self._magnet_model.itemChanged.connect(self.onMagneticModelChange) … … 406 417 self.parameters_to_fit = None 407 418 self.has_error_column = False 419 self.has_poly_error_column = False 408 420 409 421 self.respondToModelStructure(model=model, structure_factor=None) … … 488 500 # Extract changed value. Assumes proper validation by QValidator/Delegate 489 501 # TODO: abstract away hardcoded column numbers 490 if model_column == 0:502 if model_column == POLY_COL_NAME: 491 503 # Is the parameter checked for fitting? 492 504 value = item.checkState() … … 499 511 self.parameters_to_fit.remove(parameter_name) 500 512 return 501 elif model_column == 6:513 elif model_column == POLY_COL_FUNCTION: 502 514 value = item.text() 503 515 # TODO: Modify Npts/Nsigs based on function choice 516 elif model_column in [POLY_COL_MIN, POLY_COL_MAX]: 517 try: 518 value = float(item.text()) 519 except ValueError: 520 # Can't be converted properly, bring back the old value and exit 521 return 522 523 property_name = str(self._poly_model.headerData(model_column, 1).toPyObject()).lower() # Value, min, max, etc. 524 # print "%s(%s) => %d" % (parameter_name, property_name, value) 525 current_details = self.kernel_module.details[parameter_name] 526 current_details[model_column-1] = value 504 527 else: 505 528 try: … … 509 532 return 510 533 511 property_name = str(self._poly_model.headerData(model_column, 1).toPyObject()).lower() # Value, min, max, etc.512 # print "%s(%s) => %d" % (parameter_name, property_name, value)513 514 # Update the sasmodel515 # PD[ratio] -> width, npts -> npts, nsigs -> nsigmas516 self.kernel_module.setParam(parameter_name + '.' + POLY_COLUMN_DICT[model_column], value)534 property_name = str(self._poly_model.headerData(model_column, 1).toPyObject()).lower() # Value, min, max, etc. 535 # print "%s(%s) => %d" % (parameter_name, property_name, value) 536 537 # Update the sasmodel 538 # PD[ratio] -> width, npts -> npts, nsigs -> nsigmas 539 self.kernel_module.setParam(parameter_name + '.' + POLY_COLUMN_DICT[model_column], value) 517 540 518 541 # Reload the main model - may not be required if no variable is shown in main view … … 633 656 634 657 self.chi2 = res.fitness 635 param_list = res.param_list 636 param_values = res.pvec 637 param_stderr = res.stderr 658 param_list = res.param_list # ['radius', 'radius.width'] 659 param_values = res.pvec # array([ 0.36221662, 0.0146783 ]) 660 param_stderr = res.stderr # array([ 1.71293015, 1.71294233]) 638 661 params_and_errors = zip(param_values, param_stderr) 639 662 param_dict = dict(izip(param_list, params_and_errors)) … … 643 666 self.updateModelFromList(param_dict) 644 667 668 self.updatePolyModelFromList(param_dict) 669 645 670 # update charts 646 671 self.onPlot() … … 654 679 Take func and throw it inside the model row loop 655 680 """ 656 #assert isinstance(func, function)657 681 for row_i in xrange(self._model_model.rowCount()): 658 682 func(row_i) … … 715 739 self.has_error_column = True 716 740 741 def updatePolyModelFromList(self, param_dict): 742 """ 743 Update the polydispersity model with new parameters, create the errors column 744 """ 745 assert isinstance(param_dict, dict) 746 if not dict: 747 return 748 749 def updateFittedValues(row_i): 750 # Utility function for main model update 751 # internal so can use closure for param_dict 752 if row_i >= self._poly_model.rowCount(): 753 return 754 param_name = str(self._poly_model.item(row_i, 0).text()).rsplit()[-1] + '.width' 755 if param_name not in param_dict.keys(): 756 return 757 # modify the param value 758 param_repr = GuiUtils.formatNumber(param_dict[param_name][0], high=True) 759 self._poly_model.item(row_i, 1).setText(param_repr) 760 if self.has_poly_error_column: 761 error_repr = GuiUtils.formatNumber(param_dict[param_name][1], high=True) 762 self._poly_model.item(row_i, 2).setText(error_repr) 763 764 def createErrorColumn(row_i): 765 # Utility function for error column update 766 if row_i >= self._poly_model.rowCount(): 767 return 768 item = QtGui.QStandardItem() 769 for param_name in param_dict.keys(): 770 if str(self._poly_model.item(row_i, 0).text()).rsplit()[-1] + '.width' != param_name: 771 continue 772 error_repr = GuiUtils.formatNumber(param_dict[param_name][1], high=True) 773 item.setText(error_repr) 774 error_column.append(item) 775 776 # block signals temporarily, so we don't end up 777 # updating charts with every single model change on the end of fitting 778 self._poly_model.blockSignals(True) 779 self.iterateOverModel(updateFittedValues) 780 self._poly_model.blockSignals(False) 781 782 return 783 784 if self.has_poly_error_column: 785 return 786 787 error_column = [] 788 self.iterateOverModel(createErrorColumn) 789 790 # switch off reponse to model change 791 self._poly_model.blockSignals(True) 792 self._poly_model.insertColumn(2, error_column) 793 self._poly_model.blockSignals(False) 794 FittingUtilities.addErrorPolyHeadersToModel(self._poly_model) 795 796 self.has_poly_error_column = True 797 717 798 def onPlot(self): 718 799 """ … … 921 1002 922 1003 # Update the QModel 923 new_rows = FittingUtilities.addParametersToModel(self.model_parameters, self.is2D) 1004 new_rows = FittingUtilities.addParametersToModel(self.model_parameters, self.kernel_module, self.is2D) 1005 924 1006 for row in new_rows: 925 1007 self._model_model.appendRow(row) … … 996 1078 self._model_model.blockSignals(True) 997 1079 # Convert to proper indices and set requested enablement 998 _ =[self._model_model.item(row, 0).setCheckState(status) for row in rows]1080 [self._model_model.item(row, 0).setCheckState(status) for row in rows] 999 1081 self._model_model.blockSignals(False) 1000 1082 … … 1115 1197 calc_thread.addErrback(self.calculateDataFailed) 1116 1198 1117 def calculateDataFailed(self ):1199 def calculateDataFailed(self, reason): 1118 1200 """ 1119 1201 Thread returned error 1120 1202 """ 1121 print "Calculate Data failed ."1203 print "Calculate Data failed with ", reason 1122 1204 1123 1205 def complete1D(self, return_data): … … 1157 1239 1158 1240 # Plot residuals if actual data 1159 if self.data_is_loaded: 1160 residuals_plot = FittingUtilities.plotResiduals(self.data, fitted_data) 1161 residuals_plot.id = "Residual " + residuals_plot.id 1162 self.createNewIndex(residuals_plot) 1163 self.communicate.plotUpdateSignal.emit([residuals_plot]) 1241 if not self.data_is_loaded: 1242 return 1243 1244 residuals_plot = FittingUtilities.plotResiduals(self.data, fitted_data) 1245 residuals_plot.id = "Residual " + residuals_plot.id 1246 self.createNewIndex(residuals_plot) 1247 self.communicate.plotUpdateSignal.emit([residuals_plot]) 1164 1248 1165 1249 def calcException(self, etype, value, tb): … … 1197 1281 return 1198 1282 self._poly_model.clear() 1199 for row, param in enumerate(self.model_parameters.form_volume_parameters): 1200 # Counters should not be included 1201 if not param.polydisperse: 1202 continue 1203 1204 # Values from the sasmodel 1205 width = self.kernel_module.getParam(param.name+'.width') 1206 npts = self.kernel_module.getParam(param.name+'.npts') 1207 nsigs = self.kernel_module.getParam(param.name+'.nsigmas') 1208 # Potential multishell params 1209 checked_list = ["Distribution of "+param.name, str(width), 1210 str(param.limits[0]), str(param.limits[1]), 1211 str(npts), str(nsigs), ""] 1212 FittingUtilities.addCheckedListToModel(self._poly_model, checked_list) 1213 1214 # All possible polydisp. functions as strings in combobox 1215 func = QtGui.QComboBox() 1216 func.addItems([str(name_disp) for name_disp in POLYDISPERSITY_MODELS.iterkeys()]) 1217 # Default index 1218 func.setCurrentIndex(func.findText(DEFAULT_POLYDISP_FUNCTION)) 1219 # Index in the view 1220 func_index = self.lstPoly.model().index(row, 6) 1221 # Set the combobox in cell 1222 self.lstPoly.setIndexWidget(func_index, func) 1223 1283 1284 [self.setPolyModelParameters(row, param) for row, param in \ 1285 enumerate(self.model_parameters.form_volume_parameters) if param.polydisperse] 1224 1286 FittingUtilities.addPolyHeadersToModel(self._poly_model) 1225 1287 1288 def setPolyModelParameters(self, row, param): 1289 """ 1290 Creates a checked row of for a polydisperse parameter 1291 """ 1292 # Values from the sasmodel 1293 width = self.kernel_module.getParam(param.name+'.width') 1294 npts = self.kernel_module.getParam(param.name+'.npts') 1295 nsigs = self.kernel_module.getParam(param.name+'.nsigmas') 1296 # Potential multishell params 1297 checked_list = ["Distribution of "+param.name, str(width), 1298 str(param.limits[0]), str(param.limits[1]), 1299 str(npts), str(nsigs), "gaussian "] 1300 FittingUtilities.addCheckedListToModel(self._poly_model, checked_list) 1301 1302 # All possible polydisp. functions as strings in combobox 1303 func = QtGui.QComboBox() 1304 func.addItems([str(name_disp) for name_disp in POLYDISPERSITY_MODELS.iterkeys()]) 1305 # Default index 1306 func.setCurrentIndex(func.findText(DEFAULT_POLYDISP_FUNCTION)) 1307 # Index in the view 1308 func_index = self.lstPoly.model().index(row, 6) 1309 1310 def onPolyComboIndexChange(self, combo_string, row_index): 1311 """ 1312 Modify polydisp. defaults on function choice 1313 """ 1314 # get npts/nsigs for current selection 1315 param = self.model_parameters.form_volume_parameters[row_index] 1316 1317 if combo_string == 'array': 1318 try: 1319 self.loadPolydispArray() 1320 except ValueError: 1321 # Don't do anything if file lookup failed 1322 return 1323 # disable the row 1324 [self._poly_model.item(row_index, i).setEnabled(False) for i in xrange(6)] 1325 return 1326 1327 # Enable the row in case it was disabled by Array 1328 #[self._poly_model.item(row_index, i).setEnabled(True) for i in xrange(6)] 1329 1330 npts_index = self._poly_model.index(row_index, POLY_COL_NPTS) 1331 nsigs_index = self._poly_model.index(row_index, POLY_COL_NSIGMAS) 1332 1333 npts = POLYDISPERSITY_MODELS[str(combo_string)].default['npts'] 1334 nsigs = POLYDISPERSITY_MODELS[str(combo_string)].default['nsigmas'] 1335 1336 self._poly_model.setData(npts_index, QtCore.QVariant(npts)) 1337 self._poly_model.setData(nsigs_index, QtCore.QVariant(nsigs)) 1338 1339 def loadPolydispArray(self): 1340 """ 1341 Show the load file dialog and loads requested data into state 1342 """ 1343 datafile = QtGui.QFileDialog.getOpenFileName( 1344 self, "Choose a weight file", "", "All files (*.*)") 1345 if not datafile: 1346 logging.info("No weight data chosen.") 1347 return 1348 values = [] 1349 weights = [] 1350 with open(datafile, 'r') as column_file: 1351 column_data = [line.rstrip().split() for line in column_file.readlines()] 1352 for line in column_data: 1353 try: 1354 values.append(float(line[0])) 1355 weights.append(float(line[1])) 1356 except (ValueError, IndexError): 1357 # just pass through if line with bad data 1358 pass 1359 1360 self.disp_model = POLYDISPERSITY_MODELS['array']() 1361 self.disp_model.set_weights(np.array(values), np.array(weights)) 1362 1226 1363 def setMagneticModel(self): 1227 1364 """ … … 1231 1368 return 1232 1369 self._magnet_model.clear() 1233 for param in self.model_parameters.call_parameters: 1234 if param.type != "magnetic": 1235 continue 1236 checked_list = [param.name, 1237 str(param.default), 1238 str(param.limits[0]), 1239 str(param.limits[1]), 1240 param.units] 1241 FittingUtilities.addCheckedListToModel(self._magnet_model, checked_list) 1242 1370 [self.addCheckedMagneticListToModel(param, self._magnet_model) for param in \ 1371 self.model_parameters.call_parameters if param != 'magnetic'] 1243 1372 FittingUtilities.addHeadersToModel(self._magnet_model) 1373 1374 def addCheckedMagneticListToModel(self, param, model): 1375 """ 1376 Wrapper for model update with a subset of magnetic parameters 1377 """ 1378 checked_list = [param.name, 1379 str(param.default), 1380 str(param.limits[0]), 1381 str(param.limits[1]), 1382 param.units] 1383 1384 FittingUtilities.addCheckedListToModel(model, checked_list) 1244 1385 1245 1386 def enableStructureFactorControl(self, structure_factor): -
src/sas/qtgui/Perspectives/Fitting/UI/FittingWidgetUI.ui
r00b3b40 raca8418 402 402 </property> 403 403 <property name="selectionMode"> 404 <enum>QAbstractItemView:: MultiSelection</enum>404 <enum>QAbstractItemView::ExtendedSelection</enum> 405 405 </property> 406 406 <property name="selectionBehavior"> -
src/sas/qtgui/Perspectives/Fitting/ViewDelegate.py
rdc5ef15 raca8418 21 21 POLY_NSIGS=5 22 22 POLY_FUNCTION=6 23 POLY_EDITABLE_PARAMS = [POLY_MIN, POLY_MAX, POLY_NPTS, POLY_NSIGS] 23 24 24 25 … … 94 95 Custom delegate for appearance and behavior control of the polydispersity view 95 96 """ 97 combo_updated = QtCore.pyqtSignal(str, int) 96 98 def createEditor(self, widget, option, index): 97 99 # Remember the current choice 98 100 current_text = index.data().toString() 101 if not index.isValid(): 102 return 0 99 103 if index.column() == POLY_FUNCTION: 100 104 editor = QtGui.QComboBox(widget) … … 102 106 editor.addItem(function) 103 107 current_index = editor.findText(current_text) 104 editor.setCurrentIndex(current_index if current_index>-1 else 0) 108 editor.setCurrentIndex(current_index if current_index>-1 else 3) 109 editor.currentIndexChanged.connect(lambda: self.combo_updated.emit(str(editor.currentText()), index.row())) 110 return editor 111 elif index.column() in POLY_EDITABLE_PARAMS: 112 editor = QtGui.QLineEdit(widget) 113 validator = QtGui.QDoubleValidator() 114 editor.setValidator(validator) 105 115 return editor 106 116 else: 107 117 QtGui.QStyledItemDelegate.createEditor(self, widget, option, index) 118 119 def paint(self, painter, option, index): 120 """ 121 Overwrite generic painter for certain columns 122 """ 123 if index.column() in (POLY_MIN, POLY_MAX): 124 # Units - present in nice HTML 125 options = QtGui.QStyleOptionViewItemV4(option) 126 self.initStyleOption(options,index) 127 128 style = QtGui.QApplication.style() if options.widget is None else options.widget.style() 129 130 # Prepare document for inserting into cell 131 doc = QtGui.QTextDocument() 132 133 # Convert the unit description into HTML 134 text_html = GuiUtils.convertUnitToHTML(str(options.text)) 135 doc.setHtml(text_html) 136 137 # delete the original content 138 options.text = "" 139 style.drawControl(QtGui.QStyle.CE_ItemViewItem, options, painter, options.widget); 140 141 context = QtGui.QAbstractTextDocumentLayout.PaintContext() 142 textRect = style.subElementRect(QtGui.QStyle.SE_ItemViewItemText, options) 143 144 painter.save() 145 painter.translate(textRect.topLeft()) 146 painter.setClipRect(textRect.translated(-textRect.topLeft())) 147 # Draw the QTextDocument in the cell 148 doc.documentLayout().draw(painter, context) 149 painter.restore() 150 else: 151 # Just the default paint 152 QtGui.QStyledItemDelegate.paint(self, painter, option, index)
Note: See TracChangeset
for help on using the changeset viewer.