Changeset 21e71f1 in sasview for src/sas/qtgui/Perspectives/Fitting


Ignore:
Timestamp:
Nov 21, 2018 6:39:24 AM (5 years ago)
Author:
Piotr Rozyczko <piotr.rozyczko@…>
Branches:
ESS_GUI, ESS_GUI_batch_fitting, ESS_GUI_bumps_abstraction, ESS_GUI_iss1116, ESS_GUI_opencl, ESS_GUI_ordering, ESS_GUI_sync_sascalc
Children:
f2e199e
Parents:
44c15fc (diff), fb39f28 (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.
Message:

Merge branch 'ESS_GUI_project_save' into ESS_GUI

Location:
src/sas/qtgui/Perspectives/Fitting
Files:
14 edited

Legend:

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

    r305114c rd72ac57  
    1515from sas.qtgui.Perspectives.Fitting import FittingUtilities 
    1616import sas.qtgui.Utilities.GuiUtils as GuiUtils 
    17 ALLOWED_OPERATORS = ['=','<','>','>=','<='] 
     17from sas.qtgui.Perspectives.Fitting.Constraint import Constraint 
     18 
     19#ALLOWED_OPERATORS = ['=','<','>','>=','<='] 
     20ALLOWED_OPERATORS = ['='] 
    1821 
    1922# Local UI 
     
    2124 
    2225class ComplexConstraint(QtWidgets.QDialog, Ui_ComplexConstraintUI): 
     26    constraintReadySignal = QtCore.pyqtSignal(tuple) 
    2327    def __init__(self, parent=None, tabs=None): 
    2428        super(ComplexConstraint, self).__init__() 
     
    3236        self.tab_names = None 
    3337        self.operator = '=' 
     38        self._constraint = Constraint() 
    3439 
    3540        self.warning = self.lblWarning.text() 
     
    5358        Signals from various elements 
    5459        """ 
    55         self.cmdOK.clicked.connect(self.accept) 
     60        self.cmdOK.clicked.connect(self.onApply) 
    5661        self.cmdHelp.clicked.connect(self.onHelp) 
    5762        self.cmdRevert.clicked.connect(self.onRevert) 
     
    6974        self.txtName2.setText(self.tab_names[1]) 
    7075 
    71         # Show only parameters not already constrained 
     76        self.setupParamWidgets() 
     77 
     78        # Add menu to the Apply button 
     79        all_menu   = QtWidgets.QMenu() 
     80        self.actionAddAll = QtWidgets.QAction(self) 
     81        self.actionAddAll.setObjectName("actionAddAll") 
     82        self.actionAddAll.setText(QtCore.QCoreApplication.translate("self", "Add all")) 
     83        ttip = "Add constraints between all identically named parameters in both fitpages" 
     84        self.actionAddAll.setToolTip(ttip) 
     85        self.actionAddAll.triggered.connect(self.onSetAll) 
     86        all_menu.addAction(self.actionAddAll) 
     87        # https://bugreports.qt.io/browse/QTBUG-13663 
     88        all_menu.setToolTipsVisible(True) 
     89        self.cmdOK.setMenu(all_menu) 
     90 
     91    def setupParamWidgets(self): 
     92        """ 
     93        Fill out comboboxes and set labels with non-constrained parameters 
     94        """ 
    7295        self.cbParam1.clear() 
    73         items = [param for i,param in enumerate(self.params[0]) if not self.tabs[0].rowHasConstraint(i)] 
    74         self.cbParam1.addItems(items) 
     96        items1 = [param for param in self.params[0] if not self.tabs[0].paramHasConstraint(param)] 
     97        self.cbParam1.addItems(items1) 
     98 
     99        # M2 doesn't have to be non-constrained 
    75100        self.cbParam2.clear() 
    76         items = [param for i,param in enumerate(self.params[1]) if not self.tabs[1].rowHasConstraint(i)] 
    77         self.cbParam2.addItems(items) 
     101        #items2 = [param for param in self.params[1] if not self.tabs[1].paramHasConstraint(param)] 
     102        items2 = [param for param in self.params[1]] 
     103        self.cbParam2.addItems(items2) 
    78104 
    79105        self.txtParam.setText(self.tab_names[0] + ":" + self.cbParam1.currentText()) 
     
    84110 
    85111        self.txtConstraint.setText(self.tab_names[1]+"."+self.cbParam2.currentText()) 
     112 
     113        # disable Apply if no parameters available 
     114        if len(items1)==0: 
     115            self.cmdOK.setEnabled(False) 
     116            txt = "No parameters in model "+self.tab_names[0] +\ 
     117                " are available for constraining." 
     118            self.lblWarning.setText(txt) 
     119        else: 
     120            self.cmdOK.setEnabled(True) 
     121            txt = "" 
     122            self.lblWarning.setText(txt) 
    86123 
    87124    def setupTooltip(self): 
     
    144181 
    145182        # Original indices 
     183        index2 = index2 if index2 >= 0 else 0 
     184        index1 = index1 if index1 >= 0 else 0 
    146185        self.cbParam1.setCurrentIndex(index2) 
    147186        self.cbParam2.setCurrentIndex(index1) 
     
    205244    def constraint(self): 
    206245        """ 
    207         Return the generated constraint as tuple (model1, param1, operator, constraint) 
    208         """ 
    209         return (self.txtName1.text(), self.cbParam1.currentText(), self.cbOperator.currentText(), self.txtConstraint.text()) 
     246        Return the generated constraint 
     247        """ 
     248        param = self.cbParam1.currentText() 
     249        value = self.cbParam2.currentText() 
     250        func = self.txtConstraint.text() 
     251        value_ex = self.txtName2.text() + "." + self.cbParam2.currentText() 
     252        model1 = self.txtName1.text() 
     253        operator = self.cbOperator.currentText() 
     254 
     255        con = Constraint(self, 
     256                         param=param, 
     257                         value=value, 
     258                         func=func, 
     259                         value_ex=value_ex, 
     260                         operator=operator) 
     261 
     262        return (model1, con) 
     263 
     264    def onApply(self): 
     265        """ 
     266        Respond to Add constraint action. 
     267        Send a signal that the constraint is ready to be applied 
     268        """ 
     269        cons_tuple = self.constraint() 
     270        self.constraintReadySignal.emit(cons_tuple) 
     271        # reload the comboboxes 
     272        self.setupParamWidgets() 
     273 
     274    def onSetAll(self): 
     275        """ 
     276        Set constraints on all identically named parameters between two fitpages 
     277        """ 
     278        # loop over parameters in constrained model 
     279        items1 = [param for param in self.params[0] if not self.tabs[0].paramHasConstraint(param)] 
     280        #items2 = [param for param in self.params[1] if not self.tabs[1].paramHasConstraint(i)] 
     281        items2 = self.params[1] 
     282        for item in items1: 
     283            if item not in items2: continue 
     284            param = item 
     285            value = item 
     286            func = self.txtName2.text() + "." + param 
     287            value_ex = self.txtName1.text() + "." + param 
     288            model1 = self.txtName1.text() 
     289            operator = self.cbOperator.currentText() 
     290 
     291            con = Constraint(self, 
     292                             param=param, 
     293                             value=value, 
     294                             func=func, 
     295                             value_ex=value_ex, 
     296                             operator=operator) 
     297 
     298            self.constraintReadySignal.emit((model1, con)) 
     299 
     300        # reload the comboboxes 
     301        self.setupParamWidgets() 
    210302 
    211303    def onHelp(self): 
  • src/sas/qtgui/Perspectives/Fitting/Constraint.py

    r14ec91c5 r09e0c32  
    55    hence made into a class. 
    66    """ 
    7     def __init__(self, parent=None, param=None, value=0.0, min=None, max=None, func=None): 
     7    def __init__(self, parent=None, param=None, value=0.0, 
     8                 min=None, max=None, func=None, value_ex=None, 
     9                 operator="="): 
    810        self._value = value 
    911        self._param = param 
     12        self._value_ex = value_ex 
    1013        self._func = func 
    11         self.active = True 
    1214        self._min = min 
    1315        self._max = max 
     16        self._operator = operator 
     17        self.validate = True 
     18        self.active = True 
    1419 
    1520    @property 
    1621    def value(self): 
     22        # value/parameter to fit to (e.g. 1.0 or sld) 
    1723        return self._value 
    1824 
     
    2228 
    2329    @property 
     30    def value_ex(self): 
     31        # full parameter name to fit to (e.g. M1.sld) 
     32        return self._value_ex 
     33 
     34    @value_ex.setter 
     35    def value_ex(self, val): 
     36        self._value_ex = val 
     37 
     38    @property 
    2439    def param(self): 
     40        # parameter which is being fitted 
    2541        return self._param 
    2642 
     
    3147    @property 
    3248    def func(self): 
     49        # Function to be used for constraint 
     50        # e.g. sqrt(M1.sld+1.0) 
    3351        return self._func 
    3452 
     
    3957    @property 
    4058    def min(self): 
     59        # min param value for single value constraints 
    4160        return self._min 
    4261 
     
    4766    @property 
    4867    def max(self): 
     68        # max param value for single value constraints 
    4969        return self._max 
    5070 
     
    5373        self._max = val 
    5474 
     75    @property 
     76    def operator(self): 
     77        # operator to use for constraint 
     78        return self._operator 
     79 
     80    @operator.setter 
     81    def operator(self, val): 
     82        self._operator = val 
     83 
  • src/sas/qtgui/Perspectives/Fitting/ConstraintWidget.py

    rc4c4957 r21e71f1  
    2323    Constraints Dialog to select the desired parameter/model constraints. 
    2424    """ 
     25    fitCompleteSignal = QtCore.pyqtSignal(tuple) 
     26    batchCompleteSignal = QtCore.pyqtSignal(tuple) 
     27    fitFailedSignal = QtCore.pyqtSignal(tuple) 
    2528 
    2629    def __init__(self, parent=None): 
     
    3235        # To keep with previous SasView values, use 300 as the start offset 
    3336        self.page_id = 301 
     37        self.tab_id = self.page_id 
    3438 
    3539        # Are we chain fitting? 
     
    6064        Set up various widget states 
    6165        """ 
     66        # disable special cases until properly defined 
     67        self.label.setVisible(False) 
     68        self.cbCases.setVisible(False) 
     69 
    6270        labels = ['FitPage', 'Model', 'Data', 'Mnemonic'] 
    6371        # tab widget - headers 
     
    7987        self.tblConstraints.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch) 
    8088        self.tblConstraints.setEnabled(False) 
     89        header = self.tblConstraints.horizontalHeaderItem(0) 
     90        header.setToolTip("Double click a row below to edit the constraint.") 
    8191 
    8292        self.tblConstraints.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) 
     
    93103        self.cmdFit.clicked.connect(self.onFit) 
    94104        self.cmdHelp.clicked.connect(self.onHelp) 
     105        self.cmdAdd.clicked.connect(self.showMultiConstraint) 
    95106        self.chkChain.toggled.connect(self.onChainFit) 
    96107 
     
    100111        self.tblConstraints.cellChanged.connect(self.onConstraintChange) 
    101112 
     113        # Internal signals 
     114        self.fitCompleteSignal.connect(self.fitComplete) 
     115        self.batchCompleteSignal.connect(self.batchComplete) 
     116        self.fitFailedSignal.connect(self.fitFailed) 
     117 
    102118        # External signals 
    103119        self.parent.tabsModifiedSignal.connect(self.initializeFitList) 
     
    156172        fitter = Fit() 
    157173        fitter.fitter_id = self.page_id 
    158  
    159         # Notify the parent about fitting started 
    160         self.parent.fittingStartedSignal.emit(tabs_to_fit) 
    161174 
    162175        # prepare fitting problems for each tab 
     
    168181        try: 
    169182            for tab in tabs_to_fit: 
     183                if not self.isTabImportable(tab): continue 
    170184                tab_object = ObjectLibrary.getObject(tab) 
    171185                if tab_object is None: 
     
    177191        except ValueError: 
    178192            # No parameters selected in one of the tabs 
    179             no_params_msg = "Fitting can not be performed.\n" +\ 
     193            no_params_msg = "Fitting cannot be performed.\n" +\ 
    180194                            "Not all tabs chosen for fitting have parameters selected for fitting." 
    181195            QtWidgets.QMessageBox.warning(self, 
     
    200214        batch_inputs = {} 
    201215        batch_outputs = {} 
     216 
     217        # Notify the parent about fitting started 
     218        self.parent.fittingStartedSignal.emit(tabs_to_fit) 
    202219 
    203220        # new fit thread object 
     
    223240 
    224241        #disable the Fit button 
     242        self.cmdFit.setStyleSheet('QPushButton {color: red;}') 
    225243        self.cmdFit.setText('Running...') 
    226244        self.parent.communicate.statusBarUpdateSignal.emit('Fitting started...') 
     
    292310        """ 
    293311        item = self.tblConstraints.item(row, column) 
    294         if column == 0: 
    295             # Update the tabs for fitting list 
    296             constraint = self.available_constraints[row] 
    297             constraint.active = (item.checkState() == QtCore.Qt.Checked) 
     312        if column != 0: return 
     313        # Update the tabs for fitting list 
     314        constraint = self.available_constraints[row] 
     315        constraint.active = (item.checkState() == QtCore.Qt.Checked) 
     316        # Update the constraint formula 
     317        constraint = self.available_constraints[row] 
     318        function = item.text() 
     319        # remove anything left of '=' to get the constraint 
     320        function = function[function.index('=')+1:] 
     321        # No check on function here - trust the user (R) 
     322        if function != constraint.func: 
     323            # This becomes rather difficult to validate now. 
     324            # Turn off validation for Edit Constraint 
     325            constraint.func = function 
     326            constraint.validate = False 
    298327 
    299328    def onTabCellEntered(self, row, column): 
     
    308337    def onFitComplete(self, result): 
    309338        """ 
     339        Send the fit complete signal to main thread 
     340        """ 
     341        self.fitCompleteSignal.emit(result) 
     342 
     343    def fitComplete(self, result): 
     344        """ 
    310345        Respond to the successful fit complete signal 
    311346        """ 
    312347        #re-enable the Fit button 
     348        self.cmdFit.setStyleSheet('QPushButton {color: black;}') 
    313349        self.cmdFit.setText("Fit") 
    314350        self.cmdFit.setEnabled(True) 
     
    347383    def onBatchFitComplete(self, result): 
    348384        """ 
     385        Send the fit complete signal to main thread 
     386        """ 
     387        self.batchCompleteSignal.emit(result) 
     388 
     389    def batchComplete(self, result): 
     390        """ 
    349391        Respond to the successful batch fit complete signal 
    350392        """ 
    351393        #re-enable the Fit button 
     394        self.cmdFit.setStyleSheet('QPushButton {color: black;}') 
    352395        self.cmdFit.setText("Fit") 
    353396        self.cmdFit.setEnabled(True) 
     
    375418    def onFitFailed(self, reason): 
    376419        """ 
     420        Send the fit failed signal to main thread 
     421        """ 
     422        self.fitFailedSignal.emit(result) 
     423 
     424    def fitFailed(self, reason): 
     425        """ 
    377426        Respond to fitting failure. 
    378427        """ 
    379428        #re-enable the Fit button 
     429        self.cmdFit.setStyleSheet('QPushButton {color: black;}') 
    380430        self.cmdFit.setText("Fit") 
    381431        self.cmdFit.setEnabled(True) 
     
    386436        msg = "Fitting failed: %s s.\n" % reason 
    387437        self.parent.communicate.statusBarUpdateSignal.emit(msg) 
    388   
     438 
    389439    def isTabImportable(self, tab): 
    390440        """ 
     
    599649            # Show the text in the constraint table 
    600650            item = self.uneditableItem(label) 
     651            item = QtWidgets.QTableWidgetItem(label) 
    601652            item.setFlags(item.flags() ^ QtCore.Qt.ItemIsUserCheckable) 
    602653            item.setCheckState(QtCore.Qt.Checked) 
     
    667718        return None 
    668719 
     720    def onAcceptConstraint(self, con_tuple): 
     721        """ 
     722        Receive constraint tuple from the ComplexConstraint dialog and adds contraint 
     723        """ 
     724        #"M1, M2, M3" etc 
     725        model_name, constraint = con_tuple 
     726        constrained_tab = self.getObjectByName(model_name) 
     727        if constrained_tab is None: 
     728            return 
     729 
     730        # Find the constrained parameter row 
     731        constrained_row = constrained_tab.getRowFromName(constraint.param) 
     732 
     733        # Update the tab 
     734        constrained_tab.addConstraintToRow(constraint, constrained_row) 
     735 
     736        # Select this parameter for adjusting/fitting 
     737        constrained_tab.selectCheckbox(constrained_row) 
     738 
     739 
    669740    def showMultiConstraint(self): 
    670741        """ 
     
    672743        """ 
    673744        selected_rows = self.selectedParameters(self.tblTabList) 
    674         assert(len(selected_rows)==2) 
     745        if len(selected_rows)!=2: 
     746            msg = "Please select two fit pages from the Source Choice table." 
     747            msgbox = QtWidgets.QMessageBox(self.parent) 
     748            msgbox.setIcon(QtWidgets.QMessageBox.Warning) 
     749            msgbox.setText(msg) 
     750            msgbox.setWindowTitle("2 fit page constraints") 
     751            retval = msgbox.exec_() 
     752            return 
    675753 
    676754        tab_list = [ObjectLibrary.getObject(self.tblTabList.item(s, 0).data(0)) for s in selected_rows] 
    677755        # Create and display the widget for param1 and param2 
    678756        cc_widget = ComplexConstraint(self, tabs=tab_list) 
     757        cc_widget.constraintReadySignal.connect(self.onAcceptConstraint) 
     758 
    679759        if cc_widget.exec_() != QtWidgets.QDialog.Accepted: 
    680760            return 
    681761 
    682         constraint = Constraint() 
    683         model1, param1, operator, constraint_text = cc_widget.constraint() 
    684  
    685         constraint.func = constraint_text 
    686         # param1 is the parameter we're constraining 
    687         constraint.param = param1 
    688  
    689         # Find the right tab 
    690         constrained_tab = self.getObjectByName(model1) 
    691         if constrained_tab is None: 
    692             return 
    693  
    694         # Find the constrained parameter row 
    695         constrained_row = constrained_tab.getRowFromName(param1) 
    696  
    697         # Update the tab 
    698         constrained_tab.addConstraintToRow(constraint, constrained_row) 
     762    def getFitPage(self): 
     763        """ 
     764        Retrieves the state of this page 
     765        """ 
     766        param_list = [] 
     767 
     768        param_list.append(['is_constraint', 'True']) 
     769        param_list.append(['data_id', "cs_tab"+str(self.page_id)]) 
     770        param_list.append(['current_type', self.currentType]) 
     771        param_list.append(['is_chain_fitting', str(self.is_chain_fitting)]) 
     772        param_list.append(['special_case', self.cbCases.currentText()]) 
     773 
     774        return param_list 
     775 
     776    def getFitModel(self): 
     777        """ 
     778        Retrieves current model 
     779        """ 
     780        model_list = [] 
     781 
     782        checked_models = {} 
     783        for row in range(self.tblTabList.rowCount()): 
     784            model_name = self.tblTabList.item(row,1).data(0) 
     785            active = self.tblTabList.item(row,0).checkState()# == QtCore.Qt.Checked 
     786            checked_models[model_name] = str(active) 
     787 
     788        checked_constraints = {} 
     789        for row in range(self.tblConstraints.rowCount()): 
     790            model_name = self.tblConstraints.item(row,0).data(0) 
     791            active = self.tblConstraints.item(row,0).checkState()# == QtCore.Qt.Checked 
     792            checked_constraints[model_name] = str(active) 
     793 
     794        model_list.append(['checked_models', checked_models]) 
     795        model_list.append(['checked_constraints', checked_constraints]) 
     796        return model_list 
     797 
     798    def createPageForParameters(self, parameters=None): 
     799        """ 
     800        Update the page with passed parameter values 
     801        """ 
     802        # checked models 
     803        if not 'checked_models' in parameters: 
     804            return 
     805        models = parameters['checked_models'][0] 
     806        for model, check_state in models.items(): 
     807            for row in range(self.tblTabList.rowCount()): 
     808                model_name = self.tblTabList.item(row,1).data(0) 
     809                if model_name != model: 
     810                    continue 
     811                # check/uncheck item 
     812                self.tblTabList.item(row,0).setCheckState(int(check_state)) 
     813 
     814        if not 'checked_constraints' in parameters: 
     815            return 
     816        # checked constraints 
     817        models = parameters['checked_constraints'][0] 
     818        for model, check_state in models.items(): 
     819            for row in range(self.tblConstraints.rowCount()): 
     820                model_name = self.tblConstraints.item(row,0).data(0) 
     821                if model_name != model: 
     822                    continue 
     823                # check/uncheck item 
     824                self.tblConstraints.item(row,0).setCheckState(int(check_state)) 
     825 
     826        # fit/batch radio 
     827        isBatch = parameters['current_type'][0] == 'BatchPage' 
     828        if isBatch: 
     829            self.btnBatch.toggle() 
     830 
     831        # chain 
     832        is_chain = parameters['is_chain_fitting'][0] == 'True' 
     833        if isBatch: 
     834            self.chkChain.setChecked(is_chain) 
  • src/sas/qtgui/Perspectives/Fitting/FittingPerspective.py

    rd2007a8 rfb39f28  
    11import numpy 
     2import copy 
    23 
    34from PyQt5 import QtCore 
     
    1011import sas.qtgui.Utilities.LocalConfig as LocalConfig 
    1112import sas.qtgui.Utilities.ObjectLibrary as ObjectLibrary 
     13import sas.qtgui.Utilities.GuiUtils as GuiUtils 
    1214 
    1315from sas.qtgui.Perspectives.Fitting.FittingWidget import FittingWidget 
     
    3739        self.maxIndex = 1 
    3840 
    39         ## Index of the current tab 
    40         #self.currentTab = 0 
    41  
    4241        # The default optimizer 
    4342        self.optimizer = 'Levenberg-Marquardt' 
     
    8584        self.updateWindowTitle() 
    8685 
     86        # Add new tab mini-button 
     87        self.plusButton = QtWidgets.QToolButton(self) 
     88        self.plusButton.setText("+") 
     89        self.setCornerWidget(self.plusButton) 
     90        self.plusButton.setToolTip("Add a new Fit Page") 
     91        self.plusButton.clicked.connect(lambda: self.addFit(None)) 
     92 
    8793    def updateWindowTitle(self): 
    8894        """ 
     
    112118    def onLatexCopy(self): 
    113119        self.currentTab.onCopyToClipboard("Latex") 
     120 
     121    def serializeAllFitpage(self): 
     122        # serialize all active fitpages and return 
     123        # a dictionary: {data_id: fitpage_state} 
     124        params = {} 
     125        for i, tab in enumerate(self.tabs): 
     126            tab_data = self.getSerializedFitpage(tab) 
     127            if 'data_id' not in tab_data: continue 
     128            id = tab_data['data_id'][0] 
     129            if isinstance(id, list): 
     130                for i in id: 
     131                    if i in params: 
     132                        params[i].append(tab_data) 
     133                    else: 
     134                        params[i] = [tab_data] 
     135            else: 
     136                if id in params: 
     137                    params[id].append(tab_data) 
     138                else: 
     139                    params[id] = [tab_data] 
     140        return params 
     141 
     142    def serializeCurrentFitpage(self): 
     143        # serialize current(active) fitpage 
     144        return self.getSerializedFitpage(self.currentTab) 
     145 
     146    def getSerializedFitpage(self, tab): 
     147        """ 
     148        get serialize requested fit tab 
     149        """ 
     150        fitpage_state = tab.getFitPage() 
     151        fitpage_state += tab.getFitModel() 
     152        # put the text into dictionary 
     153        line_dict = {} 
     154        for line in fitpage_state: 
     155            #content = line.split(',') 
     156            if len(line) > 1: 
     157                line_dict[line[0]] = line[1:] 
     158        return line_dict 
     159 
     160    def currentTabDataId(self): 
     161        """ 
     162        Returns the data ID of the current tab 
     163        """ 
     164        tab_id = [] 
     165        if not self.currentTab.data: 
     166            return tab_id 
     167        for item in self.currentTab.all_data: 
     168            data = GuiUtils.dataFromItem(item) 
     169            tab_id.append(data.id) 
     170 
     171        return tab_id 
     172 
     173    def updateFromParameters(self, parameters): 
     174        """ 
     175        Pass the update parameters to the current fit page 
     176        """ 
     177        self.currentTab.createPageForParameters(parameters) 
    114178 
    115179    def closeEvent(self, event): 
     
    246310        for index_to_delete in index_list: 
    247311            index_to_delete_str = str(index_to_delete) 
    248             if index_to_delete_str in list(self.dataToFitTab.keys()): 
    249                 for tab_name in self.dataToFitTab[index_to_delete_str]: 
    250                     # delete tab #index after corresponding data got removed 
    251                     self.closeTabByName(tab_name) 
    252                 self.dataToFitTab.pop(index_to_delete_str) 
     312            orig_dict = copy.deepcopy(self.dataToFitTab) 
     313            for tab_key in orig_dict.keys(): 
     314                if index_to_delete_str in tab_key: 
     315                    for tab_name in orig_dict[tab_key]: 
     316                        self.closeTabByName(tab_name) 
     317                    self.dataToFitTab.pop(tab_key) 
    253318 
    254319    def allowBatch(self): 
    255320        """ 
    256321        Tell the caller that we accept multiple data instances 
     322        """ 
     323        return True 
     324 
     325    def isSerializable(self): 
     326        """ 
     327        Tell the caller that this perspective writes its state 
    257328        """ 
    258329        return True 
     
    337408        pass 
    338409 
     410    def getCurrentStateAsXml(self): 
     411        """ 
     412        Returns an XML version of the current state 
     413        """ 
     414        state = {} 
     415        for tab in self.tabs: 
     416            pass 
     417        return state 
     418 
    339419    @property 
    340420    def currentTab(self): 
  • src/sas/qtgui/Perspectives/Fitting/FittingWidget.py

    r7f41584 r21e71f1  
    5252TAB_POLY = 3 
    5353CATEGORY_DEFAULT = "Choose category..." 
     54MODEL_DEFAULT = "Choose model..." 
    5455CATEGORY_STRUCTURE = "Structure Factor" 
    5556CATEGORY_CUSTOM = "Plugin Models" 
     
    574575        # Signals from other widgets 
    575576        self.communicate.customModelDirectoryChanged.connect(self.onCustomModelChange) 
    576         self.communicate.saveAnalysisSignal.connect(self.savePageState) 
    577         #self.communicate.loadAnalysisSignal.connect(self.loadPageState) 
    578577        self.smearing_widget.smearingChangedSignal.connect(self.onSmearingOptionsUpdate) 
    579578 
     
    623622        to_string = "to its current value" if num_rows == 1 else "to their current values" 
    624623        has_constraints = any([self.rowHasConstraint(i) for i in rows]) 
     624        has_real_constraints = any([self.rowHasActiveConstraint(i) for i in rows]) 
    625625 
    626626        self.actionSelect = QtWidgets.QAction(self) 
     
    640640        self.actionRemoveConstraint.setText(QtCore.QCoreApplication.translate("self", "Remove constraint")) 
    641641 
     642        self.actionEditConstraint = QtWidgets.QAction(self) 
     643        self.actionEditConstraint.setObjectName("actionEditConstrain") 
     644        self.actionEditConstraint.setText(QtCore.QCoreApplication.translate("self", "Edit constraint")) 
     645 
    642646        self.actionMultiConstrain = QtWidgets.QAction(self) 
    643647        self.actionMultiConstrain.setObjectName("actionMultiConstrain") 
     
    654658        if has_constraints: 
    655659            menu.addAction(self.actionRemoveConstraint) 
     660            if num_rows == 1 and has_real_constraints: 
     661                menu.addAction(self.actionEditConstraint) 
    656662            #if num_rows == 1: 
    657663            #    menu.addAction(self.actionEditConstraint) 
     
    664670        self.actionConstrain.triggered.connect(self.addSimpleConstraint) 
    665671        self.actionRemoveConstraint.triggered.connect(self.deleteConstraint) 
     672        self.actionEditConstraint.triggered.connect(self.editConstraint) 
    666673        self.actionMutualMultiConstrain.triggered.connect(self.showMultiConstraint) 
    667674        self.actionSelect.triggered.connect(self.selectParameters) 
     
    701708        new_func = c_text.replace(param_used, updated_param_used) 
    702709        constraint.func = new_func 
     710        constraint.value_ex = updated_param_used 
    703711        # Which row is the constrained parameter in? 
    704712        row = self.getRowFromName(constraint.param) 
     713 
     714        # what is the parameter to constraint to? 
     715        constraint.value = param_used 
     716 
     717        # Should the new constraint be validated? 
     718        constraint.validate = mc_widget.validate 
    705719 
    706720        # Create a new item and add the Constraint object as a child 
     
    799813        self.communicate.statusBarUpdateSignal.emit('Constraint added') 
    800814 
     815    def editConstraint(self): 
     816        """ 
     817        Delete constraints from selected parameters. 
     818        """ 
     819        params_list = [s.data() for s in self.lstParams.selectionModel().selectedRows() 
     820                   if self.isCheckable(s.row())] 
     821        assert len(params_list) == 1 
     822        row = self.lstParams.selectionModel().selectedRows()[0].row() 
     823        constraint = self.getConstraintForRow(row) 
     824        # Create and display the widget for param1 and param2 
     825        mc_widget = MultiConstraint(self, params=params_list, constraint=constraint) 
     826        # Check if any of the parameters are polydisperse 
     827        if not np.any([FittingUtilities.isParamPolydisperse(p, self.model_parameters, is2D=self.is2D) for p in params_list]): 
     828            # no parameters are pd - reset the text to not show the warning 
     829            mc_widget.lblWarning.setText("") 
     830        if mc_widget.exec_() != QtWidgets.QDialog.Accepted: 
     831            return 
     832 
     833        constraint = Constraint() 
     834        c_text = mc_widget.txtConstraint.text() 
     835 
     836        # widget.params[0] is the parameter we're constraining 
     837        constraint.param = mc_widget.params[0] 
     838        # parameter should have the model name preamble 
     839        model_name = self.kernel_module.name 
     840        # param_used is the parameter we're using in constraining function 
     841        param_used = mc_widget.params[1] 
     842        # Replace param_used with model_name.param_used 
     843        updated_param_used = model_name + "." + param_used 
     844        # Update constraint with new values 
     845        constraint.func = c_text 
     846        constraint.value_ex = updated_param_used 
     847        constraint.value = param_used 
     848        # Should the new constraint be validated? 
     849        constraint.validate = mc_widget.validate 
     850 
     851        # Which row is the constrained parameter in? 
     852        row = self.getRowFromName(constraint.param) 
     853 
     854        # Create a new item and add the Constraint object as a child 
     855        self.addConstraintToRow(constraint=constraint, row=row) 
     856 
    801857    def deleteConstraint(self): 
    802858        """ 
     
    853909            return None 
    854910 
     911    def allParamNames(self): 
     912        """ 
     913        Returns a list of all parameter names defined on the current model 
     914        """ 
     915        all_params = self.kernel_module._model_info.parameters.kernel_parameters 
     916        all_param_names = [param.name for param in all_params] 
     917        # Assure scale and background are always included 
     918        if 'scale' not in all_param_names: 
     919            all_param_names.append('scale') 
     920        if 'background' not in all_param_names: 
     921            all_param_names.append('background') 
     922        return all_param_names 
     923 
     924    def paramHasConstraint(self, param=None): 
     925        """ 
     926        Finds out if the given parameter in the main model has a constraint child 
     927        """ 
     928        if param is None: return False 
     929        if param not in self.allParamNames(): return False 
     930 
     931        for row in range(self._model_model.rowCount()): 
     932            if self._model_model.item(row,0).text() != param: continue 
     933            return self.rowHasConstraint(row) 
     934 
     935        # nothing found 
     936        return False 
     937 
    855938    def rowHasConstraint(self, row): 
    856939        """ 
     
    10181101        Checks if the current model has magnetic scattering implemented 
    10191102        """ 
    1020         has_params = False 
     1103        has_mag_params = False 
    10211104        if self.kernel_module: 
    10221105            has_mag_params = len(self.kernel_module.magnetic_params) > 0 
     
    10291112        model = self.cbModel.currentText() 
    10301113 
     1114        if model == MODEL_DEFAULT: 
     1115            # if the previous category was not the default, keep it. 
     1116            # Otherwise, just return 
     1117            if self._previous_model_index != 0: 
     1118                # We need to block signals, or else state changes on perceived unchanged conditions 
     1119                self.cbModel.blockSignals(True) 
     1120                self.cbModel.setCurrentIndex(self._previous_model_index) 
     1121                self.cbModel.blockSignals(False) 
     1122            return 
     1123 
    10311124        # Assure the control is active 
    10321125        if not self.cbModel.isEnabled(): 
     
    10351128        if not model: 
    10361129            return 
     1130 
     1131        self.chkMagnetism.setEnabled(self.canHaveMagnetism()) 
     1132        self.chkMagnetism.setEnabled(self.canHaveMagnetism()) 
     1133        self.tabFitting.setTabEnabled(TAB_MAGNETISM, self.chkMagnetism.isChecked() and self.canHaveMagnetism()) 
     1134        self._previous_model_index = self.cbModel.currentIndex() 
    10371135 
    10381136        # Reset parameters to fit 
     
    12131311            self._model_model.clear() 
    12141312            return 
    1215  
     1313        # Wipe out the parameter model 
     1314        self._model_model.clear() 
    12161315        # Safely clear and enable the model combo 
    12171316        self.cbModel.blockSignals(True) 
     
    12251324        model_list = self.master_category_dict[category] 
    12261325        # Populate the models combobox 
     1326        self.cbModel.blockSignals(True) 
     1327        self.cbModel.addItem(MODEL_DEFAULT) 
    12271328        self.cbModel.addItems(sorted([model for (model, _) in model_list])) 
     1329        self.cbModel.blockSignals(False) 
    12281330 
    12291331    def onPolyModelChange(self, top, bottom): 
     
    19372039        Emits plotRequestedSignal for all plots found in the given model under the provided item name. 
    19382040        """ 
    1939         fitpage_name = "" if self.tab_id is None else "M"+str(self.tab_id) 
     2041        fitpage_name = self.kernel_module.name 
    19402042        plots = GuiUtils.plotsFromFilename(item_name, item_model) 
    19412043        # Has the fitted data been shown? 
     
    24252527    def isCheckable(self, row): 
    24262528        return self._model_model.item(row, 0).isCheckable() 
     2529 
     2530    def selectCheckbox(self, row): 
     2531        """ 
     2532        Select the checkbox in given row. 
     2533        """ 
     2534        assert 0<= row <= self._model_model.rowCount() 
     2535        index = self._model_model.index(row, 0) 
     2536        item = self._model_model.itemFromIndex(index) 
     2537        item.setCheckState(QtCore.Qt.Checked) 
    24272538 
    24282539    def checkboxSelected(self, item): 
     
    34303541        return report_logic.reportList() 
    34313542 
    3432     def savePageState(self): 
    3433         """ 
    3434         Create and serialize local PageState 
    3435         """ 
    3436         filepath = self.saveAsAnalysisFile() 
    3437         if filepath is None or filepath == "": 
    3438             return 
    3439  
    3440         fitpage_state = self.getFitPage() 
    3441         fitpage_state += self.getFitModel() 
    3442  
    3443         with open(filepath, 'w') as statefile: 
    3444             for line in fitpage_state: 
    3445                 statefile.write(str(line)) 
    3446  
    3447         self.communicate.statusBarUpdateSignal.emit('Analysis saved.') 
    3448  
    3449     def saveAsAnalysisFile(self): 
    3450         """ 
    3451         Show the save as... dialog and return the chosen filepath 
    3452         """ 
    3453         default_name = "FitPage"+str(self.tab_id)+".fitv" 
    3454  
    3455         wildcard = "fitv files (*.fitv)" 
    3456         kwargs = { 
    3457             'caption'   : 'Save As', 
    3458             'directory' : default_name, 
    3459             'filter'    : wildcard, 
    3460             'parent'    : None, 
    3461         } 
    3462         # Query user for filename. 
    3463         filename_tuple = QtWidgets.QFileDialog.getSaveFileName(**kwargs) 
    3464         filename = filename_tuple[0] 
    3465         return filename 
    3466  
    34673543    def loadPageStateCallback(self,state=None, datainfo=None, format=None): 
    34683544        """ 
     
    35493625 
    35503626        param_list.append(['is_data', str(self.data_is_loaded)]) 
    3551         if self.data_is_loaded: 
    3552             param_list.append(['data_id', str(self.logic.data.id)]) 
    3553             param_list.append(['data_name', str(self.logic.data.filename)]) 
    3554  
     3627        data_ids = [] 
     3628        filenames = [] 
     3629        if self.is_batch_fitting: 
     3630            for item in self.all_data: 
     3631                # need item->data->data_id 
     3632                data = GuiUtils.dataFromItem(item) 
     3633                data_ids.append(data.id) 
     3634                filenames.append(data.filename) 
     3635        else: 
     3636            if self.data_is_loaded: 
     3637                data_ids = [str(self.logic.data.id)] 
     3638                filenames = [str(self.logic.data.filename)] 
     3639        param_list.append(['is_batch_fitting', str(self.is_batch_fitting)]) 
     3640        param_list.append(['data_name', filenames]) 
     3641        param_list.append(['data_id', data_ids]) 
     3642        param_list.append(['tab_name', self.modelName()]) 
    35553643        # option tab 
    35563644        param_list.append(['q_range_min', str(self.q_range_min)]) 
     
    35833671        """ 
    35843672        param_list = [] 
     3673        if self.kernel_module is None: 
     3674            return param_list 
     3675 
    35853676        param_list.append(['model_name', str(self.cbModel.currentText())]) 
    35863677 
     
    36223713            except: 
    36233714                pass 
    3624  
    3625             param_list.append([param_name, param_checked, param_value, param_error, param_min, param_max]) 
     3715            # Do we have any constraints on this parameter? 
     3716            constraint = self.getConstraintForRow(row) 
     3717            cons = () 
     3718            if constraint is not None: 
     3719                value = constraint.value 
     3720                func = constraint.func 
     3721                value_ex = constraint.value_ex 
     3722                param = constraint.param 
     3723                validate = constraint.validate 
     3724 
     3725                cons = (value, param, value_ex, validate, func) 
     3726 
     3727            param_list.append([param_name, param_checked, param_value,param_error, param_min, param_max, cons]) 
    36263728 
    36273729        def gatherPolyParams(row): 
     
    36873789        cb_text = cb.text() 
    36883790 
    3689         context = {} 
    36903791        lines = cb_text.split(':') 
    36913792        if lines[0] != 'sasview_parameter_values': 
     
    36993800                line_dict[content[0]] = content[1:] 
    37003801 
     3802        self.updatePageWithParameters(line_dict) 
     3803 
     3804    def createPageForParameters(self, line_dict): 
     3805        """ 
     3806        Sets up page with requested model/str factor 
     3807        and fills it up with sent parameters 
     3808        """ 
     3809        if 'fitpage_category' in line_dict: 
     3810            self.cbCategory.setCurrentIndex(self.cbCategory.findText(line_dict['fitpage_category'][0])) 
     3811        if 'fitpage_model' in line_dict: 
     3812            self.cbModel.setCurrentIndex(self.cbModel.findText(line_dict['fitpage_model'][0])) 
     3813        if 'fitpage_structure' in line_dict: 
     3814            self.cbStructureFactor.setCurrentIndex(self.cbStructureFactor.findText(line_dict['fitpage_structure'][0])) 
     3815 
     3816        # Now that the page is ready for parameters, fill it up 
     3817        self.updatePageWithParameters(line_dict) 
     3818 
     3819    def updatePageWithParameters(self, line_dict): 
     3820        """ 
     3821        Update FitPage with parameters in line_dict 
     3822        """ 
     3823        if 'model_name' not in line_dict.keys(): 
     3824            return 
    37013825        model = line_dict['model_name'][0] 
    3702  
    3703         if 'model_name' not in line_dict.keys(): 
    3704             return False 
     3826        context = {} 
    37053827 
    37063828        if 'multiplicity' in line_dict.keys(): 
     
    37113833                self.updateMultiplicityCombo(multip) 
    37123834 
     3835        if 'tab_name' in line_dict.keys(): 
     3836            self.kernel_module.name = line_dict['tab_name'][0] 
    37133837        if 'polydisperse_params' in line_dict.keys(): 
    37143838            self.chkPolydispersity.setChecked(line_dict['polydisperse_params'][0]=='True') 
     
    38333957            ioffset = 0 
    38343958            joffset = 0 
    3835             if len(param_dict[param_name])>4: 
     3959            if len(param_dict[param_name])>5: 
    38363960                # error values are not editable - no need to update 
    38373961                ioffset = 1 
     
    38463970            except: 
    38473971                pass 
     3972 
     3973            # constraints 
     3974            cons = param_dict[param_name][4+ioffset] 
     3975            if cons is not None and len(cons)==5: 
     3976                value = cons[0] 
     3977                param = cons[1] 
     3978                value_ex = cons[2] 
     3979                validate = cons[3] 
     3980                function = cons[4] 
     3981                constraint = Constraint() 
     3982                constraint.value = value 
     3983                constraint.func = function 
     3984                constraint.param = param 
     3985                constraint.value_ex = value_ex 
     3986                constraint.validate = validate 
     3987                self.addConstraintToRow(constraint=constraint, row=row) 
    38483988 
    38493989            self.setFocus() 
  • src/sas/qtgui/Perspectives/Fitting/MultiConstraint.py

    r305114c r09e0c32  
    2020    Logic class for interacting with MultiConstrainedUI view 
    2121    """ 
    22     def __init__(self, parent=None, params=None): 
     22    def __init__(self, parent=None, params=None, constraint=None): 
    2323        """ 
    2424        parent: ConstraintWidget object 
     
    3131        self.params = params 
    3232        self.parent = parent 
     33        # Text of the constraint 
     34        self.function = None 
     35        # Should this constraint be validated? 
     36        self.validate = True 
     37 
     38        self.input_constraint = constraint 
     39        if self.input_constraint is not None: 
     40            variable = constraint.value 
     41            self.function = constraint.func 
     42            self.params.append(variable) 
     43            self.model_name = constraint.value_ex 
     44            # Passed constraint may be too complex for simple validation 
     45            self.validate = constraint.validate 
     46        else: 
     47            self.model_name = self.params[1] 
    3348 
    3449        self.setupLabels() 
     
    3651 
    3752        # Set param text control to the second parameter passed 
    38         self.txtConstraint.setText(self.params[1]) 
    39  
     53        if self.input_constraint is None: 
     54            self.txtConstraint.setText(self.params[1]) 
     55        else: 
     56            self.txtConstraint.setText(self.function) 
    4057        self.cmdOK.clicked.connect(self.accept) 
    4158        self.cmdHelp.clicked.connect(self.onHelp) 
     
    5269        # Switch parameters 
    5370        self.params[1], self.params[0] = self.params[0], self.params[1] 
     71        # change fully qualified param name (e.g. M1.sld -> M1.sld_solvent) 
     72        self.model_name = self.model_name.replace(self.params[0], self.params[1]) 
    5473        # Try to swap parameter names in the line edit 
    5574        current_text = self.txtConstraint.text() 
     
    6483        Setup labels based on current parameters 
    6584        """ 
    66         l1 = self.params[0] 
    67         l2 = self.params[1] 
     85        l1 = str(self.params[0]) 
     86        l2 = str(self.params[1]) 
    6887        self.txtParam1.setText(l1) 
    6988        self.txtParam1_2.setText(l1) 
     
    82101        Add visual cues when formula is incorrect 
    83102        """ 
     103        # Don't validate if requested 
     104        if not self.validate: return 
     105 
    84106        formula_is_valid = False 
    85107        formula_is_valid = self.validateConstraint(self.txtConstraint.text()) 
     
    99121            return False 
    100122 
    101         param_str = str(self.params[1]) 
    102         constraint_text = constraint_text.strip() 
     123        param_str = self.model_name 
    103124 
    104125        # 1. just the parameter 
  • src/sas/qtgui/Perspectives/Fitting/UI/ComplexConstraintUI.ui

    r1738173 recc5d043  
    77    <x>0</x> 
    88    <y>0</y> 
    9     <width>367</width> 
    10     <height>199</height> 
     9    <width>478</width> 
     10    <height>257</height> 
    1111   </rect> 
    1212  </property> 
     
    6969           </sizepolicy> 
    7070          </property> 
     71          <property name="sizeAdjustPolicy"> 
     72           <enum>QComboBox::AdjustToContents</enum> 
     73          </property> 
    7174          <item> 
    7275           <property name="text"> 
     
    100103           </sizepolicy> 
    101104          </property> 
     105          <property name="sizeAdjustPolicy"> 
     106           <enum>QComboBox::AdjustToContents</enum> 
     107          </property> 
    102108          <item> 
    103109           <property name="text"> 
     
    161167       <property name="sizeHint" stdset="0"> 
    162168        <size> 
    163          <width>40</width> 
     169         <width>88</width> 
    164170         <height>20</height> 
    165171        </size> 
     
    168174     </item> 
    169175     <item> 
    170       <widget class="QPushButton" name="cmdOK"> 
     176      <widget class="QToolButton" name="cmdOK"> 
     177       <property name="minimumSize"> 
     178        <size> 
     179         <width>93</width> 
     180         <height>28</height> 
     181        </size> 
     182       </property> 
     183       <property name="toolTip"> 
     184        <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Add the constraint as defined by the above expression.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> 
     185       </property> 
    171186       <property name="text"> 
    172         <string>OK</string> 
    173        </property> 
    174        <property name="default"> 
     187        <string>Add</string> 
     188       </property> 
     189       <property name="popupMode"> 
     190        <enum>QToolButton::MenuButtonPopup</enum> 
     191       </property> 
     192       <property name="toolButtonStyle"> 
     193        <enum>Qt::ToolButtonTextOnly</enum> 
     194       </property> 
     195       <property name="autoRaise"> 
    175196        <bool>false</bool> 
    176197       </property> 
     198       <property name="arrowType"> 
     199        <enum>Qt::DownArrow</enum> 
     200       </property> 
    177201      </widget> 
    178202     </item> 
    179203     <item> 
    180204      <widget class="QPushButton" name="cmdCancel"> 
     205       <property name="toolTip"> 
     206        <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Close the window.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> 
     207       </property> 
    181208       <property name="text"> 
    182         <string>Cancel</string> 
     209        <string>Close</string> 
    183210       </property> 
    184211      </widget> 
     
    197224 <resources/> 
    198225 <connections> 
    199   <connection> 
    200    <sender>cmdOK</sender> 
    201    <signal>clicked()</signal> 
    202    <receiver>ComplexConstraintUI</receiver> 
    203    <slot>accept()</slot> 
    204    <hints> 
    205     <hint type="sourcelabel"> 
    206      <x>149</x> 
    207      <y>144</y> 
    208     </hint> 
    209     <hint type="destinationlabel"> 
    210      <x>179</x> 
    211      <y>82</y> 
    212     </hint> 
    213    </hints> 
    214   </connection> 
    215226  <connection> 
    216227   <sender>cmdCancel</sender> 
  • src/sas/qtgui/Perspectives/Fitting/UI/ConstraintWidgetUI.ui

    r91ad45c recc5d043  
    77    <x>0</x> 
    88    <y>0</y> 
    9     <width>428</width> 
    10     <height>457</height> 
     9    <width>597</width> 
     10    <height>607</height> 
    1111   </rect> 
    1212  </property> 
     
    1414   <string>Constrained and Simultaneous Fit</string> 
    1515  </property> 
    16   <layout class="QGridLayout" name="gridLayout"> 
     16  <layout class="QGridLayout" name="gridLayout_2"> 
    1717   <item row="0" column="0"> 
    1818    <widget class="QGroupBox" name="groupBox"> 
     
    8989      <string>Constraints</string> 
    9090     </property> 
    91      <layout class="QGridLayout" name="gridLayout_2"> 
     91     <layout class="QGridLayout" name="gridLayout"> 
    9292      <item row="0" column="0"> 
    93        <layout class="QHBoxLayout" name="horizontalLayout"> 
    94         <item> 
    95          <widget class="QLabel" name="label"> 
    96           <property name="text"> 
    97            <string>Special cases</string> 
    98           </property> 
    99          </widget> 
    100         </item> 
    101         <item> 
    102          <widget class="QComboBox" name="cbCases"> 
     93       <layout class="QHBoxLayout" name="horizontalLayout_4"> 
     94        <item> 
     95         <layout class="QHBoxLayout" name="horizontalLayout"> 
    10396          <item> 
    104            <property name="text"> 
    105             <string>None</string> 
    106            </property> 
     97           <widget class="QLabel" name="label"> 
     98            <property name="text"> 
     99             <string>Special cases</string> 
     100            </property> 
     101           </widget> 
    107102          </item> 
     103          <item> 
     104           <widget class="QComboBox" name="cbCases"> 
     105            <item> 
     106             <property name="text"> 
     107              <string>None</string> 
     108             </property> 
     109            </item> 
     110           </widget> 
     111          </item> 
     112         </layout> 
     113        </item> 
     114        <item> 
     115         <spacer name="horizontalSpacer_3"> 
     116          <property name="orientation"> 
     117           <enum>Qt::Horizontal</enum> 
     118          </property> 
     119          <property name="sizeHint" stdset="0"> 
     120           <size> 
     121            <width>40</width> 
     122            <height>20</height> 
     123           </size> 
     124          </property> 
     125         </spacer> 
     126        </item> 
     127        <item> 
     128         <widget class="QPushButton" name="cmdAdd"> 
     129          <property name="toolTip"> 
     130           <string>Define constraints between two fit pages.</string> 
     131          </property> 
     132          <property name="text"> 
     133           <string>Add constraints</string> 
     134          </property> 
    108135         </widget> 
    109136        </item> 
     
    160187        </size> 
    161188       </property> 
     189       <property name="toolTip"> 
     190        <string>Perform simultaneous fitting of selected fit pages.</string> 
     191       </property> 
    162192       <property name="text"> 
    163193        <string>Fit</string> 
     
    179209        </size> 
    180210       </property> 
     211       <property name="toolTip"> 
     212        <string>Display help on constrained and simultaneous fitting.</string> 
     213       </property> 
    181214       <property name="text"> 
    182215        <string>Help</string> 
  • src/sas/qtgui/Perspectives/Fitting/UI/MultiConstraintUI.ui

    r1738173 recc5d043  
    1010    <x>0</x> 
    1111    <y>0</y> 
    12     <width>369</width> 
    13     <height>201</height> 
     12    <width>435</width> 
     13    <height>233</height> 
    1414   </rect> 
    1515  </property> 
     
    184184 <connections> 
    185185  <connection> 
     186   <sender>cmdCancel</sender> 
     187   <signal>clicked()</signal> 
     188   <receiver>MultiConstraintUI</receiver> 
     189   <slot>reject()</slot> 
     190   <hints> 
     191    <hint type="sourcelabel"> 
     192     <x>187</x> 
     193     <y>121</y> 
     194    </hint> 
     195    <hint type="destinationlabel"> 
     196     <x>184</x> 
     197     <y>71</y> 
     198    </hint> 
     199   </hints> 
     200  </connection> 
     201  <connection> 
    186202   <sender>cmdOK</sender> 
    187203   <signal>clicked()</signal> 
     
    199215   </hints> 
    200216  </connection> 
    201   <connection> 
    202    <sender>cmdCancel</sender> 
    203    <signal>clicked()</signal> 
    204    <receiver>MultiConstraintUI</receiver> 
    205    <slot>reject()</slot> 
    206    <hints> 
    207     <hint type="sourcelabel"> 
    208      <x>187</x> 
    209      <y>121</y> 
    210     </hint> 
    211     <hint type="destinationlabel"> 
    212      <x>184</x> 
    213      <y>71</y> 
    214     </hint> 
    215    </hints> 
    216   </connection> 
    217217 </connections> 
    218218</ui> 
  • src/sas/qtgui/Perspectives/Fitting/UnitTesting/ComplexConstraintTest.py

    r725d9c06 rd72ac57  
    3434        category_index = self.tab1.cbCategory.findText("Shape Independent") 
    3535        self.tab1.cbCategory.setCurrentIndex(category_index) 
     36        model_index = self.tab1.cbModel.findText("be_polyelectrolyte") 
     37        self.tab1.cbModel.setCurrentIndex(model_index) 
     38 
    3639        category_index = self.tab2.cbCategory.findText("Cylinder") 
    3740        self.tab2.cbCategory.setCurrentIndex(category_index) 
     41        model_index = self.tab2.cbModel.findText("barbell") 
     42        self.tab2.cbModel.setCurrentIndex(model_index) 
    3843 
    3944        tabs = [self.tab1, self.tab2] 
     
    127132        """ 
    128133        # default data 
    129         self.assertEqual(self.widget.constraint(), ('M1', 'scale', '=', 'M1.scale')) 
     134        c = self.widget.constraint() 
     135        self.assertEqual(c[0], 'M1') 
     136        self.assertEqual(c[1].func, 'M1.scale') 
    130137 
    131138        # Change parameter and operand 
    132         self.widget.cbOperator.setCurrentIndex(3) 
    133         self.widget.cbParam1.setCurrentIndex(3) 
    134         self.assertEqual(self.widget.constraint(), ('M1', 'bjerrum_length', '>=', 'M1.scale')) 
     139        #self.widget.cbOperator.setCurrentIndex(3) 
     140        self.widget.cbParam2.setCurrentIndex(3) 
     141        c = self.widget.constraint() 
     142        self.assertEqual(c[0], 'M1') 
     143        self.assertEqual(c[1].func, 'M1.sld_solvent') 
     144        #self.assertEqual(c[1].operator, '>=') 
    135145 
    136146    def testOnHelp(self): 
  • src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingWidgetTest.py

    rec4a143 rbaeac95  
    175175        category_index = self.widget.cbCategory.findText("Shape Independent") 
    176176        self.widget.cbCategory.setCurrentIndex(category_index) 
     177        model_index = self.widget.cbModel.findText("be_polyelectrolyte") 
     178        self.widget.cbModel.setCurrentIndex(model_index) 
    177179 
    178180        # test the model combo content 
    179         self.assertEqual(self.widget.cbModel.count(), 28) 
     181        self.assertEqual(self.widget.cbModel.count(), 30) 
    180182 
    181183        # Try to change back to default 
     
    183185 
    184186        # Observe no such luck 
    185         self.assertEqual(self.widget.cbCategory.currentIndex(), 6) 
    186         self.assertEqual(self.widget.cbModel.count(), 29) 
     187        self.assertEqual(self.widget.cbCategory.currentIndex(), 7) 
     188        self.assertEqual(self.widget.cbModel.count(), 30) 
    187189 
    188190        # Set the structure factor 
     
    201203        category_index = self.widget.cbCategory.findText("Shape Independent") 
    202204        self.widget.cbCategory.setCurrentIndex(category_index) 
     205        model_index = self.widget.cbModel.findText("be_polyelectrolyte") 
     206        self.widget.cbModel.setCurrentIndex(model_index) 
    203207 
    204208        # check the enablement of controls 
     
    215219        #  
    216220        # Now change the model 
    217         self.widget.cbModel.setCurrentIndex(2) 
     221        self.widget.cbModel.setCurrentIndex(4) 
    218222        self.assertEqual(self.widget.cbModel.currentText(),'dab') 
    219223 
     
    226230        self.widget.data_is_loaded = True 
    227231        # Reset the sasmodel index 
    228         self.widget.cbModel.setCurrentIndex(1) 
     232        self.widget.cbModel.setCurrentIndex(2) 
    229233        self.assertEqual(self.widget.cbModel.currentText(),'broad_peak') 
    230234 
     
    377381        category_index = self.widget.cbCategory.findText("Shape Independent") 
    378382        self.widget.cbCategory.setCurrentIndex(category_index) 
     383        model_index = self.widget.cbModel.findText("be_polyelectrolyte") 
     384        self.widget.cbModel.setCurrentIndex(model_index) 
     385 
    379386        # Check the poly model 
    380387        self.assertEqual(self.widget._poly_model.rowCount(), 0) 
     
    383390        # Change the category index so we have a model available 
    384391        self.widget.cbCategory.setCurrentIndex(2) 
     392        self.widget.cbModel.setCurrentIndex(1) 
    385393 
    386394        # Check the poly model 
     
    556564        category_index = self.widget.cbCategory.findText("Sphere") 
    557565        self.widget.cbCategory.setCurrentIndex(category_index) 
     566        model_index = self.widget.cbModel.findText("adsorbed_layer") 
     567        self.widget.cbModel.setCurrentIndex(model_index) 
    558568 
    559569        # Check the magnetic model 
     
    634644        category_index = self.widget.cbCategory.findText("Sphere") 
    635645        self.widget.cbCategory.setCurrentIndex(category_index) 
     646        model_index = self.widget.cbModel.findText("adsorbed_layer") 
     647        self.widget.cbModel.setCurrentIndex(model_index) 
    636648 
    637649        # Check the enablement/text 
     
    973985        category_index = self.widget.cbCategory.findText("Sphere") 
    974986        self.widget.cbCategory.setCurrentIndex(category_index) 
     987        model_index = self.widget.cbModel.findText("adsorbed_layer") 
     988        self.widget.cbModel.setCurrentIndex(model_index) 
    975989        self.widget.main_params_to_fit = ['scale'] 
    976990 
     
    9861000        self.assertListEqual(fp.main_params_to_fit, ['scale']) 
    9871001 
    988     def testPushFitPage(self): 
     1002    def notestPushFitPage(self): 
    9891003        """ 
    9901004        Push current state of fitpage onto stack 
     
    9971011        self.widget.data = item 
    9981012        category_index = self.widget.cbCategory.findText("Sphere") 
     1013        model_index = self.widget.cbModel.findText("adsorbed_layer") 
     1014        self.widget.cbModel.setCurrentIndex(model_index) 
    9991015 
    10001016        # Asses the initial state of stack 
  • src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py

    r65759c7 r7f41584  
    368368            cbox = createFixedChoiceComboBox(par, row) 
    369369 
     370            # Apply combobox if required 
     371            if None not in (view, cbox): 
     372                # set the min/max cell to be empty 
     373                item3.setText("") 
     374                item4.setText("") 
     375 
    370376            # Always add to the model 
    371377            if row_num is None: 
     
    375381                row_num += 1 
    376382 
    377             # Apply combobox if required 
    378             if None not in (view, cbox): 
     383            if cbox is not None: 
    379384                view.setIndexWidget(item2.index(), cbox) 
    380385 
  • src/sas/qtgui/Perspectives/Fitting/ViewDelegate.py

    r722b7d6 r7f41584  
    9595            # Set some columns uneditable 
    9696            return None 
     97        if index.column() in (self.param_min, self.param_max): 
     98            # Check if the edit role is set 
     99            if not (index.flags() & QtCore.Qt.ItemIsEditable): 
     100                return None 
    97101 
    98102        return super(ModelViewDelegate, self).createEditor(widget, option, index) 
  • src/sas/qtgui/Perspectives/Fitting/media/fitting_help.rst

    r00a40bd r3b0b17e  
    125125 
    126126For a complete list of all the library models available in SasView, see 
    127 the `Model Documentation <../../../index.html>`_ . 
     127the `Model Documentation <models/index.html>`_ . 
    128128 
    129129It is also possible to add your own models. 
Note: See TracChangeset for help on using the changeset viewer.