Changeset 2add354 in sasview for src/sas/qtgui/Perspectives


Ignore:
Timestamp:
May 9, 2017 3:38:40 AM (8 years ago)
Author:
Piotr Rozyczko <rozyczko@…>
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
Message:

Code review fixes for SASVIEW-273

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 
     1import numpy as np 
    22 
    33from sas.sasgui.guiframe.dataFitting import Data1D 
     
    7272        ymin = -qmax 
    7373 
    74         x = numpy.linspace(start=xmin, stop=xmax, num=qstep, endpoint=True) 
    75         y = numpy.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) 
    7676        # Use data info instead 
    77         new_x = numpy.tile(x, (len(y), 1)) 
    78         new_y = numpy.tile(y, (len(x), 1)) 
     77        new_x = np.tile(x, (len(y), 1)) 
     78        new_y = np.tile(y, (len(x), 1)) 
    7979        new_y = new_y.swapaxes(0, 1) 
    8080 
     
    8282        qx_data = new_x.flatten() 
    8383        qy_data = new_y.flatten() 
    84         q_data = numpy.sqrt(qx_data * qx_data + qy_data * qy_data) 
     84        q_data = np.sqrt(qx_data * qx_data + qy_data * qy_data) 
    8585 
    8686        # set all True (standing for unmasked) as default 
    87         mask = numpy.ones(len(qx_data), dtype=bool) 
     87        mask = np.ones(len(qx_data), dtype=bool) 
    8888        # calculate the range of qx and qy: this way, 
    8989        # it is a little more independent 
     
    9393 
    9494        self._data.source = Source() 
    95         self._data.data = numpy.ones(len(mask)) 
    96         self._data.err_data = numpy.ones(len(mask)) 
     95        self._data.data = np.ones(len(mask)) 
     96        self._data.err_data = np.ones(len(mask)) 
    9797        self._data.qx_data = qx_data 
    9898        self._data.qy_data = qy_data 
     
    120120        new_plot = Data1D(x=x, y=y) 
    121121        new_plot.is_data = False 
    122         new_plot.dy = numpy.zeros(len(y)) 
     122        new_plot.dy = np.zeros(len(y)) 
    123123        _yaxis, _yunit = data.get_yaxis() 
    124124        _xaxis, _xunit = data.get_xaxis() 
     
    141141        update_chisqr, source = return_data 
    142142 
    143         numpy.nan_to_num(image) 
     143        np.nan_to_num(image) 
    144144        new_plot = Data2D(image=image, err_image=data.err_data) 
    145145        new_plot.name = model.name + '2d' 
     
    194194            qmin = 0 
    195195            try: 
    196                 x = max(numpy.fabs(self.data.xmin), numpy.fabs(self.data.xmax)) 
    197                 y = max(numpy.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)) 
    198198            except (ValueError, TypeError): 
    199199                msg = "Unable to find min/max of \n data named %s" % \ 
    200200                            self.data.filename 
    201201                raise ValueError, msg 
    202             qmax = numpy.sqrt(x * x + y * y) 
     202            qmax = np.sqrt(x * x + y * y) 
    203203            npts = len(self.data.data) 
    204204        return qmin, qmax, npts 
  • src/sas/qtgui/Perspectives/Fitting/FittingPerspective.py

    r31e4bb8 r2add354  
    6161        Overwrite QDialog close method to allow for custom widget close 
    6262        """ 
     63        # Invoke fit page events 
     64        for tab in self.tabs: 
     65            tab.close() 
    6366        if self._allow_close: 
    6467            # reset the closability flag 
     
    6669            event.accept() 
    6770        else: 
    68             event.ignore() 
    6971            # Maybe we should just minimize 
    7072            self.setWindowState(QtCore.Qt.WindowMinimized) 
     73            event.ignore() 
    7174 
    7275    def addFit(self, data): 
  • src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py

    rd48cc19 r2add354  
    5656        item1 = QtGui.QStandardItem(item_name) 
    5757        item1.setCheckable(True) 
     58        item1.setEditable(False) 
    5859        item_err = QtGui.QStandardItem() 
    5960        # check for polydisp params 
    6061        if param.polydisperse: 
    6162            poly_item = QtGui.QStandardItem("Polydispersity") 
     63            poly_item.setEditable(False) 
    6264            item1_1 = QtGui.QStandardItem("Distribution") 
     65            item1_1.setEditable(False) 
    6366            # Find param in volume_params 
    6467            for p in parameters.form_volume_parameters: 
     
    6669                    continue 
    6770                item1_2 = QtGui.QStandardItem(str(p.default)) 
     71                item1_2.setEditable(False) 
    6872                item1_3 = QtGui.QStandardItem(str(p.limits[0])) 
     73                item1_3.setEditable(False) 
    6974                item1_4 = QtGui.QStandardItem(str(p.limits[1])) 
     75                item1_4.setEditable(False) 
    7076                item1_5 = QtGui.QStandardItem(p.units) 
     77                item1_5.setEditable(False) 
    7178                poly_item.appendRow([item1_1, item1_2, item1_3, item1_4, item1_5]) 
    7279                break 
     
    8188        item4 = QtGui.QStandardItem(str(param.limits[1])) 
    8289        item5 = QtGui.QStandardItem(param.units) 
     90        item5.setEditable(False) 
    8391        item.append([item1, item2, item3, item4, item5]) 
    8492    return item 
     
    95103        item1 = QtGui.QStandardItem(item_name) 
    96104        item1.setCheckable(True) 
     105        item1.setEditable(False) 
    97106        # Param values 
     107        # TODO: add delegate for validation of cells 
    98108        item2 = QtGui.QStandardItem(str(param.default)) 
    99         # TODO: the error column. 
    100         # Either add a proxy model or a custom view delegate 
    101         item_err = QtGui.QStandardItem() 
    102109        item4 = QtGui.QStandardItem(str(param.limits[0])) 
    103110        item5 = QtGui.QStandardItem(str(param.limits[1])) 
    104111        item6 = QtGui.QStandardItem(param.units) 
     112        item6.setEditable(False) 
    105113        item.append([item1, item2, item4, item5, item6]) 
    106114    return item 
  • src/sas/qtgui/Perspectives/Fitting/FittingWidget.py

    r02ddfb4 r2add354  
    1212from PyQt4 import QtGui 
    1313from PyQt4 import QtCore 
     14from PyQt4 import QtWebKit 
    1415 
    1516from sasmodels import generate 
     
    3233from SmearingWidget import SmearingWidget 
    3334from OptionsWidget import OptionsWidget 
     35from FitPage import FitPage 
    3436 
    3537TAB_MAGNETISM = 4 
     
    7375        self.log_points = False 
    7476        self.weighting = 0 
     77        self.chi2 = None 
    7578 
    7679        # Data for chosen model 
     
    132135        """ 
    133136        self.lstParams.setStyleSheet(stylesheet) 
     137        self.lstParams.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) 
     138        self.lstParams.customContextMenuRequested.connect(self.showModelDescription) 
    134139 
    135140        # Poly model displayed in poly list 
     
    163168        self.initializeControls() 
    164169 
     170        # Display HTML content 
     171        self.helpView = QtWebKit.QWebView() 
     172 
    165173        self._index = None 
    166174        if data is not None: 
    167175            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 
    168183 
    169184    @property 
     
    197212        self.lblFilename.setText(self.logic.data.filename) 
    198213        self.updateQRange() 
    199         self.cmdFit.setEnabled(True) 
    200214        # Switch off Data2D control 
    201215        self.chk2DView.setEnabled(False) 
     
    285299        self.cmdFit.clicked.connect(self.onFit) 
    286300        self.cmdPlot.clicked.connect(self.onPlot) 
     301        self.cmdHelp.clicked.connect(self.onHelp) 
    287302 
    288303        # Respond to change in parameters from the UI 
     
    294309        # Signals from separate tabs asking for replot 
    295310        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)) 
    296332 
    297333    def onSelectModel(self): 
     
    321357            # Create default datasets if no data passed 
    322358            self.createDefaultDataset() 
     359 
     360        #state = self.currentState() 
    323361 
    324362    def onSelectStructureFactor(self): 
     
    403441        pass # debug anchor 
    404442 
     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 
    405452    def onFit(self): 
    406453        """ 
     
    438485        fitter.set_model(model, fit_id, params_to_fit, data=data, 
    439486                         constraints=constraints) 
     487 
    440488        fitter.set_data(data=data, id=fit_id, smearer=smearer, qmin=qmin, 
    441489                        qmax=qmax) 
     
    501549        self.communicate.statusBarUpdateSignal.emit(msg) 
    502550 
    503         fitness = res.fitness 
     551        self.chi2 = res.fitness 
    504552        param_list = res.param_list 
    505553        param_values = res.pvec 
     
    516564 
    517565        # 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) 
    519567        self.lblChi2Value.setText(chi2_repr) 
    520568 
     
    586634        # Regardless of previous state, this should now be `plot show` functionality only 
    587635        self.cmdPlot.setText("Show Plot") 
    588         self.recalculatePlotData() 
    589636        self.showPlot() 
    590637 
     
    685732        checked_list = ['background', '0.001', '-inf', 'inf', '1/cm'] 
    686733        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) 
    687737 
    688738    def addScaleToModel(self, model): 
     
    693743        checked_list = ['scale', '1.0', '0.0', 'inf', ''] 
    694744        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) 
    695748 
    696749    def addWeightingToData(self, data): 
     
    781834        if model_column == 0: 
    782835            self.checkboxSelected(item) 
     836            self.cmdFit.setEnabled(self.parameters_to_fit != [] and self.logic.data_is_loaded) 
    783837            return 
    784838 
     
    787841 
    788842        # 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 
    790849        parameter_name = str(self._model_model.data(name_index).toPyObject()) # sld, background etc. 
    791850        property_name = str(self._model_model.headerData(1, model_column).toPyObject()) # Value, min, max, etc. 
     
    9441003        self.createNewIndex(fitted_data) 
    9451004        # 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) 
    9471006        # 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) 
    9491008        self.lblChi2Value.setText(chi2_repr) 
    9501009 
  • src/sas/qtgui/Perspectives/Fitting/UI/FittingWidgetUI.ui

    r180bd54 r2add354  
    158158            </item> 
    159159            <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> 
    161165            </item> 
    162166            <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> 
    164172            </item> 
    165173            <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> 
    167179            </item> 
    168180           </layout> 
     
    172184            <property name="styleSheet"> 
    173185             <string notr="true"/> 
     186            </property> 
     187            <property name="editTriggers"> 
     188             <set>QAbstractItemView::CurrentChanged|QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked</set> 
    174189            </property> 
    175190            <property name="selectionMode"> 
  • src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingWidgetTest.py

    r02ddfb4 r2add354  
    1818 
    1919from sas.sasgui.guiframe.dataFitting import Data1D 
     20from sas.sasgui.guiframe.dataFitting import Data2D 
    2021 
    2122app = QtGui.QApplication(sys.argv) 
     
    5051        self.assertFalse(self.widget.data_is_loaded) 
    5152 
    52     def testSelectCategory(self): 
     53    def testSelectCategoryDefault(self): 
    5354        """ 
    5455        Test if model categories have been loaded properly 
     
    7980        # self.assertTrue(widget_with_data.cmdFit.isEnabled()) 
    8081        self.assertFalse(widget_with_data.acceptsData()) 
    81  
    82     def testSelectModel(self): 
    83         """ 
    84         Test if models have been loaded properly 
    85         """ 
    86         fittingWindow =  self.widget 
    87  
    88         #Test loading from json categories 
    89         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 combobox 
    105         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  
    11982 
    12083    def testSelectPolydispersity(self): 
     
    154117        pass 
    155118 
    156     def  testSelectCategory(self): 
     119    def testSelectCategory(self): 
    157120        """ 
    158121        Assure proper behaviour on changing category 
     
    498461        self.assertEqual(spy.count(), 1) 
    499462 
    500     def testOnFit(self): 
     463    def testOnFit1D(self): 
    501464        """ 
    502465        Test the threaded fitting call 
     
    540503            self.assertEqual(update_spy.count(), 1) 
    541504 
     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 
    542552 
    543553if __name__ == "__main__": 
Note: See TracChangeset for help on using the changeset viewer.