Ignore:
Timestamp:
Mar 21, 2018 2:17:04 AM (7 years ago)
Author:
Piotr Rozyczko <rozyczko@…>
Branches:
ESS_GUI, ESS_GUI_Docs, ESS_GUI_batch_fitting, ESS_GUI_bumps_abstraction, ESS_GUI_iss1116, ESS_GUI_iss879, ESS_GUI_iss959, ESS_GUI_opencl, ESS_GUI_ordering, ESS_GUI_sync_sascalc
Children:
8b480d27
Parents:
e4c475b7
git-author:
Piotr Rozyczko <rozyczko@…> (02/08/18 02:19:04)
git-committer:
Piotr Rozyczko <rozyczko@…> (03/21/18 02:17:04)
Message:

Merging feature branches

File:
1 edited

Legend:

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

    re4c475b7 r3b3b40b  
    2424import sas.qtgui.Utilities.GuiUtils as GuiUtils 
    2525import sas.qtgui.Utilities.LocalConfig as LocalConfig 
     26from sas.qtgui.Utilities.GridPanel import BatchOutputPanel 
    2627from sas.qtgui.Utilities.CategoryInstaller import CategoryInstaller 
    2728from sas.qtgui.Plotting.PlotterData import Data1D 
     
    3637from sas.qtgui.Perspectives.Fitting.FittingLogic import FittingLogic 
    3738from sas.qtgui.Perspectives.Fitting import FittingUtilities 
     39from sas.qtgui.Perspectives.Fitting import ModelUtilities 
    3840from sas.qtgui.Perspectives.Fitting.SmearingWidget import SmearingWidget 
    3941from sas.qtgui.Perspectives.Fitting.OptionsWidget import OptionsWidget 
     
    5052CATEGORY_DEFAULT = "Choose category..." 
    5153CATEGORY_STRUCTURE = "Structure Factor" 
     54CATEGORY_CUSTOM = "Plugin Models" 
    5255STRUCTURE_DEFAULT = "None" 
    5356 
     
    8386    constraintAddedSignal = QtCore.pyqtSignal(list) 
    8487    newModelSignal = QtCore.pyqtSignal() 
     88    fittingFinishedSignal = QtCore.pyqtSignal(tuple) 
     89    batchFittingFinishedSignal = QtCore.pyqtSignal(tuple) 
     90 
    8591    def __init__(self, parent=None, data=None, tab_id=1): 
    8692 
     
    211217        self.page_stack = [] 
    212218        self.all_data = [] 
     219        # custom plugin models 
     220        # {model.name:model} 
     221        self.custom_models = self.customModels() 
    213222        # Polydisp widget table default index for function combobox 
    214223        self.orig_poly_index = 3 
     
    415424            self.onSelectModel() 
    416425 
     426    def customModels(self): 
     427        """ Reads in file names in the custom plugin directory """ 
     428        return ModelUtilities._find_models() 
     429 
    417430    def initializeControls(self): 
    418431        """ 
     
    465478        self._poly_model.itemChanged.connect(self.onPolyModelChange) 
    466479        self._magnet_model.itemChanged.connect(self.onMagnetModelChange) 
     480        self.lstParams.selectionModel().selectionChanged.connect(self.onSelectionChanged) 
     481 
     482        # Local signals 
     483        self.batchFittingFinishedSignal.connect(self.batchFitComplete) 
     484        self.fittingFinishedSignal.connect(self.fitComplete) 
    467485 
    468486        # Signals from separate tabs asking for replot 
    469487        self.options_widget.plot_signal.connect(self.onOptionsUpdate) 
     488 
     489        # Signals from other widgets 
     490        self.communicate.customModelDirectoryChanged.connect(self.onCustomModelChange) 
    470491 
    471492    def modelName(self): 
     
    576597        # widget.params[0] is the parameter we're constraining 
    577598        constraint.param = mc_widget.params[0] 
    578         # Function should have the model name preamble 
     599        # parameter should have the model name preamble 
    579600        model_name = self.kernel_module.name 
    580         constraint.func = model_name + "." + c_text 
     601        # param_used is the parameter we're using in constraining function 
     602        param_used = mc_widget.params[1] 
     603        # Replace param_used with model_name.param_used 
     604        updated_param_used = model_name + "." + param_used 
     605        new_func = c_text.replace(param_used, updated_param_used) 
     606        constraint.func = new_func 
    581607        # Which row is the constrained parameter in? 
    582608        row = self.getRowFromName(constraint.param) 
     
    677703        Delete constraints from selected parameters. 
    678704        """ 
    679         self.deleteConstraintOnParameter(param=None) 
     705        params =  [s.data() for s in self.lstParams.selectionModel().selectedRows() 
     706                   if self.isCheckable(s.row())] 
     707        for param in params: 
     708            self.deleteConstraintOnParameter(param=param) 
    680709 
    681710    def deleteConstraintOnParameter(self, param=None): 
     
    686715        max_col = self.lstParams.itemDelegate().param_max 
    687716        for row in range(self._model_model.rowCount()): 
     717            if not self.rowHasConstraint(row): 
     718                continue 
    688719            # Get the Constraint object from of the model item 
    689720            item = self._model_model.item(row, 1) 
    690             if not item.hasChildren(): 
    691                 continue 
    692             constraint = item.child(0).data() 
     721            constraint = self.getConstraintForRow(row) 
    693722            if constraint is None: 
    694723                continue 
     
    816845        return constraints 
    817846 
     847    def getConstraintsForFitting(self): 
     848        """ 
     849        Return a list of constraints in format ready for use in fiting 
     850        """ 
     851        # Get constraints 
     852        constraints = self.getComplexConstraintsForModel() 
     853        # See if there are any constraints across models 
     854        multi_constraints = [cons for cons in constraints if self.isConstraintMultimodel(cons[1])] 
     855 
     856        if multi_constraints: 
     857            # Let users choose what to do 
     858            msg = "The current fit contains constraints relying on other fit pages.\n" 
     859            msg += "Parameters with those constraints are:\n" +\ 
     860                '\n'.join([cons[0] for cons in multi_constraints]) 
     861            msg += "\n\nWould you like to remove these constraints or cancel fitting?" 
     862            msgbox = QtWidgets.QMessageBox(self) 
     863            msgbox.setIcon(QtWidgets.QMessageBox.Warning) 
     864            msgbox.setText(msg) 
     865            msgbox.setWindowTitle("Existing Constraints") 
     866            # custom buttons 
     867            button_remove = QtWidgets.QPushButton("Remove") 
     868            msgbox.addButton(button_remove, QtWidgets.QMessageBox.YesRole) 
     869            button_cancel = QtWidgets.QPushButton("Cancel") 
     870            msgbox.addButton(button_cancel, QtWidgets.QMessageBox.RejectRole) 
     871            retval = msgbox.exec_() 
     872            if retval == QtWidgets.QMessageBox.RejectRole: 
     873                # cancel fit 
     874                raise ValueError("Fitting cancelled") 
     875            else: 
     876                # remove constraint 
     877                for cons in multi_constraints: 
     878                    self.deleteConstraintOnParameter(param=cons[0]) 
     879                # re-read the constraints 
     880                constraints = self.getComplexConstraintsForModel() 
     881 
     882        return constraints 
     883 
    818884    def showModelDescription(self): 
    819885        """ 
     
    874940        self.respondToModelStructure(model=model, structure_factor=structure) 
    875941 
     942    def onCustomModelChange(self): 
     943        """ 
     944        Reload the custom model combobox 
     945        """ 
     946        self.custom_models = self.customModels() 
     947        self.readCustomCategoryInfo() 
     948        # See if we need to update the combo in-place 
     949        if self.cbCategory.currentText() != CATEGORY_CUSTOM: return 
     950 
     951        current_text = self.cbModel.currentText() 
     952        self.cbModel.blockSignals(True) 
     953        self.cbModel.clear() 
     954        self.cbModel.blockSignals(False) 
     955        self.enableModelCombo() 
     956        self.disableStructureCombo() 
     957        # Retrieve the list of models 
     958        model_list = self.master_category_dict[CATEGORY_CUSTOM] 
     959        # Populate the models combobox 
     960        self.cbModel.addItems(sorted([model for (model, _) in model_list])) 
     961        new_index = self.cbModel.findText(current_text) 
     962        if new_index != -1: 
     963            self.cbModel.setCurrentIndex(self.cbModel.findText(current_text)) 
     964 
     965    def onSelectionChanged(self): 
     966        """ 
     967        React to parameter selection 
     968        """ 
     969        rows = self.lstParams.selectionModel().selectedRows() 
     970        # Clean previous messages 
     971        self.communicate.statusBarUpdateSignal.emit("") 
     972        if len(rows) == 1: 
     973            # Show constraint, if present 
     974            row = rows[0].row() 
     975            if self.rowHasConstraint(row): 
     976                func = self.getConstraintForRow(row).func 
     977                if func is not None: 
     978                    self.communicate.statusBarUpdateSignal.emit("Active constrain: "+func) 
     979 
    876980    def replaceConstraintName(self, old_name, new_name=""): 
    877981        """ 
     
    886990                    new_func = func.replace(old_name, new_name) 
    887991                    self._model_model.item(row, 1).child(0).data().func = new_func 
     992 
     993    def isConstraintMultimodel(self, constraint): 
     994        """ 
     995        Check if the constraint function text contains current model name 
     996        """ 
     997        current_model_name = self.kernel_module.name 
     998        if current_model_name in constraint: 
     999            return False 
     1000        else: 
     1001            return True 
    8881002 
    8891003    def updateData(self): 
     
    9371051            self._model_model.clear() 
    9381052            return 
    939  
     1053             
    9401054        # Safely clear and enable the model combo 
    9411055        self.cbModel.blockSignals(True) 
     
    11051219        except ValueError as ex: 
    11061220            # This should not happen! GUI explicitly forbids this situation 
    1107             self.communicate.statusBarUpdateSignal.emit('Fitting attempt without parameters.') 
     1221            self.communicate.statusBarUpdateSignal.emit(str(ex)) 
    11081222            return 
    11091223 
    11101224        # Create the fitting thread, based on the fitter 
    1111         completefn = self.batchFitComplete if self.is_batch_fitting else self.fitComplete 
     1225        completefn = self.batchFittingCompleted if self.is_batch_fitting else self.fittingCompleted 
    11121226 
    11131227        calc_fit = FitThread(handler=handler, 
     
    11461260        pass 
    11471261 
     1262    def batchFittingCompleted(self, result): 
     1263        """ 
     1264        Send the finish message from calculate threads to main thread 
     1265        """ 
     1266        self.batchFittingFinishedSignal.emit(result) 
     1267 
    11481268    def batchFitComplete(self, result): 
    11491269        """ 
     
    11521272        #re-enable the Fit button 
    11531273        self.setFittingStopped() 
     1274        # Show the grid panel 
     1275        self.grid_window = BatchOutputPanel(parent=self, output_data=result[0]) 
     1276        self.grid_window.show() 
     1277 
     1278    def fittingCompleted(self, result): 
     1279        """ 
     1280        Send the finish message from calculate threads to main thread 
     1281        """ 
     1282        self.fittingFinishedSignal.emit(result) 
    11541283 
    11551284    def fitComplete(self, result): 
     
    11621291 
    11631292        if result is None: 
    1164             msg = "Fitting failed after: %s s.\n" % GuiUtils.formatNumber(elapsed) 
     1293            msg = "Fitting failed." 
    11651294            self.communicate.statusBarUpdateSignal.emit(msg) 
    11661295            return 
     
    12281357        smearing, accuracy, smearing_min, smearing_max = self.smearing_widget.state() 
    12291358 
    1230         constraints = self.getComplexConstraintsForModel() 
     1359        constraints = self.getConstraintsForFitting() 
     1360 
    12311361        smearer = None 
    12321362        handler = None 
     
    12421372                             constraints=constraints) 
    12431373            except ValueError as ex: 
    1244                 logging.error("Setting model parameters failed with: %s" % ex) 
    1245                 return 
     1374                raise ValueError("Setting model parameters failed with: %s" % ex) 
    12461375 
    12471376            qmin, qmax, _ = self.logic.computeRangeFromData(data) 
     
    14091538        if not dict: 
    14101539            return 
    1411         if self._model_model.rowCount() == 0: 
     1540        if self._magnet_model.rowCount() == 0: 
    14121541            return 
    14131542 
     
    15551684            self.models[model.name] = model 
    15561685 
     1686        self.readCustomCategoryInfo() 
     1687 
     1688    def readCustomCategoryInfo(self): 
     1689        """ 
     1690        Reads the custom model category 
     1691        """ 
     1692        #Looking for plugins 
     1693        self.plugins = list(self.custom_models.values()) 
     1694        plugin_list = [] 
     1695        for name, plug in self.custom_models.items(): 
     1696            self.models[name] = plug 
     1697            plugin_list.append([name, True]) 
     1698        self.master_category_dict[CATEGORY_CUSTOM] = plugin_list 
     1699 
    15571700    def regenerateModelDict(self): 
    15581701        """ 
     
    16621805        Setting model parameters into QStandardItemModel based on selected _model_ 
    16631806        """ 
    1664         kernel_module = generate.load_kernel_module(model_name) 
     1807        name = model_name 
     1808        if self.cbCategory.currentText() == CATEGORY_CUSTOM: 
     1809            # custom kernel load requires full path 
     1810            name = os.path.join(ModelUtilities.find_plugins_dir(), model_name+".py") 
     1811        kernel_module = generate.load_kernel_module(name) 
    16651812        self.model_parameters = modelinfo.make_parameter_table(getattr(kernel_module, 'parameters', [])) 
    16661813 
Note: See TracChangeset for help on using the changeset viewer.