Changeset 2add354 in sasview for src/sas/qtgui/Perspectives/Fitting
- Timestamp:
- May 9, 2017 3:38:40 AM (8 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:
- f7f5796
- Parents:
- 31e4bb8
- Location:
- src/sas/qtgui/Perspectives/Fitting
- Files:
-
- 1 added
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
src/sas/qtgui/Perspectives/Fitting/FittingLogic.py
r180bd54 r2add354 1 import numpy 1 import numpy as np 2 2 3 3 from sas.sasgui.guiframe.dataFitting import Data1D … … 72 72 ymin = -qmax 73 73 74 x = n umpy.linspace(start=xmin, stop=xmax, num=qstep, endpoint=True)75 y = n umpy.linspace(start=ymin, stop=ymax, num=qstep, endpoint=True)74 x = np.linspace(start=xmin, stop=xmax, num=qstep, endpoint=True) 75 y = np.linspace(start=ymin, stop=ymax, num=qstep, endpoint=True) 76 76 # Use data info instead 77 new_x = n umpy.tile(x, (len(y), 1))78 new_y = n umpy.tile(y, (len(x), 1))77 new_x = np.tile(x, (len(y), 1)) 78 new_y = np.tile(y, (len(x), 1)) 79 79 new_y = new_y.swapaxes(0, 1) 80 80 … … 82 82 qx_data = new_x.flatten() 83 83 qy_data = new_y.flatten() 84 q_data = n umpy.sqrt(qx_data * qx_data + qy_data * qy_data)84 q_data = np.sqrt(qx_data * qx_data + qy_data * qy_data) 85 85 86 86 # set all True (standing for unmasked) as default 87 mask = n umpy.ones(len(qx_data), dtype=bool)87 mask = np.ones(len(qx_data), dtype=bool) 88 88 # calculate the range of qx and qy: this way, 89 89 # it is a little more independent … … 93 93 94 94 self._data.source = Source() 95 self._data.data = n umpy.ones(len(mask))96 self._data.err_data = n umpy.ones(len(mask))95 self._data.data = np.ones(len(mask)) 96 self._data.err_data = np.ones(len(mask)) 97 97 self._data.qx_data = qx_data 98 98 self._data.qy_data = qy_data … … 120 120 new_plot = Data1D(x=x, y=y) 121 121 new_plot.is_data = False 122 new_plot.dy = n umpy.zeros(len(y))122 new_plot.dy = np.zeros(len(y)) 123 123 _yaxis, _yunit = data.get_yaxis() 124 124 _xaxis, _xunit = data.get_xaxis() … … 141 141 update_chisqr, source = return_data 142 142 143 n umpy.nan_to_num(image)143 np.nan_to_num(image) 144 144 new_plot = Data2D(image=image, err_image=data.err_data) 145 145 new_plot.name = model.name + '2d' … … 194 194 qmin = 0 195 195 try: 196 x = max(n umpy.fabs(self.data.xmin), numpy.fabs(self.data.xmax))197 y = max(n umpy.fabs(self.data.ymin), numpy.fabs(self.data.ymax))196 x = max(np.fabs(self.data.xmin), np.fabs(self.data.xmax)) 197 y = max(np.fabs(self.data.ymin), np.fabs(self.data.ymax)) 198 198 except (ValueError, TypeError): 199 199 msg = "Unable to find min/max of \n data named %s" % \ 200 200 self.data.filename 201 201 raise ValueError, msg 202 qmax = n umpy.sqrt(x * x + y * y)202 qmax = np.sqrt(x * x + y * y) 203 203 npts = len(self.data.data) 204 204 return qmin, qmax, npts -
src/sas/qtgui/Perspectives/Fitting/FittingPerspective.py
r31e4bb8 r2add354 61 61 Overwrite QDialog close method to allow for custom widget close 62 62 """ 63 # Invoke fit page events 64 for tab in self.tabs: 65 tab.close() 63 66 if self._allow_close: 64 67 # reset the closability flag … … 66 69 event.accept() 67 70 else: 68 event.ignore()69 71 # Maybe we should just minimize 70 72 self.setWindowState(QtCore.Qt.WindowMinimized) 73 event.ignore() 71 74 72 75 def addFit(self, data): -
src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py
rd48cc19 r2add354 56 56 item1 = QtGui.QStandardItem(item_name) 57 57 item1.setCheckable(True) 58 item1.setEditable(False) 58 59 item_err = QtGui.QStandardItem() 59 60 # check for polydisp params 60 61 if param.polydisperse: 61 62 poly_item = QtGui.QStandardItem("Polydispersity") 63 poly_item.setEditable(False) 62 64 item1_1 = QtGui.QStandardItem("Distribution") 65 item1_1.setEditable(False) 63 66 # Find param in volume_params 64 67 for p in parameters.form_volume_parameters: … … 66 69 continue 67 70 item1_2 = QtGui.QStandardItem(str(p.default)) 71 item1_2.setEditable(False) 68 72 item1_3 = QtGui.QStandardItem(str(p.limits[0])) 73 item1_3.setEditable(False) 69 74 item1_4 = QtGui.QStandardItem(str(p.limits[1])) 75 item1_4.setEditable(False) 70 76 item1_5 = QtGui.QStandardItem(p.units) 77 item1_5.setEditable(False) 71 78 poly_item.appendRow([item1_1, item1_2, item1_3, item1_4, item1_5]) 72 79 break … … 81 88 item4 = QtGui.QStandardItem(str(param.limits[1])) 82 89 item5 = QtGui.QStandardItem(param.units) 90 item5.setEditable(False) 83 91 item.append([item1, item2, item3, item4, item5]) 84 92 return item … … 95 103 item1 = QtGui.QStandardItem(item_name) 96 104 item1.setCheckable(True) 105 item1.setEditable(False) 97 106 # Param values 107 # TODO: add delegate for validation of cells 98 108 item2 = QtGui.QStandardItem(str(param.default)) 99 # TODO: the error column.100 # Either add a proxy model or a custom view delegate101 item_err = QtGui.QStandardItem()102 109 item4 = QtGui.QStandardItem(str(param.limits[0])) 103 110 item5 = QtGui.QStandardItem(str(param.limits[1])) 104 111 item6 = QtGui.QStandardItem(param.units) 112 item6.setEditable(False) 105 113 item.append([item1, item2, item4, item5, item6]) 106 114 return item -
src/sas/qtgui/Perspectives/Fitting/FittingWidget.py
r02ddfb4 r2add354 12 12 from PyQt4 import QtGui 13 13 from PyQt4 import QtCore 14 from PyQt4 import QtWebKit 14 15 15 16 from sasmodels import generate … … 32 33 from SmearingWidget import SmearingWidget 33 34 from OptionsWidget import OptionsWidget 35 from FitPage import FitPage 34 36 35 37 TAB_MAGNETISM = 4 … … 73 75 self.log_points = False 74 76 self.weighting = 0 77 self.chi2 = None 75 78 76 79 # Data for chosen model … … 132 135 """ 133 136 self.lstParams.setStyleSheet(stylesheet) 137 self.lstParams.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) 138 self.lstParams.customContextMenuRequested.connect(self.showModelDescription) 134 139 135 140 # Poly model displayed in poly list … … 163 168 self.initializeControls() 164 169 170 # Display HTML content 171 self.helpView = QtWebKit.QWebView() 172 165 173 self._index = None 166 174 if data is not None: 167 175 self.data = data 176 177 def close(self): 178 """ 179 Remember to kill off things on exit 180 """ 181 self.helpView.close() 182 del self.helpView 168 183 169 184 @property … … 197 212 self.lblFilename.setText(self.logic.data.filename) 198 213 self.updateQRange() 199 self.cmdFit.setEnabled(True)200 214 # Switch off Data2D control 201 215 self.chk2DView.setEnabled(False) … … 285 299 self.cmdFit.clicked.connect(self.onFit) 286 300 self.cmdPlot.clicked.connect(self.onPlot) 301 self.cmdHelp.clicked.connect(self.onHelp) 287 302 288 303 # Respond to change in parameters from the UI … … 294 309 # Signals from separate tabs asking for replot 295 310 self.options_widget.plot_signal.connect(self.onOptionsUpdate) 311 312 def showModelDescription(self, position): 313 """ 314 Shows a window with model description, when right clicked in the treeview 315 """ 316 msg = 'Model description:\n' 317 info = "Info" 318 if self.kernel_module is not None: 319 if str(self.kernel_module.description).rstrip().lstrip() == '': 320 msg += "Sorry, no information is available for this model." 321 else: 322 msg += self.kernel_module.description + '\n' 323 else: 324 msg += "You must select a model to get information on this" 325 326 menu = QtGui.QMenu() 327 label = QtGui.QLabel(msg) 328 action = QtGui.QWidgetAction(self) 329 action.setDefaultWidget(label) 330 menu.addAction(action) 331 menu.exec_(self.lstParams.viewport().mapToGlobal(position)) 296 332 297 333 def onSelectModel(self): … … 321 357 # Create default datasets if no data passed 322 358 self.createDefaultDataset() 359 360 #state = self.currentState() 323 361 324 362 def onSelectStructureFactor(self): … … 403 441 pass # debug anchor 404 442 443 def onHelp(self): 444 """ 445 Show the "Fitting" section of help 446 """ 447 tree_location = self.parent.HELP_DIRECTORY_LOCATION +\ 448 "/user/sasgui/perspectives/fitting/fitting_help.html" 449 self.helpView.load(QtCore.QUrl(tree_location)) 450 self.helpView.show() 451 405 452 def onFit(self): 406 453 """ … … 438 485 fitter.set_model(model, fit_id, params_to_fit, data=data, 439 486 constraints=constraints) 487 440 488 fitter.set_data(data=data, id=fit_id, smearer=smearer, qmin=qmin, 441 489 qmax=qmax) … … 501 549 self.communicate.statusBarUpdateSignal.emit(msg) 502 550 503 fitness= res.fitness551 self.chi2 = res.fitness 504 552 param_list = res.param_list 505 553 param_values = res.pvec … … 516 564 517 565 # Read only value - we can get away by just printing it here 518 chi2_repr = GuiUtils.formatNumber( fitness, high=True)566 chi2_repr = GuiUtils.formatNumber(self.chi2, high=True) 519 567 self.lblChi2Value.setText(chi2_repr) 520 568 … … 586 634 # Regardless of previous state, this should now be `plot show` functionality only 587 635 self.cmdPlot.setText("Show Plot") 588 self.recalculatePlotData()589 636 self.showPlot() 590 637 … … 685 732 checked_list = ['background', '0.001', '-inf', 'inf', '1/cm'] 686 733 FittingUtilities.addCheckedListToModel(model, checked_list) 734 last_row = model.rowCount()-1 735 model.item(last_row, 0).setEditable(False) 736 model.item(last_row, 4).setEditable(False) 687 737 688 738 def addScaleToModel(self, model): … … 693 743 checked_list = ['scale', '1.0', '0.0', 'inf', ''] 694 744 FittingUtilities.addCheckedListToModel(model, checked_list) 745 last_row = model.rowCount()-1 746 model.item(last_row, 0).setEditable(False) 747 model.item(last_row, 4).setEditable(False) 695 748 696 749 def addWeightingToData(self, data): … … 781 834 if model_column == 0: 782 835 self.checkboxSelected(item) 836 self.cmdFit.setEnabled(self.parameters_to_fit != [] and self.logic.data_is_loaded) 783 837 return 784 838 … … 787 841 788 842 # Extract changed value. Assumes proper validation by QValidator/Delegate 789 value = float(item.text()) 843 # TODO: disable model update for uneditable cells/columns 844 try: 845 value = float(item.text()) 846 except ValueError: 847 # Unparsable field 848 return 790 849 parameter_name = str(self._model_model.data(name_index).toPyObject()) # sld, background etc. 791 850 property_name = str(self._model_model.headerData(1, model_column).toPyObject()) # Value, min, max, etc. … … 944 1003 self.createNewIndex(fitted_data) 945 1004 # Calculate difference between return_data and logic.data 946 chi2 = FittingUtilities.calculateChi2(fitted_data, self.logic.data)1005 self.chi2 = FittingUtilities.calculateChi2(fitted_data, self.logic.data) 947 1006 # Update the control 948 chi2_repr = "---" if chi2 is None else GuiUtils.formatNumber(chi2, high=True)1007 chi2_repr = "---" if self.chi2 is None else GuiUtils.formatNumber(self.chi2, high=True) 949 1008 self.lblChi2Value.setText(chi2_repr) 950 1009 -
src/sas/qtgui/Perspectives/Fitting/UI/FittingWidgetUI.ui
r180bd54 r2add354 158 158 </item> 159 159 <item row="1" column="0"> 160 <widget class="QComboBox" name="cbCategory"/> 160 <widget class="QComboBox" name="cbCategory"> 161 <property name="toolTip"> 162 <string>Select a category</string> 163 </property> 164 </widget> 161 165 </item> 162 166 <item row="1" column="1"> 163 <widget class="QComboBox" name="cbModel"/> 167 <widget class="QComboBox" name="cbModel"> 168 <property name="toolTip"> 169 <string>Select a model</string> 170 </property> 171 </widget> 164 172 </item> 165 173 <item row="1" column="2"> 166 <widget class="QComboBox" name="cbStructureFactor"/> 174 <widget class="QComboBox" name="cbStructureFactor"> 175 <property name="toolTip"> 176 <string>Select a structure factor</string> 177 </property> 178 </widget> 167 179 </item> 168 180 </layout> … … 172 184 <property name="styleSheet"> 173 185 <string notr="true"/> 186 </property> 187 <property name="editTriggers"> 188 <set>QAbstractItemView::CurrentChanged|QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked</set> 174 189 </property> 175 190 <property name="selectionMode"> -
src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingWidgetTest.py
r02ddfb4 r2add354 18 18 19 19 from sas.sasgui.guiframe.dataFitting import Data1D 20 from sas.sasgui.guiframe.dataFitting import Data2D 20 21 21 22 app = QtGui.QApplication(sys.argv) … … 50 51 self.assertFalse(self.widget.data_is_loaded) 51 52 52 def testSelectCategory (self):53 def testSelectCategoryDefault(self): 53 54 """ 54 55 Test if model categories have been loaded properly … … 79 80 # self.assertTrue(widget_with_data.cmdFit.isEnabled()) 80 81 self.assertFalse(widget_with_data.acceptsData()) 81 82 def testSelectModel(self):83 """84 Test if models have been loaded properly85 """86 fittingWindow = self.widget87 88 #Test loading from json categories89 model_list = fittingWindow.master_category_dict["Cylinder"]90 self.assertTrue(['cylinder', True] in model_list)91 self.assertTrue(['core_shell_cylinder', True] in model_list)92 self.assertTrue(['barbell', True] in model_list)93 self.assertTrue(['core_shell_bicelle', True] in model_list)94 self.assertTrue(['flexible_cylinder', True] in model_list)95 self.assertTrue(['flexible_cylinder_elliptical', True] in model_list)96 self.assertTrue(['pearl_necklace', True] in model_list)97 self.assertTrue(['capped_cylinder', True] in model_list)98 self.assertTrue(['elliptical_cylinder', True] in model_list)99 self.assertTrue(['pringle', True] in model_list)100 self.assertTrue(['hollow_cylinder', True] in model_list)101 self.assertTrue(['core_shell_bicelle_elliptical', True] in model_list)102 self.assertTrue(['stacked_disks', True] in model_list)103 104 #Test for existence in combobox105 self.assertNotEqual(fittingWindow.cbModel.findText("cylinder"),-1)106 self.assertNotEqual(fittingWindow.cbModel.findText("core_shell_cylinder"),-1)107 self.assertNotEqual(fittingWindow.cbModel.findText("barbell"),-1)108 self.assertNotEqual(fittingWindow.cbModel.findText("core_shell_bicelle"),-1)109 self.assertNotEqual(fittingWindow.cbModel.findText("flexible_cylinder"),-1)110 self.assertNotEqual(fittingWindow.cbModel.findText("flexible_cylinder_elliptical"),-1)111 self.assertNotEqual(fittingWindow.cbModel.findText("pearl_necklace"),-1)112 self.assertNotEqual(fittingWindow.cbModel.findText("capped_cylinder"),-1)113 self.assertNotEqual(fittingWindow.cbModel.findText("elliptical_cylinder"),-1)114 self.assertNotEqual(fittingWindow.cbModel.findText("pringle"),-1)115 self.assertNotEqual(fittingWindow.cbModel.findText("hollow_cylinder"),-1)116 self.assertNotEqual(fittingWindow.cbModel.findText("core_shell_bicelle_elliptical"),-1)117 self.assertNotEqual(fittingWindow.cbModel.findText("stacked_disks"),-1)118 119 82 120 83 def testSelectPolydispersity(self): … … 154 117 pass 155 118 156 def 119 def testSelectCategory(self): 157 120 """ 158 121 Assure proper behaviour on changing category … … 498 461 self.assertEqual(spy.count(), 1) 499 462 500 def testOnFit (self):463 def testOnFit1D(self): 501 464 """ 502 465 Test the threaded fitting call … … 540 503 self.assertEqual(update_spy.count(), 1) 541 504 505 def testOnFit2D(self): 506 """ 507 Test the threaded fitting call 508 """ 509 # Set data 510 test_data = Data2D(image=[1.0, 2.0, 3.0], 511 err_image=[0.01, 0.02, 0.03], 512 qx_data=[0.1, 0.2, 0.3], 513 qy_data=[0.1, 0.2, 0.3], 514 xmin=0.1, xmax=0.3, ymin=0.1, ymax=0.3, 515 mask=[True, True, True]) 516 517 # Force same data into logic 518 self.widget.logic.data = test_data 519 self.widget.data_is_loaded = True 520 category_index = self.widget.cbCategory.findText("Sphere") 521 self.widget.cbCategory.setCurrentIndex(category_index) 522 523 self.widget.show() 524 525 # Test no fitting params 526 self.widget.parameters_to_fit = [] 527 528 with self.assertRaises(ValueError) as error: 529 self.widget.onFit() 530 self.assertEqual(str(error.exception), 'no fitting parameters') 531 532 # Assing fitting params 533 self.widget.parameters_to_fit = ['scale'] 534 535 # Spying on status update signal 536 update_spy = QtSignalSpy(self.widget, self.widget.communicate.statusBarUpdateSignal) 537 538 with threads.deferToThread as MagicMock: 539 self.widget.onFit() 540 # thread called 541 self.assertTrue(threads.deferToThread.called) 542 # thread method is 'compute' 543 self.assertEqual(threads.deferToThread.call_args_list[0][0][0].__name__, 'compute') 544 545 # the fit button changed caption and got disabled 546 self.assertEqual(self.widget.cmdFit.text(), 'Calculating...') 547 self.assertFalse(self.widget.cmdFit.isEnabled()) 548 549 # Signal pushed up 550 self.assertEqual(update_spy.count(), 1) 551 542 552 543 553 if __name__ == "__main__":
Note: See TracChangeset
for help on using the changeset viewer.