Changeset dd2c2a31 in sasview for src/sas/qtgui/Perspectives


Ignore:
Timestamp:
Sep 8, 2018 9:51:20 AM (6 years ago)
Author:
GitHub <noreply@…>
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)
Message:

Merge e0da3076784fd83799c78408c1e47e2575db46d3 into 5e0891b0b2b36e6704b9104db380d985888c0424

Location:
src/sas/qtgui/Perspectives/Fitting
Files:
1 added
8 edited

Legend:

Unmodified
Added
Removed
  • src/sas/qtgui/Perspectives/Fitting/FittingLogic.py

    rb4d05bd r9ba91b7  
    161161        Create a new 1D data instance based on fitting results 
    162162        """ 
    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']) 
    172165 
    173166    def new2DPlot(self, return_data): 
     
    175168        Create a new 2D data instance based on fitting results 
    176169        """ 
    177         image, data, page_id, model, state, toggle_mode_on,\ 
    178         elapsed, index, fid, qmin, qmax, weight, \ 
    179         update_chisqr, source = return_data 
     170        image = return_data['image'] 
     171        data = return_data['data'] 
     172        model = return_data['model'] 
    180173 
    181174        np.nan_to_num(image) 
     
    183176        new_plot.name = model.name + '2d' 
    184177        new_plot.title = "Analytical model 2D " 
    185         new_plot.id = str(page_id) + " " + data.name 
    186         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" 
    187180        new_plot.detector = data.detector 
    188181        new_plot.source = data.source 
     
    218211        (pq_plot, sq_plot). If either are unavailable, the corresponding plot is None. 
    219212        """ 
    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 
    237219 
    238220    def computeDataRange(self): 
  • src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py

    rb764ae5 r01b4877  
    88from sas.qtgui.Plotting.PlotterData import Data1D 
    99from sas.qtgui.Plotting.PlotterData import Data2D 
     10 
     11from sas.qtgui.Perspectives.Fitting.AssociatedComboBox import AssociatedComboBox 
    1012 
    1113model_header_captions = ['Parameter', 'Value', 'Min', 'Max', 'Units'] 
     
    6163    return (param_name, param_length) 
    6264 
    63 def addParametersToModel(parameters, kernel_module, is2D): 
    64     """ 
    65     Update local ModelModel with sasmodel parameters 
     65def 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 
     93def 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. 
    6698    """ 
    6799    multishell_parameters = getIterParams(parameters) 
     
    72104    else: 
    73105        params = parameters.iq_parameters 
    74     item = [] 
     106 
     107    rows = [] 
    75108    for param in params: 
    76109        # don't include shell parameters 
    77110        if param.name == multishell_param_name: 
    78111            continue 
     112 
    79113        # Modify parameter name from <param>[n] to <param>1 
    80114        item_name = param.name 
    81115        if param in multishell_parameters: 
    82116            continue 
    83         #    item_name = replaceShellName(param.name, 1) 
    84117 
    85118        item1 = QtGui.QStandardItem(item_name) 
    86119        item1.setCheckable(True) 
    87120        item1.setEditable(False) 
    88         # item_err = QtGui.QStandardItem() 
     121 
    89122        # check for polydisp params 
    90123        if param.polydisperse: 
     
    93126            item1_1 = QtGui.QStandardItem("Distribution") 
    94127            item1_1.setEditable(False) 
     128 
    95129            # Find param in volume_params 
    96130            for p in parameters.form_volume_parameters: 
     
    99133                width = kernel_module.getParam(p.name+'.width') 
    100134                ptype = kernel_module.getParam(p.name+'.type') 
    101  
    102135                item1_2 = QtGui.QStandardItem(str(width)) 
    103136                item1_2.setEditable(False) 
     
    110143                poly_item.appendRow([item1_1, item1_2, item1_3, item1_4, item1_5]) 
    111144                break 
     145 
    112146            # Add the polydisp item as a child 
    113147            item1.appendRow([poly_item]) 
     148 
    114149        # Param values 
    115150        item2 = QtGui.QStandardItem(str(param.default)) 
    116         # TODO: the error column. 
    117         # Either add a proxy model or a custom view delegate 
    118         #item_err = QtGui.QStandardItem() 
    119151        item3 = QtGui.QStandardItem(str(param.limits[0])) 
    120152        item4 = QtGui.QStandardItem(str(param.limits[1])) 
    121         item5 = QtGui.QStandardItem(param.units) 
     153        item5 = QtGui.QStandardItem(str(param.units)) 
    122154        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 
     169def 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) 
    129177    """ 
    130178    if is2D: 
     
    132180    else: 
    133181        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): 
    136197        # Create the top level, checkable item 
    137         item_name = param.name 
     198        item_name = param_orig.name 
    138199        item1 = QtGui.QStandardItem(item_name) 
     200        item1.setData(param.name, QtCore.Qt.UserRole) 
    139201        item1.setCheckable(True) 
    140202        item1.setEditable(False) 
     203 
    141204        # Param values 
    142205        # TODO: add delegate for validation of cells 
    143206        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 
    150230 
    151231def markParameterDisabled(model, row): 
     
    182262    model.appendRow(item_list) 
    183263 
     264def 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 
    184280def addHeadersToModel(model): 
    185281    """ 
     
    227323    model.header_tooltips = copy.copy(poly_header_error_tooltips) 
    228324 
    229 def addShellsToModel(parameters, model, index): 
    230     """ 
    231     Find out multishell parameters and update the model with the requested number of them 
     325def 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. 
    232331    """ 
    233332    multishell_parameters = getIterParams(parameters) 
    234333 
     334    rows = [] 
    235335    for i in range(index): 
    236336        for par in multishell_parameters: 
     
    250350                    item1_3 = QtGui.QStandardItem(str(p.limits[0])) 
    251351                    item1_4 = QtGui.QStandardItem(str(p.limits[1])) 
    252                     item1_5 = QtGui.QStandardItem(p.units) 
     352                    item1_5 = QtGui.QStandardItem(str(p.units)) 
    253353                    poly_item.appendRow([item1_1, item1_2, item1_3, item1_4, item1_5]) 
    254354                    break 
     
    258358            item3 = QtGui.QStandardItem(str(par.limits[0])) 
    259359            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 
    262381 
    263382def calculateChi2(reference_data, current_data): 
  • src/sas/qtgui/Perspectives/Fitting/FittingWidget.py

    rf84d793 r5e0891b  
    4848from sas.qtgui.Perspectives.Fitting.ReportPageLogic import ReportPageLogic 
    4949 
    50  
    5150TAB_MAGNETISM = 4 
    5251TAB_POLY = 3 
     
    9190    fittingFinishedSignal = QtCore.pyqtSignal(tuple) 
    9291    batchFittingFinishedSignal = QtCore.pyqtSignal(tuple) 
    93     Calc1DFinishedSignal = QtCore.pyqtSignal(tuple) 
    94     Calc2DFinishedSignal = QtCore.pyqtSignal(tuple) 
     92    Calc1DFinishedSignal = QtCore.pyqtSignal(dict) 
     93    Calc2DFinishedSignal = QtCore.pyqtSignal(dict) 
    9594 
    9695    def __init__(self, parent=None, data=None, tab_id=1): 
     
    188187 
    189188        # Overwrite data type descriptor 
     189 
    190190        self.is2D = True if isinstance(self.logic.data, Data2D) else False 
    191191 
     
    219219        # Utility variable to enable unselectable option in category combobox 
    220220        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 
    223224        # Dictionary of {model name: model class} for the current category 
    224225        self.models = {} 
     
    247248        # copy of current kernel model 
    248249        self.kernel_module_copy = None 
     250 
     251        # dictionaries of current params 
     252        self.poly_params = {} 
     253        self.magnet_params = {} 
    249254 
    250255        # Page id for fitting 
     
    558563        When clicked on white space: model description 
    559564        """ 
    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())] 
    561567        menu = self.showModelDescription() if not rows else self.modelContextMenu(rows) 
    562568        try: 
     
    672678        Return list of all parameters for the current model 
    673679        """ 
    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)] 
    675683 
    676684    def modifyViewOnRow(self, row, font=None, brush=None): 
     
    700708        assert isinstance(constraint, Constraint) 
    701709        assert 0 <= row <= self._model_model.rowCount() 
     710        assert self.isCheckable(row) 
    702711 
    703712        item = QtGui.QStandardItem() 
     
    720729        max_col = self.lstParams.itemDelegate().param_max 
    721730        for row in self.selectedParameters(): 
     731            assert(self.isCheckable(row)) 
    722732            param = self._model_model.item(row, 0).text() 
    723733            value = self._model_model.item(row, 1).text() 
     
    762772        max_col = self.lstParams.itemDelegate().param_max 
    763773        for row in range(self._model_model.rowCount()): 
     774            if not self.isCheckable(row): 
     775                continue 
    764776            if not self.rowHasConstraint(row): 
    765777                continue 
     
    788800    def getConstraintForRow(self, row): 
    789801        """ 
    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) 
    792807        try: 
    793             item = self._model_model.item(row, 1) 
    794808            return item.child(0).data() 
    795809        except AttributeError: 
    796             # return none when no constraints 
    797810            return None 
    798811 
     
    801814        Finds out if row of the main model has a constraint child 
    802815        """ 
     816        if not self.isCheckable(row): 
     817            return False 
    803818        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 
    808824        return False 
    809825 
     
    812828        Finds out if row of the main model has an active constraint child 
    813829        """ 
     830        if not self.isCheckable(row): 
     831            return False 
    814832        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 
    819838        return False 
    820839 
     
    823842        Finds out if row of the main model has an active, nontrivial constraint child 
    824843        """ 
     844        if not self.isCheckable(row): 
     845            return False 
    825846        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 
    830852        return False 
    831853 
     
    10371059            # Show constraint, if present 
    10381060            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) 
    10431066 
    10441067    def replaceConstraintName(self, old_name, new_name=""): 
     
    11861209            # Update the sasmodel 
    11871210            # 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 
    11891214 
    11901215            # Update plot 
     
    11951220            row = self.getRowFromName(parameter_name) 
    11961221            param_item = self._model_model.item(row) 
     1222            self._model_model.blockSignals(True) 
    11971223            param_item.child(0).child(0, model_column).setText(item.text()) 
     1224            self._model_model.blockSignals(False) 
    11981225 
    11991226    def onMagnetModelChange(self, item): 
     
    12241251            # Unparsable field 
    12251252            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 
    12371270            self.recalculatePlotData() 
    12381271 
     
    14811514        # update charts 
    14821515        self.onPlot() 
     1516        #self.recalculatePlotData() 
     1517 
    14831518 
    14841519        # Read only value - we can get away by just printing it here 
     
    14951530        # Data going in 
    14961531        data = self.logic.data 
    1497         model = self.kernel_module 
     1532        model = copy.deepcopy(self.kernel_module) 
    14981533        qmin = self.q_range_min 
    14991534        qmax = self.q_range_max 
     1535        # add polydisperse/magnet parameters if asked 
     1536        self.updateKernelModelWithExtraParams(model) 
    15001537 
    15011538        params_to_fit = self.main_params_to_fit 
     
    15611598            # internal so can use closure for param_dict 
    15621599            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()): 
    15641601                return 
    15651602            # modify the param value 
    15661603            param_repr = GuiUtils.formatNumber(param_dict[param_name][0], high=True) 
    15671604            self._model_model.item(row, 1).setText(param_repr) 
     1605            self.kernel_module.setParam(param_name, param_dict[param_name][0]) 
    15681606            if self.has_error_column: 
    15691607                error_repr = GuiUtils.formatNumber(param_dict[param_name][1], high=True) 
     
    15731611            # Utility function for updateof polydispersity part of the main model 
    15741612            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()): 
    15761614                return 
    15771615            # modify the param value 
     
    16071645            poly_item.insertColumn(2, [QtGui.QStandardItem("")]) 
    16081646 
    1609         # block signals temporarily, so we don't end up 
    1610         # updating charts with every single model change on the end of fitting 
    1611         self._model_model.blockSignals(True) 
    1612  
    16131647        if not self.has_error_column: 
    16141648            # create top-level error column 
     
    16171651            self.iterateOverModel(createErrorColumn) 
    16181652 
    1619             # we need to enable signals for this, otherwise the final column mysteriously disappears (don't ask, I don't 
    1620             # know) 
    1621             self._model_model.blockSignals(False) 
    16221653            self._model_model.insertColumn(2, error_column) 
    1623             self._model_model.blockSignals(True) 
    16241654 
    16251655            FittingUtilities.addErrorHeadersToModel(self._model_model) 
     
    16301660            self.has_error_column = True 
    16311661 
     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() 
    16321665        self.iterateOverModel(updateFittedValues) 
    16331666        self.iterateOverModel(updatePolyValues) 
    1634  
    1635         self._model_model.blockSignals(False) 
     1667        self._model_model.itemChanged.connect(self.onMainParamsChange) 
    16361668 
    16371669        # Adjust the table cells width. 
     
    16681700            param_repr = GuiUtils.formatNumber(param_dict[param_name][0], high=True) 
    16691701            self._poly_model.item(row_i, 1).setText(param_repr) 
     1702            self.kernel_module.setParam(param_name, param_dict[param_name][0]) 
    16701703            if self.has_poly_error_column: 
    16711704                error_repr = GuiUtils.formatNumber(param_dict[param_name][1], high=True) 
    16721705                self._poly_model.item(row_i, 2).setText(error_repr) 
    1673  
    16741706 
    16751707        def createErrorColumn(row_i): 
     
    16921724        # block signals temporarily, so we don't end up 
    16931725        # updating charts with every single model change on the end of fitting 
    1694         self._poly_model.blockSignals(True) 
     1726        self._poly_model.itemChanged.disconnect() 
    16951727        self.iterateOverPolyModel(updateFittedValues) 
    1696         self._poly_model.blockSignals(False) 
     1728        self._poly_model.itemChanged.connect(self.onPolyModelChange) 
    16971729 
    16981730        if self.has_poly_error_column: 
     
    17041736 
    17051737        # switch off reponse to model change 
    1706         self._poly_model.blockSignals(True) 
    17071738        self._poly_model.insertColumn(2, error_column) 
    1708         self._poly_model.blockSignals(False) 
    17091739        FittingUtilities.addErrorPolyHeadersToModel(self._poly_model) 
    17101740 
     
    17391769            param_repr = GuiUtils.formatNumber(param_dict[param_name][0], high=True) 
    17401770            self._magnet_model.item(row, 1).setText(param_repr) 
     1771            self.kernel_module.setParam(param_name, param_dict[param_name][0]) 
    17411772            if self.has_magnet_error_column: 
    17421773                error_repr = GuiUtils.formatNumber(param_dict[param_name][1], high=True) 
     
    17581789        # block signals temporarily, so we don't end up 
    17591790        # updating charts with every single model change on the end of fitting 
    1760         self._magnet_model.blockSignals(True) 
     1791        self._magnet_model.itemChanged.disconnect() 
    17611792        self.iterateOverMagnetModel(updateFittedValues) 
    1762         self._magnet_model.blockSignals(False) 
     1793        self._magnet_model.itemChanged.connect(self.onMagnetModelChange) 
    17631794 
    17641795        if self.has_magnet_error_column: 
     
    17701801 
    17711802        # switch off reponse to model change 
    1772         self._magnet_model.blockSignals(True) 
    17731803        self._magnet_model.insertColumn(2, error_column) 
    1774         self._magnet_model.blockSignals(False) 
    17751804        FittingUtilities.addErrorHeadersToModel(self._magnet_model) 
    17761805 
     
    17841813        self.cmdPlot.setText("Show Plot") 
    17851814        # 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() 
    17861820        self.recalculatePlotData() 
    1787         self.showPlot() 
    17881821 
    17891822    def onSmearingOptionsUpdate(self): 
     
    19501983        # Crete/overwrite model items 
    19511984        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: 
    19551997            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 
    19642000            # Allow the SF combobox visibility for the given sasmodel 
    19652001            self.enableStructureFactorControl(structure_factor) 
     2002         
     2003            # Add S(Q) 
    19662004            if self.cbStructureFactor.isEnabled(): 
    19672005                structure_factor = self.cbStructureFactor.currentText() 
    19682006                self.fromStructureFactorToQModel(structure_factor) 
    19692007 
    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() 
    19792014 
    19802015        # Adjust the table cells width 
     
    20492084        self.shell_names = self.shellNamesList() 
    20502085 
     2086        # Add heading row 
     2087        FittingUtilities.addHeadingRowToModel(self._model_model, model_name) 
     2088 
    20512089        # 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 display 
    2057         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) 
    20582096 
    20592097    def fromStructureFactorToQModel(self, structure_factor): 
     
    20632101        if structure_factor is None or structure_factor=="None": 
    20642102            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) 
    20832170 
    20842171    def haveParamsToFit(self): 
     
    21062193        model_row = item.row() 
    21072194        name_index = self._model_model.index(model_row, 0) 
     2195        name_item = self._model_model.itemFromIndex(name_index) 
    21082196 
    21092197        # Extract changed value. 
     
    21142202            return 
    21152203 
    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)) 
    21172209 
    21182210        # Update the parameter value - note: this supports +/-inf as well 
     
    22372329        return self.completed1D if isinstance(self.data, Data1D) else self.completed2D 
    22382330 
     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 
    22392348    def calculateQGridForModelExt(self, data=None, model=None, completefn=None, use_threads=True): 
    22402349        """ 
     
    22442353            data = self.data 
    22452354        if model is None: 
    2246             model = self.kernel_module 
     2355            model = copy.deepcopy(self.kernel_module) 
     2356            self.updateKernelModelWithExtraParams(model) 
     2357 
    22472358        if completefn is None: 
    22482359            completefn = self.methodCompleteForData() 
     
    23172428 
    23182429        # 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) 
    23302449 
    23312450        # Update/generate plots 
    23322451        for plot in new_plots: 
    23332452            self.communicate.plotUpdateSignal.emit([plot]) 
    2334  
    2335     def complete2D(self, return_data): 
    2336         """ 
    2337         Plot the current 2D data 
    2338         """ 
    2339         fitted_data = self.logic.new2DPlot(return_data) 
    2340         self.calculateResiduals(fitted_data) 
    2341         self.model_data = fitted_data 
    23422453 
    23432454    def calculateResiduals(self, fitted_data): 
     
    24702581        _, min, max = self.kernel_module.details[param_name] 
    24712582 
     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 
    24722588        # Construct a row with polydisp. related variable. 
    24732589        # This will get added to the polydisp. model 
     
    25172633        def updateFunctionCaption(row): 
    25182634            # 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) 
    25192638            param_name = str(self._model_model.item(row, 0).text()) 
     2639            self._model_model.blockSignals(False) 
    25202640            if param_name !=  param.name: 
    25212641                return 
    25222642            # Modify the param value 
     2643            self._model_model.blockSignals(True) 
    25232644            if self.has_error_column: 
    25242645                # err column changes the indexing 
     
    25262647            else: 
    25272648                self._model_model.item(row, 0).child(0).child(0,4).setText(combo_string) 
     2649            self._model_model.blockSignals(False) 
    25282650 
    25292651        if combo_string == 'array': 
     
    26442766                        param.units] 
    26452767 
     2768        self.magnet_params[param.name] = param.default 
     2769 
    26462770        FittingUtilities.addCheckedListToModel(model, checked_list) 
    26472771 
     
    26832807 
    26842808        self.lstParams.setIndexWidget(shell_index, func) 
    2685         self._last_model_row = self._model_model.rowCount() 
     2809        self._n_shells_row = shell_row - 1 
    26862810 
    26872811        # Set the index to the state-kept value 
     
    26942818        """ 
    26952819        # Find row location of the combobox 
    2696         last_row = self._last_model_row 
    2697         remove_rows = self._model_model.rowCount() - last_row 
     2820        first_row = self._n_shells_row + 1 
     2821        remove_rows = self._num_shell_params 
    26982822 
    26992823        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) 
    27032834        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)) 
    27042838 
    27052839        # Update relevant models 
     
    30463180            formatted_output = FittingUtilities.formatParameters(param_list) 
    30473181        elif format == "Excel": 
    3048             formatted_output = FittingUtilities.formatParametersExcel(param_list) 
     3182            formatted_output = FittingUtilities.formatParametersExcel(param_list[1:]) 
    30493183        elif format == "Latex": 
    3050             formatted_output = FittingUtilities.formatParametersLatex(param_list) 
     3184            formatted_output = FittingUtilities.formatParametersLatex(param_list[1:]) 
    30513185        else: 
    30523186            raise AttributeError("Bad format specifier.") 
     
    32133347        self._poly_model.blockSignals(False) 
    32143348 
     3349 
     3350 
  • src/sas/qtgui/Perspectives/Fitting/ModelThread.py

    r2df558e r5181e9b  
    101101        elapsed = time.time() - self.starttime 
    102102 
     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 
    103111        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) 
    135115 
    136116class Calc1D(CalcThread): 
     
    184164        index = (self.qmin <= self.data.x) & (self.data.x <= self.qmax) 
    185165 
     166        intermediate_results = None 
     167 
    186168        # If we use a smearer, also return the unsmeared model 
    187169        unsmeared_output = None 
     
    194176            mask = self.data.x[first_bin:last_bin+1] 
    195177            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 
    197185            output = self.smearer(unsmeared_output, first_bin, last_bin) 
    198186 
     
    213201                unsmeared_error=unsmeared_error 
    214202        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 = {} 
    235242 
    236243        elapsed = time.time() - self.starttime 
    237244 
     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 
    238254        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) 
    264258 
    265259    def results(self): 
  • src/sas/qtgui/Perspectives/Fitting/UI/OptionsWidgetUI.ui

    r79bd268 r309fa1b  
    3232        <item row="0" column="1"> 
    3333         <widget class="QLineEdit" name="txtMinRange"> 
     34          <property name="minimumSize"> 
     35           <size> 
     36            <width>80</width> 
     37            <height>0</height> 
     38           </size> 
     39          </property> 
    3440          <property name="toolTip"> 
    3541           <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Minimum value of Q.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> 
     
    5460        <item row="1" column="1"> 
    5561         <widget class="QLineEdit" name="txtMaxRange"> 
     62          <property name="minimumSize"> 
     63           <size> 
     64            <width>80</width> 
     65            <height>0</height> 
     66           </size> 
     67          </property> 
    5668          <property name="toolTip"> 
    5769           <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Maximum value of Q.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> 
  • src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingLogicTest.py

    re752ab8 rbfb5d9e  
    9999        data.name = "boop" 
    100100        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) 
    106111 
    107112        new_plot = self.logic.new1DPlot(return_data=return_data, tab_id=0) 
     
    139144        qmin, qmax, npts = self.logic.computeDataRange() 
    140145 
    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) 
    144154 
    145155        new_plot = self.logic.new2DPlot(return_data=return_data) 
  • src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingOptionsTest.py

    r725d9c06 rbfb5d9e  
    3838        # The combo box 
    3939        self.assertIsInstance(self.widget.cbAlgorithm, QtWidgets.QComboBox) 
    40         self.assertEqual(self.widget.cbAlgorithm.count(), 5) 
     40        self.assertEqual(self.widget.cbAlgorithm.count(), 6) 
    4141        self.assertEqual(self.widget.cbAlgorithm.itemText(0), 'Nelder-Mead Simplex') 
    4242        self.assertEqual(self.widget.cbAlgorithm.itemText(4), 'Levenberg-Marquardt') 
  • src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingWidgetTest.py

    r605d944 r4ea8020  
    256256        self.widget.cbStructureFactor.setCurrentIndex(structure_index) 
    257257 
    258         # We have 4 more rows now 
     258        # We have 3 more param rows now (radius_effective is removed), and a new heading 
    259259        self.assertEqual(self.widget._model_model.rowCount(), rowcount+4) 
    260260 
     
    276276        last_index = self.widget.cbStructureFactor.count() 
    277277        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) 
    280280 
    281281        # Are the command buttons properly enabled? 
     
    445445        self.assertEqual(self.widget.kernel_module.details['radius_bell'][1], 1.0) 
    446446 
     447        #self.widget.show() 
     448        #QtWidgets.QApplication.exec_() 
     449 
    447450        # 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) 
    449452        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) 
    451454        # try something stupid 
    452455        self.widget._poly_model.item(0,4).setText("butt") 
    453456        # 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) 
    455458 
    456459        # 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) 
    458461        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) 
    460463        # try something stupid again 
    461464        self.widget._poly_model.item(0,4).setText("beer") 
    462465        # 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) 
    464467 
    465468    def testOnPolyComboIndexChange(self): 
     
    482485        self.widget.onPolyComboIndexChange('rectangle', 0) 
    483486        # 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) 
    486489        # Change the index 
    487490        self.widget.onPolyComboIndexChange('lognormal', 0) 
    488491        # 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) 
    491494        # Change the index 
    492495        self.widget.onPolyComboIndexChange('schulz', 0) 
    493496        # 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) 
    496499 
    497500        # mock up file load 
     
    506509        Test opening of the load file dialog for 'array' polydisp. function 
    507510        """ 
     511 
     512        # open a non-existent file 
    508513        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) 
    509516        QtWidgets.QFileDialog.getOpenFileName = MagicMock(return_value=(filename,'')) 
    510517        self.widget.show() 
     
    522529 
    523530        # 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 
    524534        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") 
    525539        QtWidgets.QFileDialog.getOpenFileName = MagicMock(return_value=(filename,'')) 
    526540 
     
    588602 
    589603        # Assure we have the combobox available 
    590         last_row = self.widget._last_model_row 
    591         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) 
    592606        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() 
    593610 
    594611        # Change the combo box index 
     
    10241041 
    10251042         # Check the model 
    1026         self.assertEqual(self.widget._model_model.rowCount(), 6) 
     1043        self.assertEqual(self.widget._model_model.rowCount(), 7) 
    10271044        self.assertEqual(self.widget._model_model.columnCount(), 5) 
    10281045 
     
    11401157        # two rows selected 
    11411158        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()) 
    11431160        selection_model = self.widget.lstParams.selectionModel() 
    11441161        selection_model.select(index1, selection_model.Select | selection_model.Rows) 
     
    11761193        # several random parameters 
    11771194        self.assertEqual(self.widget.getRowFromName('scale'), 0) 
    1178         self.assertEqual(self.widget.getRowFromName('length'), 5) 
     1195        self.assertEqual(self.widget.getRowFromName('length'), 6) 
    11791196 
    11801197    def testGetParamNames(self): 
     
    12131230        # Create a constraint object 
    12141231        const = Constraint(parent=None, value=7.0) 
    1215         row = 2 
     1232        row = 3 
    12161233 
    12171234        spy = QtSignalSpy(self.widget, self.widget.constraintAddedSignal) 
     
    12321249        # assign complex constraint now 
    12331250        const = Constraint(parent=None, param='radius', func='5*sld') 
    1234         row = 4 
     1251        row = 5 
    12351252        # call the method tested 
    12361253        self.widget.addConstraintToRow(constraint=const, row=row) 
     
    12911308        self.widget.cbModel.setCurrentIndex(model_index) 
    12921309 
     1310        row1 = 1 
     1311        row2 = 5 
     1312 
     1313        param1 = "background" 
     1314        param2 = "radius" 
     1315 
     1316        #default_value1 = "0.001" 
     1317        default_value2 = "20" 
     1318 
    12931319        # select two rows 
    1294         row1 = 1 
    1295         row2 = 4 
    12961320        index1 = self.widget.lstParams.model().index(row1, 0, QtCore.QModelIndex()) 
    12971321        index2 = self.widget.lstParams.model().index(row2, 0, QtCore.QModelIndex()) 
     
    13101334 
    13111335        # delete one of the constraints 
    1312         self.widget.deleteConstraintOnParameter(param='background') 
     1336        self.widget.deleteConstraintOnParameter(param=param1) 
    13131337 
    13141338        # see that the other constraint is still present 
    1315         cons = self.widget.getConstraintForRow(4) # 4 = radius 
    1316         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) 
    13181342 
    13191343        # kill the other constraint 
     
    13211345 
    13221346        # see that the other constraint is still present 
    1323         self.assertEqual(self.widget.getConstraintsForModel(), [('radius', None)]) 
     1347        self.assertEqual(self.widget.getConstraintsForModel(), [(param2, None)]) 
    13241348 
    13251349    def testGetConstraintForRow(self): 
     
    13411365        self.widget.cbModel.setCurrentIndex(model_index) 
    13421366 
     1367        row1 = 1 
     1368        row2 = 5 
     1369 
    13431370        # select two rows 
    1344         row1 = 1 
    1345         row2 = 4 
    13461371        index1 = self.widget.lstParams.model().index(row1, 0, QtCore.QModelIndex()) 
    13471372        index2 = self.widget.lstParams.model().index(row2, 0, QtCore.QModelIndex()) 
     
    13531378        self.widget.addSimpleConstraint() 
    13541379 
    1355         con_list = [False, True, False, False, True, False] 
     1380        con_list = [False, True, False, False, False, True, False] 
    13561381        new_list = [] 
    13571382        for row in range(self.widget._model_model.rowCount()): 
     
    13711396        self.widget.cbModel.setCurrentIndex(model_index) 
    13721397 
     1398        row1 = 1 
     1399        row2 = 5 
     1400 
    13731401        # select two rows 
    1374         row1 = 1 
    1375         row2 = 4 
    13761402        index1 = self.widget.lstParams.model().index(row1, 0, QtCore.QModelIndex()) 
    13771403        index2 = self.widget.lstParams.model().index(row2, 0, QtCore.QModelIndex()) 
     
    13871413        constraint_objects[0].active = False 
    13881414 
    1389         con_list = [False, False, False, False, True, False] 
     1415        con_list = [False, False, False, False, False, True, False] 
    13901416        new_list = [] 
    13911417        for row in range(self.widget._model_model.rowCount()): 
     
    14081434        self.assertEqual(self.widget.getConstraintsForModel(),[]) 
    14091435 
     1436        row1 = 1 
     1437        row2 = 5 
     1438 
     1439        param1 = "background" 
     1440        param2 = "radius" 
     1441 
     1442        default_value1 = "0.001" 
     1443        default_value2 = "20" 
     1444 
    14101445        # select two rows 
    1411         row1 = 1 
    1412         row2 = 4 
    14131446        index1 = self.widget.lstParams.model().index(row1, 0, QtCore.QModelIndex()) 
    14141447        index2 = self.widget.lstParams.model().index(row2, 0, QtCore.QModelIndex()) 
     
    14221455        # simple constraints 
    14231456        # self.assertEqual(self.widget.getConstraintsForModel(), [('background', '0.001'), ('radius', '20')]) 
    1424         cons = self.widget.getConstraintForRow(1) # 1 - background 
    1425         self.assertEqual(cons.param, "background") 
    1426         self.assertEqual(cons.value, "0.001") 
    1427         cons = self.widget.getConstraintForRow(4) # 4 = radius 
    1428         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) 
    14301463 
    14311464        objects = self.widget.getConstraintObjectsForModel() 
    14321465        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" 
    14351472 
    14361473        # 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) 
    14391475        self.widget.addConstraintToRow(constraint=const, row=row) 
    14401476        #self.assertEqual(self.widget.getConstraintsForModel(),[('scale', '5*sld'), ('background', '0.001'), ('radius', None)]) 
    1441         cons = self.widget.getConstraintForRow(4) # 4 = radius 
    1442         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) 
    14441480 
    14451481        objects = self.widget.getConstraintObjectsForModel() 
    14461482        self.assertEqual(len(objects), 3) 
    1447         self.assertEqual(objects[0].func, '5*sld') 
     1483        self.assertEqual(objects[0].func, func) 
    14481484 
    14491485    def testReplaceConstraintName(self): 
Note: See TracChangeset for help on using the changeset viewer.