Ignore:
File:
1 edited

Legend:

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

    rdaf7c9c rd8d81ea  
    2828from sas.qtgui.Plotting.PlotterData import Data1D 
    2929from sas.qtgui.Plotting.PlotterData import Data2D 
     30from sas.qtgui.Plotting.Plotter import PlotterWidget 
    3031 
    3132from sas.qtgui.Perspectives.Fitting.UI.FittingWidgetUI import Ui_FittingWidgetUI 
     
    5758DEFAULT_POLYDISP_FUNCTION = 'gaussian' 
    5859 
     60# CRUFT: remove when new release of sasmodels is available 
     61# https://github.com/SasView/sasview/pull/181#discussion_r218135162 
     62from sasmodels.sasview_model import SasviewModel 
     63if not hasattr(SasviewModel, 'get_weights'): 
     64    def get_weights(self, name): 
     65        """ 
     66        Returns the polydispersity distribution for parameter *name* as *value* and *weight* arrays. 
     67        """ 
     68        # type: (str) -> Tuple(np.ndarray, np.ndarray) 
     69        _, x, w = self._get_weights(self._model_info.parameters[name]) 
     70        return x, w 
     71 
     72    SasviewModel.get_weights = get_weights 
    5973 
    6074logger = logging.getLogger(__name__) 
     
    273287        self.theory_item = None 
    274288 
     289        # list column widths 
     290        self.lstParamHeaderSizes = {} 
     291 
    275292        # signal communicator 
    276293        self.communicate = self.parent.communicate 
     
    361378        self.lstParams.customContextMenuRequested.connect(self.showModelContextMenu) 
    362379        self.lstParams.setAttribute(QtCore.Qt.WA_MacShowFocusRect, False) 
     380        # Column resize signals 
     381        self.lstParams.header().sectionResized.connect(self.onColumnWidthUpdate) 
     382 
    363383        # Poly model displayed in poly list 
    364384        self.lstPoly.setModel(self._poly_model) 
     
    642662        # Create and display the widget for param1 and param2 
    643663        mc_widget = MultiConstraint(self, params=params_list) 
     664        # Check if any of the parameters are polydisperse 
     665        if not np.any([FittingUtilities.isParamPolydisperse(p, self.model_parameters, is2D=self.is2D) for p in params_list]): 
     666            # no parameters are pd - reset the text to not show the warning 
     667            mc_widget.lblWarning.setText("") 
    644668        if mc_widget.exec_() != QtWidgets.QDialog.Accepted: 
    645669            return 
     
    11081132        self.SASModelToQModel(model, structure_factor) 
    11091133 
     1134        for column, width in self.lstParamHeaderSizes.items(): 
     1135            self.lstParams.setColumnWidth(column, width) 
     1136 
    11101137        # Update plot 
    11111138        self.updateData() 
     
    12191246        if model_column in [delegate.poly_pd, delegate.poly_error, delegate.poly_min, delegate.poly_max]: 
    12201247            row = self.getRowFromName(parameter_name) 
    1221             param_item = self._model_model.item(row) 
     1248            param_item = self._model_model.item(row).child(0).child(0, model_column) 
     1249            if param_item is None: 
     1250                return 
    12221251            self._model_model.blockSignals(True) 
    1223             param_item.child(0).child(0, model_column).setText(item.text()) 
     1252            param_item.setText(item.text()) 
    12241253            self._model_model.blockSignals(False) 
    12251254 
     
    13661395        self.communicate.statusBarUpdateSignal.emit('Fitting started...') 
    13671396        self.fit_started = True 
     1397 
    13681398        # Disable some elements 
    1369         self.setFittingStarted() 
     1399        self.disableInteractiveElements() 
    13701400 
    13711401    def stopFit(self): 
     
    13761406            return 
    13771407        self.calc_fit.stop() 
    1378         #self.fit_started=False 
    13791408        #re-enable the Fit button 
    1380         self.setFittingStopped() 
     1409        self.enableInteractiveElements() 
    13811410 
    13821411        msg = "Fitting cancelled." 
     
    13921421        """ 
    13931422        """ 
    1394         self.setFittingStopped() 
     1423        self.enableInteractiveElements() 
    13951424        msg = "Fitting failed with: "+ str(reason) 
    13961425        self.communicate.statusBarUpdateSignal.emit(msg) 
     
    14091438        """ 
    14101439        #re-enable the Fit button 
    1411         self.setFittingStopped() 
     1440        self.enableInteractiveElements() 
    14121441 
    14131442        if len(result) == 0: 
     
    14811510        """ 
    14821511        #re-enable the Fit button 
    1483         self.setFittingStopped() 
     1512        self.enableInteractiveElements() 
    14841513 
    14851514        if len(result) == 0: 
     
    16661695        self.iterateOverModel(updatePolyValues) 
    16671696        self._model_model.itemChanged.connect(self.onMainParamsChange) 
    1668  
    1669         # Adjust the table cells width. 
    1670         # TODO: find a way to dynamically adjust column width while resized expanding 
    1671         self.lstParams.resizeColumnToContents(0) 
    1672         self.lstParams.resizeColumnToContents(4) 
    1673         self.lstParams.resizeColumnToContents(5) 
    1674         self.lstParams.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding) 
    16751697 
    16761698    def iterateOverPolyModel(self, func): 
     
    20122034            self.magnet_params = {} 
    20132035            self.setMagneticModel() 
    2014  
    2015         # Adjust the table cells width 
    2016         self.lstParams.resizeColumnToContents(0) 
    2017         self.lstParams.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding) 
    20182036 
    20192037        # Now we claim the model has been loaded 
     
    23402358 
    23412359        # add polydisperse parameters if asked 
    2342         if self.chkPolydispersity.isChecked(): 
     2360        if self.chkPolydispersity.isChecked() and self._poly_model.rowCount() > 0: 
    23432361            for key, value in self.poly_params.items(): 
    23442362                model.setParam(key, value) 
    23452363        # add magnetic params if asked 
    23462364        if self.chkMagnetism.isChecked(): 
    2347             for key, value in self.magnet_params.items(): 
     2365            for key, value in self.magnet_params.items() and self._magnet_model.rowCount() > 0: 
    23482366                model.setParam(key, value) 
    23492367 
     
    23632381        weight = FittingUtilities.getWeight(data=data, is2d=self.is2D, flag=self.weighting) 
    23642382 
     2383        # Disable buttons/table 
     2384        self.disableInteractiveElements() 
    23652385        # Awful API to a backend method. 
    23662386        calc_thread = self.methodCalculateForData()(data=data, 
     
    24042424        Thread returned error 
    24052425        """ 
     2426        # Bring the GUI to normal state 
     2427        self.enableInteractiveElements() 
    24062428        print("Calculate Data failed with ", reason) 
    24072429 
     
    24162438        Plot the current 1D data 
    24172439        """ 
     2440        # Bring the GUI to normal state 
     2441        self.enableInteractiveElements() 
     2442 
    24182443        fitted_data = self.logic.new1DPlot(return_data, self.tab_id) 
    24192444        residuals = self.calculateResiduals(fitted_data) 
     
    24242449 
    24252450        if self.data_is_loaded: 
    2426             # delete any plots associated with the data that were not updated (e.g. to remove beta(Q), S_eff(Q)) 
     2451            # delete any plots associated with the data that were not updated 
     2452            # (e.g. to remove beta(Q), S_eff(Q)) 
    24272453            GuiUtils.deleteRedundantPlots(self.all_data[self.data_index], new_plots) 
    24282454            pass 
    24292455        else: 
    2430             # delete theory items for the model, in order to get rid of any redundant items, e.g. beta(Q), S_eff(Q) 
     2456            # delete theory items for the model, in order to get rid of any 
     2457            # redundant items, e.g. beta(Q), S_eff(Q) 
    24312458            self.communicate.deleteIntermediateTheoryPlotsSignal.emit(self.kernel_module.id) 
     2459 
     2460        # Create plots for parameters with enabled polydispersity 
     2461        for plot in FittingUtilities.plotPolydispersities(return_data.get('model', None)): 
     2462            data_id = fitted_data.id.split() 
     2463            plot.id = "{} [{}] {}".format(data_id[0], plot.name, " ".join(data_id[1:])) 
     2464            data_name = fitted_data.name.split() 
     2465            plot.name = " ".join([data_name[0], plot.name] + data_name[1:]) 
     2466            self.createNewIndex(plot) 
     2467            new_plots.append(plot) 
    24322468 
    24332469        # Create plots for intermediate product data 
     
    24452481        Plot the current 2D data 
    24462482        """ 
     2483        # Bring the GUI to normal state 
     2484        self.enableInteractiveElements() 
     2485 
    24472486        fitted_data = self.logic.new2DPlot(return_data) 
    24482487        residuals = self.calculateResiduals(fitted_data) 
     
    24792518        residuals_plot = FittingUtilities.plotResiduals(self.data, weighted_data) 
    24802519        residuals_plot.id = "Residual " + residuals_plot.id 
     2520        residuals_plot.plot_role = Data1D.ROLE_RESIDUAL 
    24812521        self.createNewIndex(residuals_plot) 
    24822522        return residuals_plot 
     
    25142554        Thread threw an exception. 
    25152555        """ 
     2556        # Bring the GUI to normal state 
     2557        self.enableInteractiveElements() 
    25162558        # TODO: remimplement thread cancellation 
    25172559        logger.error("".join(traceback.format_exception(etype, value, tb))) 
     
    27252767        self._poly_model.setData(fname_index, fname) 
    27262768 
     2769    def onColumnWidthUpdate(self, index, old_size, new_size): 
     2770        """ 
     2771        Simple state update of the current column widths in the  param list 
     2772        """ 
     2773        self.lstParamHeaderSizes[index] = new_size 
     2774 
    27272775    def setMagneticModel(self): 
    27282776        """ 
     
    27962844 
    27972845        func = QtWidgets.QComboBox() 
    2798         # Available range of shells displayed in the combobox 
    2799         func.addItems([str(i) for i in range(param_length+1)]) 
    2800  
    2801         # Respond to index change 
    2802         func.currentIndexChanged.connect(self.modifyShellsInList) 
    28032846 
    28042847        # cell 2: combobox 
    28052848        item2 = QtGui.QStandardItem() 
    2806         self._model_model.appendRow([item1, item2]) 
     2849 
     2850        # cell 3: min value 
     2851        item3 = QtGui.QStandardItem() 
     2852 
     2853        # cell 4: max value 
     2854        item4 = QtGui.QStandardItem() 
     2855 
     2856        # cell 4: SLD button 
     2857        item5 = QtGui.QStandardItem() 
     2858        button = QtWidgets.QPushButton() 
     2859        button.setText("Show SLD Profile") 
     2860 
     2861        self._model_model.appendRow([item1, item2, item3, item4, item5]) 
    28072862 
    28082863        # Beautify the row:  span columns 2-4 
    28092864        shell_row = self._model_model.rowCount() 
    28102865        shell_index = self._model_model.index(shell_row-1, 1) 
     2866        button_index = self._model_model.index(shell_row-1, 4) 
    28112867 
    28122868        self.lstParams.setIndexWidget(shell_index, func) 
     2869        self.lstParams.setIndexWidget(button_index, button) 
    28132870        self._n_shells_row = shell_row - 1 
    28142871 
     
    28232880            logger.error("Could not find %s in kernel parameters.", param_name) 
    28242881        default_shell_count = shell_par.default 
     2882        shell_min = 0 
     2883        shell_max = 0 
     2884        try: 
     2885            shell_min = int(shell_par.limits[0]) 
     2886            shell_max = int(shell_par.limits[1]) 
     2887        except IndexError as ex: 
     2888            # no info about limits 
     2889            pass 
     2890        item3.setText(str(shell_min)) 
     2891        item4.setText(str(shell_max)) 
     2892 
     2893        # Respond to index change 
     2894        func.currentTextChanged.connect(self.modifyShellsInList) 
     2895 
     2896        # Respond to button press 
     2897        button.clicked.connect(self.onShowSLDProfile) 
     2898 
     2899        # Available range of shells displayed in the combobox 
     2900        func.addItems([str(i) for i in range(shell_min, shell_max+1)]) 
    28252901 
    28262902        # Add default number of shells to the model 
    2827         func.setCurrentIndex(default_shell_count) 
    2828  
    2829     def modifyShellsInList(self, index): 
     2903        func.setCurrentText(str(default_shell_count)) 
     2904 
     2905    def modifyShellsInList(self, text): 
    28302906        """ 
    28312907        Add/remove additional multishell parameters 
     
    28342910        first_row = self._n_shells_row + 1 
    28352911        remove_rows = self._num_shell_params 
    2836  
     2912        try: 
     2913            index = int(text) 
     2914        except ValueError: 
     2915            # bad text on the control! 
     2916            index = 0 
     2917            logger.error("Multiplicity incorrect! Setting to 0") 
     2918        self.kernel_module.multiplicity = index 
    28372919        if remove_rows > 1: 
    28382920            self._model_model.removeRows(first_row, remove_rows) 
     
    28612943        self.setMagneticModel() 
    28622944 
    2863     def setFittingStarted(self): 
    2864         """ 
    2865         Set buttion caption on fitting start 
     2945    def onShowSLDProfile(self): 
     2946        """ 
     2947        Show a quick plot of SLD profile 
     2948        """ 
     2949        # get profile data 
     2950        x, y = self.kernel_module.getProfile() 
     2951        y *= 1.0e6 
     2952        profile_data = Data1D(x=x, y=y) 
     2953        profile_data.name = "SLD" 
     2954        profile_data.scale = 'linear' 
     2955        profile_data.symbol = 'Line' 
     2956        profile_data.hide_error = True 
     2957        profile_data._xaxis = "R(\AA)" 
     2958        profile_data._yaxis = "SLD(10^{-6}\AA^{-2})" 
     2959 
     2960        plotter = PlotterWidget(self, quickplot=True) 
     2961        plotter.data = profile_data 
     2962        plotter.showLegend = True 
     2963        plotter.plot(hide_error=True, marker='-') 
     2964 
     2965        self.plot_widget = QtWidgets.QWidget() 
     2966        self.plot_widget.setWindowTitle("Scattering Length Density Profile") 
     2967        layout = QtWidgets.QVBoxLayout() 
     2968        layout.addWidget(plotter) 
     2969        self.plot_widget.setLayout(layout) 
     2970        self.plot_widget.show() 
     2971 
     2972    def setInteractiveElements(self, enabled=True): 
     2973        """ 
     2974        Switch interactive GUI elements on/off 
     2975        """ 
     2976        assert isinstance(enabled, bool) 
     2977 
     2978        self.lstParams.setEnabled(enabled) 
     2979        self.lstPoly.setEnabled(enabled) 
     2980        self.lstMagnetic.setEnabled(enabled) 
     2981 
     2982        self.cbCategory.setEnabled(enabled) 
     2983        self.cbModel.setEnabled(enabled) 
     2984        self.cbStructureFactor.setEnabled(enabled) 
     2985 
     2986        self.chkPolydispersity.setEnabled(enabled) 
     2987        self.chkMagnetism.setEnabled(enabled) 
     2988        self.chk2DView.setEnabled(enabled) 
     2989 
     2990    def enableInteractiveElements(self): 
     2991        """ 
     2992        Set buttion caption on fitting/calculate finish 
     2993        Enable the param table(s) 
     2994        """ 
     2995        # Notify the user that fitting is available 
     2996        self.cmdFit.setStyleSheet('QPushButton {color: black;}') 
     2997        self.cmdFit.setText("Fit") 
     2998        self.fit_started = False 
     2999        self.setInteractiveElements(True) 
     3000 
     3001    def disableInteractiveElements(self): 
     3002        """ 
     3003        Set buttion caption on fitting/calculate start 
     3004        Disable the param table(s) 
    28663005        """ 
    28673006        # Notify the user that fitting is being run 
     
    28693008        self.cmdFit.setStyleSheet('QPushButton {color: red;}') 
    28703009        self.cmdFit.setText('Stop fit') 
    2871  
    2872     def setFittingStopped(self): 
    2873         """ 
    2874         Set button caption on fitting stop 
    2875         """ 
    2876         # Notify the user that fitting is available 
    2877         self.cmdFit.setStyleSheet('QPushButton {color: black;}') 
    2878         self.cmdFit.setText("Fit") 
    2879         self.fit_started = False 
     3010        self.setInteractiveElements(False) 
    28803011 
    28813012    def readFitPage(self, fp): 
Note: See TracChangeset for help on using the changeset viewer.