Changeset 551f2bc in sasview


Ignore:
Timestamp:
Sep 13, 2018 12:14:58 AM (7 days ago)
Author:
wojciech
Branches:
ESS_GUI, ESS_GUI_Pr_fixes
Children:
13da5f5
Parents:
9b17efd (diff), 8b745c36 (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' of https://github.com/SasView/sasview into ESS_GUI_Pr_fixes

Location:
src/sas
Files:
13 edited

Legend:

Unmodified
Added
Removed
  • src/sas/qtgui/MainWindow/DataExplorer.py

    r2b8286c r33b3e4d  
    574574        new_plots = [] 
    575575        for item, plot in plots.items(): 
    576             if self.updatePlot(plot) and filename != plot.name: 
     576            if self.updatePlot(plot) or filename not in plot.name: 
    577577                continue 
    578578            # Don't plot intermediate results, e.g. P(Q), S(Q) 
     
    583583            # Don't include plots from different fitpages, but always include the original data 
    584584            if fitpage_name in plot.name or filename == plot.name: 
    585                 # 'sophisticated' test to generate standalone plot for residuals 
    586                 if 'esiduals' in plot.title: 
     585                # Residuals get their own plot 
     586                if plot.plot_role == Data1D.ROLE_RESIDUAL: 
    587587                    plot.yscale='linear' 
    588588                    self.plotData([(item, plot)]) 
     
    686686 
    687687        # Update the active chart list 
    688         #self.active_plots[new_plot.data.id] = new_plot 
     688        self.active_plots[new_plot.data.name] = new_plot 
    689689 
    690690    def appendPlot(self): 
     
    729729        data_id = data.name 
    730730        if data_id in ids_keys: 
    731             self.active_plots[data_id].replacePlot(data_id, data) 
     731            # We have data, let's replace data that needs replacing 
     732            if data.plot_role != Data1D.ROLE_DATA: 
     733                self.active_plots[data_id].replacePlot(data_id, data) 
    732734            return True 
    733735        elif data_id in ids_vals: 
    734             list(self.active_plots.values())[ids_vals.index(data_id)].replacePlot(data_id, data) 
     736            if data.plot_role != Data1D.ROLE_DATA: 
     737                list(self.active_plots.values())[ids_vals.index(data_id)].replacePlot(data_id, data) 
    735738            return True 
    736739        return False 
     
    946949        self.context_menu.addAction(self.actionQuick3DPlot) 
    947950        self.context_menu.addAction(self.actionEditMask) 
     951        #self.context_menu.addSeparator() 
     952        #self.context_menu.addAction(self.actionFreezeResults) 
    948953        self.context_menu.addSeparator() 
    949954        self.context_menu.addAction(self.actionDelete) 
     
    957962        self.actionEditMask.triggered.connect(self.showEditDataMask) 
    958963        self.actionDelete.triggered.connect(self.deleteItem) 
     964        self.actionFreezeResults.triggered.connect(self.freezeSelectedItems) 
    959965 
    960966    def onCustomContextMenu(self, position): 
     
    977983        self.actionQuick3DPlot.setEnabled(is_2D) 
    978984        self.actionEditMask.setEnabled(is_2D) 
     985 
     986        # Freezing 
     987        # check that the selection has inner items 
     988        freeze_enabled = False 
     989        if model_item.parent() is not None: 
     990            freeze_enabled = True 
     991        self.actionFreezeResults.setEnabled(freeze_enabled) 
     992 
    979993        # Fire up the menu 
    980994        self.context_menu.exec_(self.current_view.mapToGlobal(position)) 
     
    11121126        mask_editor.exec_() 
    11131127 
     1128    def freezeItem(self, item=None): 
     1129        """ 
     1130        Freeze given item 
     1131        """ 
     1132        if item is None: 
     1133            return 
     1134        self.model.beginResetModel() 
     1135        new_item = self.cloneTheory(item) 
     1136        self.model.appendRow(new_item) 
     1137        self.model.endResetModel() 
     1138 
     1139    def freezeSelectedItems(self): 
     1140        """ 
     1141        Freeze selected items 
     1142        """ 
     1143        indices = self.treeView.selectedIndexes() 
     1144 
     1145        proxy = self.treeView.model() 
     1146        model = proxy.sourceModel() 
     1147 
     1148        for index in indices: 
     1149            row_index = proxy.mapToSource(index) 
     1150            item_to_copy = model.itemFromIndex(row_index) 
     1151            if item_to_copy and item_to_copy.isCheckable(): 
     1152                self.freezeItem(item_to_copy) 
     1153 
    11141154    def deleteItem(self): 
    11151155        """ 
     
    12721312 
    12731313        # Caption for the theories 
    1274         checkbox_item.setChild(2, QtGui.QStandardItem("THEORIES")) 
     1314        checkbox_item.setChild(2, QtGui.QStandardItem("FIT RESULTS")) 
    12751315 
    12761316        # New row in the model 
  • src/sas/qtgui/MainWindow/DataManager.py

    r4e255d1 ra54bbf2b  
    118118        new_plot.path = path 
    119119        new_plot.list_group_id = [] 
     120        # Assign the plot role to data 
     121        new_plot.plot_role = Data1D.ROLE_DATA 
    120122        ##post data to plot 
    121123        # plot data 
  • src/sas/qtgui/MainWindow/UI/DataExplorerUI.ui

    rf4a6f2c r33b3e4d  
    508508   </property> 
    509509  </action> 
     510  <action name="actionFreezeResults"> 
     511   <property name="text"> 
     512    <string>Freeze Results</string> 
     513   </property> 
     514  </action> 
    510515 </widget> 
    511516 <resources/> 
  • src/sas/qtgui/MainWindow/UI/MainWindowUI.ui

    r2f14b5d r33b3e4d  
    415415   </property> 
    416416  </action> 
    417     <action name="actionEditMask"> 
     417  <action name="actionEditMask"> 
    418418   <property name="text"> 
    419419    <string>Edit Mask</string> 
     
    549549  <action name="actionFreeze_Theory"> 
    550550   <property name="text"> 
    551     <string>Freeze Theory</string> 
     551    <string>Freeze Fit Results</string> 
    552552   </property> 
    553553  </action> 
  • src/sas/qtgui/Perspectives/Fitting/FittingLogic.py

    r61f0c75 ra54bbf2b  
    154154        new_plot.xaxis(_xaxis, _xunit) 
    155155        new_plot.yaxis(_yaxis, _yunit) 
     156 
     157        if component is not None: 
     158            new_plot.plot_role = Data1D.ROLE_DELETABLE #deletable 
    156159 
    157160        return new_plot 
  • src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py

    rf3cc979 r8b745c36  
    128128 
    129129            # Find param in volume_params 
    130             poly_pars = parameters.form_volume_parameters 
     130            poly_pars = copy.deepcopy(parameters.form_volume_parameters) 
    131131            if is2D: 
    132132                poly_pars += parameters.orientation_parameters 
  • src/sas/qtgui/Perspectives/Fitting/FittingWidget.py

    rf3cc979 r6889ba2  
    273273        self.theory_item = None 
    274274 
     275        # list column widths 
     276        self.lstParamHeaderSizes = {} 
     277 
    275278        # signal communicator 
    276279        self.communicate = self.parent.communicate 
     
    361364        self.lstParams.customContextMenuRequested.connect(self.showModelContextMenu) 
    362365        self.lstParams.setAttribute(QtCore.Qt.WA_MacShowFocusRect, False) 
     366        # Column resize signals 
     367        self.lstParams.header().sectionResized.connect(self.onColumnWidthUpdate) 
     368 
    363369        # Poly model displayed in poly list 
    364370        self.lstPoly.setModel(self._poly_model) 
     
    11081114        self.SASModelToQModel(model, structure_factor) 
    11091115 
     1116        for column, width in self.lstParamHeaderSizes.items(): 
     1117            self.lstParams.setColumnWidth(column, width) 
     1118 
    11101119        # Update plot 
    11111120        self.updateData() 
     
    16691678        self._model_model.itemChanged.connect(self.onMainParamsChange) 
    16701679 
    1671         # Adjust the table cells width. 
    1672         # TODO: find a way to dynamically adjust column width while resized expanding 
    1673         self.lstParams.resizeColumnToContents(0) 
    1674         self.lstParams.resizeColumnToContents(4) 
    1675         self.lstParams.resizeColumnToContents(5) 
    1676         self.lstParams.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding) 
    1677  
    16781680    def iterateOverPolyModel(self, func): 
    16791681        """ 
     
    20142016            self.magnet_params = {} 
    20152017            self.setMagneticModel() 
    2016  
    2017         # Adjust the table cells width 
    2018         self.lstParams.resizeColumnToContents(0) 
    2019         self.lstParams.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding) 
    20202018 
    20212019        # Now we claim the model has been loaded 
     
    23422340 
    23432341        # add polydisperse parameters if asked 
    2344         if self.chkPolydispersity.isChecked(): 
     2342        if self.chkPolydispersity.isChecked() and self._poly_model.rowCount() > 0: 
    23452343            for key, value in self.poly_params.items(): 
    23462344                model.setParam(key, value) 
    23472345        # add magnetic params if asked 
    23482346        if self.chkMagnetism.isChecked(): 
    2349             for key, value in self.magnet_params.items(): 
     2347            for key, value in self.magnet_params.items() and self._magnet_model.rowCount() > 0: 
    23502348                model.setParam(key, value) 
    23512349 
     
    24812479        residuals_plot = FittingUtilities.plotResiduals(self.data, weighted_data) 
    24822480        residuals_plot.id = "Residual " + residuals_plot.id 
     2481        residuals_plot.plot_role = Data1D.ROLE_RESIDUAL 
    24832482        self.createNewIndex(residuals_plot) 
    24842483        return residuals_plot 
     
    27262725        fname_index = self._poly_model.index(row_index, self.lstPoly.itemDelegate().poly_filename) 
    27272726        self._poly_model.setData(fname_index, fname) 
     2727 
     2728    def onColumnWidthUpdate(self, index, old_size, new_size): 
     2729        """ 
     2730        Simple state update of the current column widths in the  param list 
     2731        """ 
     2732        self.lstParamHeaderSizes[index] = new_size 
    27282733 
    27292734    def setMagneticModel(self): 
  • src/sas/qtgui/Plotting/Plotter2D.py

    rfce6c55 r676a430  
    445445                                        self.ymin, self.ymax)) 
    446446 
    447             cbax = self.figure.add_axes([0.84, 0.2, 0.02, 0.7]) 
     447            cbax = self.figure.add_axes([0.88, 0.2, 0.02, 0.7]) 
    448448 
    449449            # Current labels for axes 
  • src/sas/qtgui/Plotting/PlotterData.py

    rcee5c78 ra54bbf2b  
    1717    """ 
    1818    """ 
     19    ROLE_DATA=0 
     20    ROLE_DEFAULT=1 
     21    ROLE_DELETABLE=2 
     22    ROLE_RESIDUAL=3 
    1923    def __init__(self, x=None, y=None, dx=None, dy=None): 
    2024        """ 
     
    3539        self.title = "" 
    3640        self.scale = None 
     41        # plot_role: 
     42        # 0: data - no reload on param change 
     43        # 1: normal lifecycle (fit) 
     44        # 2: deletable on model change (Q(I), S(I)...) 
     45        # 3: separate chart on Show Plot (residuals) 
     46        self.plot_role = Data1D.ROLE_DEFAULT 
    3747         
    3848    def copy_from_datainfo(self, data1d): 
     
    184194        self.title = "" 
    185195        self.scale = None 
     196        # Always default 
     197        self.plot_role = Data1D.ROLE_DEFAULT 
    186198         
    187199    def copy_from_datainfo(self, data2d): 
  • src/sas/qtgui/Utilities/GuiUtils.py

    r5d28d6b ra54bbf2b  
    322322    assert isinstance(item, QtGui.QStandardItem) 
    323323 
     324    # lists of plots names/ids for all deletable plots on item 
    324325    names = [p.name for p in new_plots if p.name is not None] 
    325326    ids = [p.id for p in new_plots if p.id is not None] 
     
    329330    for index in range(item.rowCount()): 
    330331        plot_item = item.child(index) 
    331         if plot_item.isCheckable(): 
    332             plot_data = plot_item.child(0).data() 
    333             if (plot_data.id is not None) and (plot_data.id not in ids) and (plot_data.name not in names): 
    334                 items_to_delete.append(plot_item) 
     332        if not plot_item.isCheckable(): 
     333            continue 
     334        plot_data = plot_item.child(0).data() 
     335        if (plot_data.id is not None) and \ 
     336            (plot_data.id not in ids) and \ 
     337            (plot_data.name not in names) and \ 
     338            (plot_data.plot_role == Data1D.ROLE_DELETABLE): 
     339            items_to_delete.append(plot_item) 
    335340 
    336341    for plot_item in items_to_delete: 
  • src/sas/qtgui/Perspectives/Inversion/DMaxExplorerWidget.py

    rb0ba43e re908916  
    4242        self.parent = parent 
    4343 
    44         self.setWindowTitle("Dₐₓ Explorer") 
     44        self.setWindowTitle("Dmax Explorer") 
    4545 
    4646        self.pr_state = pr_state 
     
    116116        bck = [] 
    117117        chi2 = [] 
    118  
     118        plotable_xs = [] #Introducing this to make sure size of x and y for plotting is the same.8 
    119119        try: 
    120120            dmin = float(self.model.item(W.DMIN).text()) 
     
    128128 
    129129        original = self.pr_state.d_max 
     130 
    130131        for x in xs: 
    131132            self.pr_state.d_max = x 
     
    140141                bck.append(self.pr_state.background) 
    141142                chi2.append(self.pr_state.chi2) 
     143                plotable_xs.append(x) 
    142144            except Exception as ex: 
    143145                # This inversion failed, skip this D_max value 
     
    188190            y_unit = "a.u." 
    189191 
    190         data = Data1D(xs, ys) 
     192        data = Data1D(plotable_xs, ys) 
    191193        if self.hasPlot: 
    192194            self.plot.removePlot(None) 
  • src/sas/qtgui/Perspectives/Inversion/InversionPerspective.py

    r2b8286c r28965e9  
    4343    estimateSignal = QtCore.pyqtSignal(tuple) 
    4444    estimateNTSignal = QtCore.pyqtSignal(tuple) 
     45    estimateDynamicNTSignal = QtCore.pyqtSignal(tuple) 
     46    estimateDynamicSignal = QtCore.pyqtSignal(tuple) 
    4547    calculateSignal = QtCore.pyqtSignal(tuple) 
    4648 
     
    194196        self.model.itemChanged.connect(self.model_changed) 
    195197        self.estimateNTSignal.connect(self._estimateNTUpdate) 
     198        self.estimateDynamicNTSignal.connect(self._estimateDynamicNTUpdate) 
     199        self.estimateDynamicSignal.connect(self._estimateDynamicUpdate) 
    196200        self.estimateSignal.connect(self._estimateUpdate) 
    197201        self.calculateSignal.connect(self._calculateUpdate) 
     202 
     203        self.maxDistanceInput.textEdited.connect(self.performEstimateDynamic) 
    198204 
    199205    def setupMapper(self): 
     
    309315                                            and not self.isCalculating) 
    310316        self.removeButton.setEnabled(self.logic.data_is_loaded) 
    311         self.explorerButton.setEnabled(self.logic.data_is_loaded 
    312                                        and np.all(self.logic.data.dy != 0)) 
     317        self.explorerButton.setEnabled(self.logic.data_is_loaded) 
    313318        self.stopButton.setVisible(self.isCalculating) 
    314319        self.regConstantSuggestionButton.setEnabled( 
     
    501506        self.dataPlot = self._dataList[data_ref].get(DICT_KEYS[2]) 
    502507        self.performEstimate() 
     508 
     509    def updateDynamicGuiValues(self): 
     510        pr = self._calculator 
     511        alpha = self._calculator.suggested_alpha 
     512        self.model.setItem(WIDGETS.W_MAX_DIST, 
     513                            QtGui.QStandardItem("{:.4g}".format(pr.get_dmax()))) 
     514        self.regConstantSuggestionButton.setText("{:-3.2g}".format(alpha)) 
     515        self.noOfTermsSuggestionButton.setText( 
     516             "{:n}".format(self.nTermsSuggested)) 
     517 
     518        self.enableButtons() 
    503519 
    504520    def updateGuiValues(self): 
     
    520536        self.model.setItem(WIDGETS.W_MAX_DIST, 
    521537                           QtGui.QStandardItem("{:.4g}".format(pr.get_dmax()))) 
    522         self.regConstantSuggestionButton.setText("{:-3.2g}".format(alpha)) 
    523         self.noOfTermsSuggestionButton.setText( 
    524             "{:n}".format(self.nTermsSuggested)) 
    525538 
    526539        if isinstance(pr.chi2, np.ndarray): 
     
    671684        self.estimationThreadNT.ready(2.5) 
    672685 
     686    def performEstimateDynamicNT(self): 
     687        """ 
     688        Perform parameter estimation 
     689        """ 
     690        from .Thread import EstimateNT 
     691 
     692        self.updateCalculator() 
     693 
     694        # If a thread is already started, stop it 
     695        self.stopEstimateNTThread() 
     696 
     697        pr = self._calculator.clone() 
     698        # Skip the slit settings for the estimation 
     699        # It slows down the application and it doesn't change the estimates 
     700        pr.slit_height = 0.0 
     701        pr.slit_width = 0.0 
     702        nfunc = self.getNFunc() 
     703 
     704        self.estimationThreadNT = EstimateNT(pr, nfunc, 
     705                                             error_func=self._threadError, 
     706                                             completefn=self._estimateDynamicNTCompleted, 
     707                                             updatefn=None) 
     708        self.estimationThreadNT.queue() 
     709        self.estimationThreadNT.ready(2.5) 
     710 
    673711    def stopEstimateNTThread(self): 
    674712        if (self.estimationThreadNT is not None and 
     
    693731        self.estimationThread.ready(2.5) 
    694732 
     733    def performEstimateDynamic(self): 
     734        """ 
     735            Perform parameter estimation 
     736        """ 
     737        from .Thread import EstimatePr 
     738 
     739        # If a thread is already started, stop it 
     740        self.stopEstimationThread() 
     741 
     742        self.estimationThread = EstimatePr(self._calculator.clone(), 
     743                                           self.getNFunc(), 
     744                                           error_func=self._threadError, 
     745                                           completefn=self._estimateDynamicCompleted, 
     746                                           updatefn=None) 
     747        self.estimationThread.queue() 
     748        self.estimationThread.ready(2.5) 
     749 
    695750    def stopEstimationThread(self): 
    696751        """ Stop the estimation thread if it exists and is running """ 
     
    705760        ''' Send a signal to the main thread for model update''' 
    706761        self.estimateSignal.emit((alpha, message, elapsed)) 
     762 
     763    def _estimateDynamicCompleted(self, alpha, message, elapsed): 
     764        ''' Send a signal to the main thread for model update''' 
     765        self.estimateDynamicSignal.emit((alpha, message, elapsed)) 
    707766 
    708767    def _estimateUpdate(self, output_tuple): 
     
    720779            logger.info(message) 
    721780        self.performEstimateNT() 
     781        self.performEstimateDynamicNT() 
     782 
     783    def _estimateDynamicUpdate(self, output_tuple): 
     784        """ 
     785        Parameter estimation completed, 
     786        display the results to the user 
     787 
     788        :param alpha: estimated best alpha 
     789        :param elapsed: computation time 
     790        """ 
     791        alpha, message, elapsed = output_tuple 
     792        self._calculator.alpha = alpha 
     793        self._calculator.elapsed += self._calculator.elapsed 
     794        if message: 
     795            logger.info(message) 
     796        self.performEstimateDynamicNT() 
    722797 
    723798    def _estimateNTCompleted(self, nterms, alpha, message, elapsed): 
    724799        ''' Send a signal to the main thread for model update''' 
    725800        self.estimateNTSignal.emit((nterms, alpha, message, elapsed)) 
     801 
     802    def _estimateDynamicNTCompleted(self, nterms, alpha, message, elapsed): 
     803        ''' Send a signal to the main thread for model update''' 
     804        self.estimateDynamicNTSignal.emit((nterms, alpha, message, elapsed)) 
    726805 
    727806    def _estimateNTUpdate(self, output_tuple): 
     
    747826            self.startThread() 
    748827 
     828    def _estimateDynamicNTUpdate(self, output_tuple): 
     829        """ 
     830        Parameter estimation completed, 
     831        display the results to the user 
     832 
     833        :param alpha: estimated best alpha 
     834        :param nterms: estimated number of terms 
     835        :param elapsed: computation time 
     836        """ 
     837        nterms, alpha, message, elapsed = output_tuple 
     838        self._calculator.elapsed += elapsed 
     839        self._calculator.suggested_alpha = alpha 
     840        self.nTermsSuggested = nterms 
     841        # Save useful info 
     842        self.updateDynamicGuiValues() 
     843        if message: 
     844            logger.info(message) 
     845        if self.isBatch: 
     846            self.acceptAlpha() 
     847            self.acceptNoTerms() 
     848            self.startThread() 
     849 
    749850    def _calculateCompleted(self, out, cov, pr, elapsed): 
    750851        ''' Send a signal to the main thread for model update''' 
  • src/sas/sascalc/pr/invertor.py

    rb8080e1 r6701a0b  
    7171        A[j][i] = (Fourier transformed base function for point j) 
    7272 
    73     We them choose a number of r-points, n_r, to evaluate the second 
     73    We then choose a number of r-points, n_r, to evaluate the second 
    7474    derivative of P(r) at. This is used as our regularization term. 
    7575    For a vector r of length n_r, the following n_r rows are set to :: 
     
    144144        x, y, err, d_max, q_min, q_max and alpha 
    145145        """ 
    146         if   name == 'x': 
     146        if name == 'x': 
    147147            if 0.0 in value: 
    148148                msg = "Invertor: one of your q-values is zero. " 
     
    227227        return None 
    228228 
     229    def add_errors(self, yvalues): 
     230        """ 
     231        Adds errors to data set is they are not avaialble 
     232        :return: 
     233        """ 
     234        stats_errors = np.zeros(len(yvalues)) 
     235        for i in range(len(yvalues)): 
     236            # Scale the error so that we can fit over several decades of Q 
     237            scale = 0.05 * np.sqrt(yvalues[i]) 
     238            min_err = 0.01 * yvalues[i] 
     239            stats_errors[i] = scale * np.sqrt(np.fabs(yvalues[i])) + min_err 
     240        logger.warning("Simulated errors have been added to the data set\n") 
     241        return stats_errors 
     242 
    229243    def clone(self): 
    230244        """ 
     
    244258        invertor.x = self.x 
    245259        invertor.y = self.y 
    246         invertor.err = self.err 
     260        if np.size(self.err) == 0 or np.all(self.err) == 0: 
     261            invertor.err = self.add_errors(self.y) 
     262        else: 
     263            invertor.err = self.err 
    247264        invertor.est_bck = self.est_bck 
    248265        invertor.background = self.background 
     
    268285            A[i][j] = (Fourier transformed base function for point j) 
    269286 
    270         We them choose a number of r-points, n_r, to evaluate the second 
     287        We then choose a number of r-points, n_r, to evaluate the second 
    271288        derivative of P(r) at. This is used as our regularization term. 
    272289        For a vector r of length n_r, the following n_r rows are set to :: 
Note: See TracChangeset for help on using the changeset viewer.