Changeset 30bed93 in sasview


Ignore:
Timestamp:
Sep 21, 2018 5:41:31 AM (3 months ago)
Author:
GitHub <noreply@…>
Branches:
ESS_GUI, ESS_GUI_Invariant, ESS_GUI_batch_fitting, ESS_GUI_ordering
Children:
c928e81
Parents:
33d5956 (diff), d8d81ea (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
git-author:
Ingo Breßler <dev@…> (09/21/18 05:41:31)
git-committer:
GitHub <noreply@…> (09/21/18 05:41:31)
Message:

Merge pull request #181 from SasView?/ESS_GUI_poly_plot2

plot polydispersity (SASVIEW-1035 and trac ticket 17)

Location:
src/sas
Files:
27 edited

Legend:

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

    r9ce69ec r30bed93  
    579579            # Don't plot intermediate results, e.g. P(Q), S(Q) 
    580580            match = GuiUtils.theory_plot_ID_pattern.match(plot.id) 
    581             # 2nd match group contains the identifier for the intermediate result, if present (e.g. "[P(Q)]") 
     581            # 2nd match group contains the identifier for the intermediate 
     582            # result, if present (e.g. "[P(Q)]") 
    582583            if match and match.groups()[1] != None: 
    583584                continue 
    584             # Don't include plots from different fitpages, but always include the original data 
    585             if fitpage_name in plot.name or filename in plot.name or filename == plot.filename: 
     585            # Don't include plots from different fitpages, 
     586            # but always include the original data 
     587            if (fitpage_name in plot.name 
     588                    or filename in plot.name 
     589                    or filename == plot.filename): 
    586590                # Residuals get their own plot 
    587591                if plot.plot_role == Data1D.ROLE_RESIDUAL: 
  • src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py

    r8faac15 r30bed93  
    570570    return residuals 
    571571 
     572def plotPolydispersities(model): 
     573    plots = [] 
     574    if model is None: 
     575        return plots 
     576    # test for model being a sasmodels.sasview_model.SasviewModel? 
     577    for name in model.dispersion.keys(): 
     578        xarr, yarr = model.get_weights(name) 
     579        if len(xarr) <= 1: # param name not found or no polydisp. 
     580            continue 
     581        # create Data1D as in residualsData1D() and fill x/y members 
     582        # similar to FittingLogic._create1DPlot() but different data/axes 
     583        data1d = Data1D(x=xarr, y=yarr) 
     584        xunit = model.details[name][0] 
     585        data1d.xaxis(r'\rm{{{}}}'.format(name.replace('_', '\_')), xunit) 
     586        data1d.yaxis(r'\rm{weight}', 'normalized') 
     587        data1d.scale = 'linear' 
     588        data1d.symbol = 'Line' 
     589        data1d.name = "{} polydispersity".format(name) 
     590        data1d.id = data1d.name # placeholder, has to be completed later 
     591        data1d.plot_role = Data1D.ROLE_RESIDUAL 
     592        plots.append(data1d) 
     593    return plots 
     594 
    572595def binary_encode(i, digits): 
    573596    return [i >> d & 1 for d in range(digits)] 
  • src/sas/qtgui/Perspectives/Fitting/FittingWidget.py

    r33d5956 r30bed93  
    5858DEFAULT_POLYDISP_FUNCTION = 'gaussian' 
    5959 
     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 
    6073 
    6174logger = logging.getLogger(__name__) 
     
    24882501 
    24892502        if self.data_is_loaded: 
    2490             # delete any plots associated with the data that were not updated (e.g. to remove beta(Q), S_eff(Q)) 
     2503            # delete any plots associated with the data that were not updated 
     2504            # (e.g. to remove beta(Q), S_eff(Q)) 
    24912505            GuiUtils.deleteRedundantPlots(self.all_data[self.data_index], new_plots) 
    24922506            pass 
    24932507        else: 
    2494             # delete theory items for the model, in order to get rid of any redundant items, e.g. beta(Q), S_eff(Q) 
     2508            # delete theory items for the model, in order to get rid of any 
     2509            # redundant items, e.g. beta(Q), S_eff(Q) 
    24952510            self.communicate.deleteIntermediateTheoryPlotsSignal.emit(self.kernel_module.id) 
     2511 
     2512        # Create plots for parameters with enabled polydispersity 
     2513        for plot in FittingUtilities.plotPolydispersities(return_data.get('model', None)): 
     2514            data_id = fitted_data.id.split() 
     2515            plot.id = "{} [{}] {}".format(data_id[0], plot.name, " ".join(data_id[1:])) 
     2516            data_name = fitted_data.name.split() 
     2517            plot.name = " ".join([data_name[0], plot.name] + data_name[1:]) 
     2518            self.createNewIndex(plot) 
     2519            new_plots.append(plot) 
    24962520 
    24972521        # Create plots for intermediate product data 
  • src/sas/qtgui/Calculators/UnitTesting/SLDCalculatorTest.py

    r144fe21 r9ce69ec  
    8181        self.assertEqual(self.widget.ui.editMolecularFormula.styleSheet(), '') 
    8282        self.assertEqual(self.widget.model.columnCount(), 1) 
    83         self.assertEqual(self.widget.model.rowCount(), 12) 
     83        self.assertEqual(self.widget.model.rowCount(), 11) 
    8484        self.assertEqual(self.widget.sizePolicy().Policy(), QtWidgets.QSizePolicy.Fixed) 
    8585 
    8686    def testSimpleEntry(self): 
    8787        ''' Default compound calculations ''' 
    88  
    89         self.widget.show() 
    9088 
    9189        self.widget.ui.editMassDensity.clear() 
     
    102100 
    103101        # Change mass density 
    104         self.widget.ui.editWavelength.clear() 
    105         self.widget.ui.editWavelength.setText("666.0") 
     102        self.widget.ui.editNeutronWavelength.clear() 
     103        self.widget.ui.editNeutronWavelength.setText("666.0") 
    106104 
    107105        # Send shift-tab to update the molar volume field 
     
    130128 
    131129        # Assure the mass density field is set 
    132         self.assertEqual(self.widget.ui.editNeutronIncXs.text(), '43.4') 
     130        #self.assertEqual(self.widget.ui.editNeutronIncXs.text(), '43.4') 
     131        self.assertEqual(self.widget.ui.editNeutronIncXs.text(), '2.89') 
    133132 
    134133        # Reset the widget 
    135134        self.widget.modelReset() 
    136  
     135        
    137136        self.assertEqual(self.widget.ui.editMolecularFormula.text(), "H2O") 
    138137        self.assertEqual(self.widget.ui.editMassDensity.text(), "1") 
  • src/sas/qtgui/GUITests.py

    r7dd309a rccd2b87  
    180180        unittest.makeSuite(InvariantPerspectiveTest.InvariantPerspectiveTest,  'test'), 
    181181        #  Inversion 
    182         #unittest.makeSuite(InversionPerspectiveTest.InversionTest,  'test'), 
     182        unittest.makeSuite(InversionPerspectiveTest.InversionTest,  'test'), 
    183183        ) 
    184184    return unittest.TestSuite(suites) 
  • src/sas/qtgui/MainWindow/GuiManager.py

    r768387e0 rdee9e5f  
    1212from twisted.internet import reactor 
    1313# General SAS imports 
     14from sas import get_local_config, get_custom_config 
    1415from sas.qtgui.Utilities.ConnectionProxy import ConnectionProxy 
    1516from sas.qtgui.Utilities.SasviewLogger import setup_qt_logging 
     
    136137        self.aboutWidget = AboutBox() 
    137138        self.categoryManagerWidget = CategoryManager(self._parent, manager=self) 
    138         self.welcomePanel = WelcomePanel() 
    139139        self.grid_window = None 
    140140        self._workspace.toolBar.setVisible(LocalConfig.TOOLBAR_SHOW) 
     
    241241        perspective_width = perspective_size.width() 
    242242        self._current_perspective.resize(perspective_width, workspace_height-10) 
    243         # Resize the mdi area to match the widget within 
    244         subwindow.resize(subwindow.minimumSizeHint()) 
    245243 
    246244        self._current_perspective.show() 
     
    388386            self.communicate.statusBarUpdateSignal.emit(msg) 
    389387 
    390     def showWelcomeMessage(self): 
     388    def actionWelcome(self): 
    391389        """ Show the Welcome panel """ 
     390        self.welcomePanel = WelcomePanel() 
    392391        self._workspace.workspace.addSubWindow(self.welcomePanel) 
    393392        self.welcomePanel.show() 
     393 
     394    def showWelcomeMessage(self): 
     395        """ Show the Welcome panel, when required """ 
     396        # Assure the welcome screen is requested 
     397        show_welcome_widget = True 
     398        custom_config = get_custom_config() 
     399        if hasattr(custom_config, "WELCOME_PANEL_SHOW"): 
     400            if isinstance(custom_config.WELCOME_PANEL_SHOW, bool): 
     401                show_welcome_widget = custom_config.WELCOME_PANEL_SHOW 
     402            else: 
     403                logging.warning("WELCOME_PANEL_SHOW has invalid value in custom_config.py") 
     404        if show_welcome_widget: 
     405            self.actionWelcome() 
    394406 
    395407    def addCallbacks(self): 
     
    478490        self._workspace.actionAcknowledge.triggered.connect(self.actionAcknowledge) 
    479491        self._workspace.actionAbout.triggered.connect(self.actionAbout) 
     492        self._workspace.actionWelcomeWidget.triggered.connect(self.actionWelcome) 
    480493        self._workspace.actionCheck_for_update.triggered.connect(self.actionCheck_for_update) 
    481494 
     
    979992        When setting a perspective, sets up the menu bar 
    980993        """ 
     994        self._workspace.actionReport.setEnabled(False) 
    981995        if isinstance(perspective, Perspectives.PERSPECTIVES["Fitting"]): 
    982996            self.checkAnalysisOption(self._workspace.actionFitting) 
     
    9881002            self._workspace.menubar.addAction(self._workspace.menuWindow.menuAction()) 
    9891003            self._workspace.menubar.addAction(self._workspace.menuHelp.menuAction()) 
     1004            self._workspace.actionReport.setEnabled(True) 
    9901005 
    9911006        elif isinstance(perspective, Perspectives.PERSPECTIVES["Invariant"]): 
  • src/sas/qtgui/MainWindow/UI/MainWindowUI.ui

    r768387e0 rfc5d2d7f  
    148148    <addaction name="separator"/> 
    149149    <addaction name="actionAbout"/> 
     150    <addaction name="actionWelcomeWidget"/> 
     151    <addaction name="separator"/> 
    150152    <addaction name="actionCheck_for_update"/> 
    151153   </widget> 
     
    558560   </property> 
    559561  </action> 
     562  <action name="actionWelcomeWidget"> 
     563   <property name="text"> 
     564    <string>Welcome to SasView</string> 
     565   </property> 
     566  </action> 
    560567 </widget> 
    561568 <resources/> 
  • src/sas/qtgui/Perspectives/Fitting/FittingOptions.py

    rff3b293 r8873ab7  
    6464        default_index = self.cbAlgorithm.findText(default_name) 
    6565        self.cbAlgorithm.setCurrentIndex(default_index) 
     66        # previous algorithm choice 
     67        self.previous_index = default_index 
    6668 
    6769        # Assign appropriate validators 
     
    121123 
    122124        # Convert the name into widget instance 
    123         widget_to_activate = eval(widget_name) 
     125        try: 
     126            widget_to_activate = eval(widget_name) 
     127        except AttributeError: 
     128            # We don't yet have this optimizer. 
     129            # Show message 
     130            msg = "This algorithm has not yet been implemented in SasView.\n" 
     131            msg += "Please choose a different algorithm" 
     132            QtWidgets.QMessageBox.warning(self, 
     133                                        'Warning', 
     134                                        msg, 
     135                                        QtWidgets.QMessageBox.Ok) 
     136            # Move the index to previous position 
     137            self.cbAlgorithm.setCurrentIndex(self.previous_index) 
     138            return 
     139 
    124140        index_for_this_id = self.stackedWidget.indexOf(widget_to_activate) 
    125141 
     
    133149        # OK has to be reinitialized to True 
    134150        self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(True) 
     151 
     152        # keep reference 
     153        self.previous_index = index 
    135154 
    136155    def onApply(self): 
     
    143162            # e.g. 'samples' for 'dream' is 'self.samples_dream' 
    144163            widget_name = 'self.'+option+'_'+self.current_fitter_id 
    145             line_edit = eval(widget_name) 
     164            try: 
     165                line_edit = eval(widget_name) 
     166            except AttributeError: 
     167                # Skip bumps monitors 
     168                continue 
    146169            if line_edit is None or not isinstance(line_edit, QtWidgets.QLineEdit): 
    147170                continue 
     
    165188                return 
    166189            try: 
    167                 new_value = widget.currentText() if isinstance(widget, QtWidgets.QComboBox) \ 
    168                     else float(widget.text()) 
     190                if isinstance(widget, QtWidgets.QComboBox): 
     191                    new_value = widget.currentText() 
     192                else: 
     193                    try: 
     194                        new_value = int(widget.text()) 
     195                    except ValueError: 
     196                        new_value = float(widget.text()) 
     197                #new_value = widget.currentText() if isinstance(widget, QtWidgets.QComboBox) \ 
     198                #    else float(widget.text()) 
    169199                self.config.values[self.current_fitter_id][option] = new_value 
    170200            except ValueError: 
  • src/sas/qtgui/Perspectives/Fitting/UI/ComplexConstraintUI.ui

    r305114c r1738173  
    77    <x>0</x> 
    88    <y>0</y> 
    9     <width>463</width> 
    10     <height>234</height> 
     9    <width>367</width> 
     10    <height>199</height> 
    1111   </rect> 
    1212  </property> 
     
    135135    <widget class="QLabel" name="lblWarning"> 
    136136     <property name="text"> 
    137       <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; color:#ff0000;&quot;&gt;Warning! Polydisperse parameter selected.&lt;br/&gt;&lt;/span&gt;Constraints involving polydisperse parameters only apply to&lt;br/&gt;starting values and are nor re-applied during size or angle polydispersity&lt;br/&gt;integrations. To do which requires creating a custom model.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> 
     137      <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; color:#ff0000;&quot;&gt;Warning! Polydisperse parameter selected.&lt;br/&gt;&lt;/span&gt;Constraints involving polydisperse parameters only apply to&lt;br/&gt;starting values and are not re-applied during size or angle polydispersity&lt;br/&gt;integrations. To do so requires creating a custom model.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> 
    138138     </property> 
    139139    </widget> 
  • src/sas/qtgui/Perspectives/Fitting/UI/FittingWidgetUI.ui

    ra2e8ea5 rfc5d2d7f  
    77    <x>0</x> 
    88    <y>0</y> 
    9     <width>566</width> 
     9    <width>651</width> 
    1010    <height>646</height> 
    1111   </rect> 
  • src/sas/qtgui/Perspectives/Fitting/UI/MultiConstraintUI.ui

    raa88b76 r1738173  
    1010    <x>0</x> 
    1111    <y>0</y> 
    12     <width>436</width> 
    13     <height>217</height> 
     12    <width>369</width> 
     13    <height>201</height> 
    1414   </rect> 
    1515  </property> 
     
    121121    <widget class="QLabel" name="lblWarning"> 
    122122     <property name="text"> 
    123       <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; color:#ff0000;&quot;&gt;Warning! Polydisperse parameter selected.&lt;br/&gt;&lt;/span&gt;Constraints involving polydisperse parameters only apply to&lt;br/&gt;starting values and are nor re-applied during size or angle polydispersity&lt;br/&gt;integrations. To do which requires creating a custom model.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> 
     123      <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; color:#ff0000;&quot;&gt;Warning! Polydisperse parameter selected.&lt;br/&gt;&lt;/span&gt;Constraints involving polydisperse parameters only apply to&lt;br/&gt;starting values and are not re-applied during size or angle polydispersity&lt;br/&gt;integrations. To do so requires creating a custom model.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> 
    124124     </property> 
    125125    </widget> 
  • src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingWidgetTest.py

    r1a15ada r8faac15  
    397397        header_tooltips = ['Select parameter for fitting', 
    398398                            'Enter polydispersity ratio (Std deviation/mean).\n'+ 
    399                             'For angles this can be either std deviation or full width (for uniform distributions) in degrees', 
    400                             'STD: standard deviation from the mean value', 
     399                            'For angles this can be either std deviation or half width (for uniform distributions) in degrees', 
    401400                            'Enter minimum value for parameter', 
    402401                            'Enter maximum value for parameter', 
     
    620619        # Set to 0 
    621620        self.widget.lstParams.indexWidget(func_index).setCurrentIndex(0) 
    622         self.assertEqual(self.widget._model_model.rowCount(), last_row - 2) # 2 fewer rows than default 
     621        self.assertEqual(self.widget._model_model.rowCount(), last_row - 2) 
    623622 
    624623    def testPlotTheory(self): 
     
    658657        self.assertEqual(spy.count(), 0) 
    659658 
    660     def testPlotData(self): 
     659    def notestPlotData(self): 
    661660        """ 
    662661        See that data item can produce a chart 
  • src/sas/qtgui/Perspectives/Invariant/InvariantPerspective.py

    raed0532 rdee9e5f  
    233233 
    234234        plot_data = GuiUtils.plotsFromCheckedItems(self._manager.filesWidget.model) 
     235        #self.communicate.plotRequestedSignal.emit([item, plot], self.tab_id) 
    235236 
    236237        self._manager.filesWidget.plotData(plot_data) 
     
    347348                extrapolated_data.name = title 
    348349                extrapolated_data.title = title 
     350                extrapolated_data.style = "Line" 
     351                extrapolated_data.has_errors = False 
     352                extrapolated_data.plot_role = Data1D.ROLE_DEFAULT 
    349353 
    350354                # copy labels and units of axes for plotting 
     
    378382                high_out_data.name = title 
    379383                high_out_data.title = title 
     384                high_out_data.style = "Line" 
     385                high_out_data.has_errors = False 
     386                high_out_data.plot_role = Data1D.ROLE_DEFAULT 
    380387 
    381388                # copy labels and units of axes for plotting 
  • src/sas/qtgui/Perspectives/Inversion/DMaxExplorerWidget.py

    rb0ba43e r9f2f713  
    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 
     
    155157            logger.error(msg) 
    156158 
    157         plotter = self.model.item(W.VARIABLE).text() 
    158         y_label = y_unit = "" 
     159        plotter = self.dependentVariable.currentText() 
    159160        x_label = "D_{max}" 
    160161        x_unit = "A" 
     
    188189            y_unit = "a.u." 
    189190 
    190         data = Data1D(xs, ys) 
     191        data = Data1D(plotable_xs, ys) 
    191192        if self.hasPlot: 
    192             self.plot.removePlot(None) 
     193            self.plot.removePlot(data.name) 
    193194        self.hasPlot = True 
    194195        data.title = plotter 
  • src/sas/qtgui/Perspectives/Inversion/InversionLogic.py

    r6da860a r3c4f02e  
    111111            new_plot.title = title 
    112112 
     113        new_plot.symbol = 'Line' 
     114        new_plot.hide_error = True 
     115 
    113116        return new_plot 
    114117 
  • src/sas/qtgui/Perspectives/Inversion/InversionPerspective.py

    r855e7ad rdee9e5f  
    4444    estimateSignal = QtCore.pyqtSignal(tuple) 
    4545    estimateNTSignal = QtCore.pyqtSignal(tuple) 
     46    estimateDynamicNTSignal = QtCore.pyqtSignal(tuple) 
     47    estimateDynamicSignal = QtCore.pyqtSignal(tuple) 
    4648    calculateSignal = QtCore.pyqtSignal(tuple) 
    4749 
     
    5355 
    5456        self._manager = parent 
     57        #Needed for Batch fitting 
     58        self._parent = parent 
    5559        self.communicate = parent.communicator() 
    5660        self.communicate.dataDeletedSignal.connect(self.removeData) 
     
    110114        # Set up the Widget Map 
    111115        self.setupMapper() 
     116 
     117        #Hidding calculate all buton 
     118        self.calculateAllButton.setVisible(False) 
    112119        # Set base window state 
    113120        self.setupWindow() 
     
    120127 
    121128    def allowBatch(self): 
    122         return True 
     129        return False 
    123130 
    124131    def setClosable(self, value=True): 
     
    195202        self.model.itemChanged.connect(self.model_changed) 
    196203        self.estimateNTSignal.connect(self._estimateNTUpdate) 
     204        self.estimateDynamicNTSignal.connect(self._estimateDynamicNTUpdate) 
     205        self.estimateDynamicSignal.connect(self._estimateDynamicUpdate) 
    197206        self.estimateSignal.connect(self._estimateUpdate) 
    198207        self.calculateSignal.connect(self._calculateUpdate) 
     208 
     209        self.maxDistanceInput.textEdited.connect(self.performEstimateDynamic) 
    199210 
    200211    def setupMapper(self): 
     
    310321                                            and not self.isCalculating) 
    311322        self.removeButton.setEnabled(self.logic.data_is_loaded) 
    312         self.explorerButton.setEnabled(self.logic.data_is_loaded 
    313                                        and np.all(self.logic.data.dy != 0)) 
     323        self.explorerButton.setEnabled(self.logic.data_is_loaded) 
    314324        self.stopButton.setVisible(self.isCalculating) 
    315325        self.regConstantSuggestionButton.setEnabled( 
     
    454464            # Create initial internal mappings 
    455465            self.logic.data = GuiUtils.dataFromItem(data) 
     466            if not isinstance(self.logic.data, Data1D): 
     467                msg = "P(r) perspective works for 1D data only" 
     468                logger.warning(msg) 
     469                continue 
    456470            # Estimate q range 
    457471            qmin, qmax = self.logic.computeDataRange() 
    458472            self._calculator.set_qmin(qmin) 
    459473            self._calculator.set_qmax(qmax) 
     474            if np.size(self.logic.data.dy) == 0 or np.all(self.logic.data.dy) == 0: 
     475                self.logic.data.dy = self._calculator.add_errors(self.logic.data.y) 
    460476            self.updateDataList(data) 
    461477            self.populateDataComboBox(self.logic.data.filename, data) 
    462478        self.dataList.setCurrentIndex(len(self.dataList) - 1) 
    463         self.setCurrentData(data) 
     479        #Checking for 1D again to mitigate the case when 2D data is last on the data list 
     480        if isinstance(self.logic.data, Data1D): 
     481            self.setCurrentData(data) 
    464482 
    465483    def updateDataList(self, dataRef): 
     
    502520        self.dataPlot = self._dataList[data_ref].get(DICT_KEYS[2]) 
    503521        self.performEstimate() 
     522 
     523    def updateDynamicGuiValues(self): 
     524        pr = self._calculator 
     525        alpha = self._calculator.suggested_alpha 
     526        self.model.setItem(WIDGETS.W_MAX_DIST, 
     527                            QtGui.QStandardItem("{:.4g}".format(pr.get_dmax()))) 
     528        self.regConstantSuggestionButton.setText("{:-3.2g}".format(alpha)) 
     529        self.noOfTermsSuggestionButton.setText( 
     530             "{:n}".format(self.nTermsSuggested)) 
     531 
     532        self.enableButtons() 
    504533 
    505534    def updateGuiValues(self): 
     
    521550        self.model.setItem(WIDGETS.W_MAX_DIST, 
    522551                           QtGui.QStandardItem("{:.4g}".format(pr.get_dmax()))) 
    523         self.regConstantSuggestionButton.setText("{:-3.2g}".format(alpha)) 
    524         self.noOfTermsSuggestionButton.setText( 
    525             "{:n}".format(self.nTermsSuggested)) 
    526552 
    527553        if isinstance(pr.chi2, np.ndarray): 
     
    547573            self.prPlot.plot_role = Data1D.ROLE_RESIDUAL 
    548574            GuiUtils.updateModelItemWithPlot(self._data, self.prPlot, title) 
     575            self.communicate.plotRequestedSignal.emit([self._data,self.prPlot], None) 
    549576        if self.dataPlot is not None: 
    550577            title = self.dataPlot.name 
    551578            self.dataPlot.plot_role = Data1D.ROLE_DEFAULT 
     579            self.dataPlot.symbol = "Line" 
     580            self.dataPlot.show_errors = False 
    552581            GuiUtils.updateModelItemWithPlot(self._data, self.dataPlot, title) 
    553         if self.dataPlot is not None or self.prPlot is not None: 
    554             self.communicate.plotRequestedSignal.emit([self.logic.data], None) 
     582            self.communicate.plotRequestedSignal.emit([self._data,self.dataPlot], None) 
    555583        self.enableButtons() 
    556584 
     
    636664 
    637665        pr = self._calculator.clone() 
    638         nfunc = self.getNFunc() 
    639         self.calcThread = CalcPr(pr, nfunc, 
     666        #Making sure that nfunc and alpha parameters are correctly initialized 
     667        pr.suggested_alpha = self._calculator.alpha 
     668        self.calcThread = CalcPr(pr, self.nTermsSuggested, 
    640669                                 error_func=self._threadError, 
    641670                                 completefn=self._calculateCompleted, 
     
    670699                                             error_func=self._threadError, 
    671700                                             completefn=self._estimateNTCompleted, 
     701                                             updatefn=None) 
     702        self.estimationThreadNT.queue() 
     703        self.estimationThreadNT.ready(2.5) 
     704 
     705    def performEstimateDynamicNT(self): 
     706        """ 
     707        Perform parameter estimation 
     708        """ 
     709        from .Thread import EstimateNT 
     710 
     711        self.updateCalculator() 
     712 
     713        # If a thread is already started, stop it 
     714        self.stopEstimateNTThread() 
     715 
     716        pr = self._calculator.clone() 
     717        # Skip the slit settings for the estimation 
     718        # It slows down the application and it doesn't change the estimates 
     719        pr.slit_height = 0.0 
     720        pr.slit_width = 0.0 
     721        nfunc = self.getNFunc() 
     722 
     723        self.estimationThreadNT = EstimateNT(pr, nfunc, 
     724                                             error_func=self._threadError, 
     725                                             completefn=self._estimateDynamicNTCompleted, 
    672726                                             updatefn=None) 
    673727        self.estimationThreadNT.queue() 
     
    696750        self.estimationThread.ready(2.5) 
    697751 
     752    def performEstimateDynamic(self): 
     753        """ 
     754            Perform parameter estimation 
     755        """ 
     756        from .Thread import EstimatePr 
     757 
     758        # If a thread is already started, stop it 
     759        self.stopEstimationThread() 
     760 
     761        self.estimationThread = EstimatePr(self._calculator.clone(), 
     762                                           self.getNFunc(), 
     763                                           error_func=self._threadError, 
     764                                           completefn=self._estimateDynamicCompleted, 
     765                                           updatefn=None) 
     766        self.estimationThread.queue() 
     767        self.estimationThread.ready(2.5) 
     768 
    698769    def stopEstimationThread(self): 
    699770        """ Stop the estimation thread if it exists and is running """ 
     
    708779        ''' Send a signal to the main thread for model update''' 
    709780        self.estimateSignal.emit((alpha, message, elapsed)) 
     781 
     782    def _estimateDynamicCompleted(self, alpha, message, elapsed): 
     783        ''' Send a signal to the main thread for model update''' 
     784        self.estimateDynamicSignal.emit((alpha, message, elapsed)) 
    710785 
    711786    def _estimateUpdate(self, output_tuple): 
     
    723798            logger.info(message) 
    724799        self.performEstimateNT() 
     800        self.performEstimateDynamicNT() 
     801 
     802    def _estimateDynamicUpdate(self, output_tuple): 
     803        """ 
     804        Parameter estimation completed, 
     805        display the results to the user 
     806 
     807        :param alpha: estimated best alpha 
     808        :param elapsed: computation time 
     809        """ 
     810        alpha, message, elapsed = output_tuple 
     811        self._calculator.alpha = alpha 
     812        self._calculator.elapsed += self._calculator.elapsed 
     813        if message: 
     814            logger.info(message) 
     815        self.performEstimateDynamicNT() 
    725816 
    726817    def _estimateNTCompleted(self, nterms, alpha, message, elapsed): 
    727818        ''' Send a signal to the main thread for model update''' 
    728819        self.estimateNTSignal.emit((nterms, alpha, message, elapsed)) 
     820 
     821    def _estimateDynamicNTCompleted(self, nterms, alpha, message, elapsed): 
     822        ''' Send a signal to the main thread for model update''' 
     823        self.estimateDynamicNTSignal.emit((nterms, alpha, message, elapsed)) 
    729824 
    730825    def _estimateNTUpdate(self, output_tuple): 
     
    750845            self.startThread() 
    751846 
     847    def _estimateDynamicNTUpdate(self, output_tuple): 
     848        """ 
     849        Parameter estimation completed, 
     850        display the results to the user 
     851 
     852        :param alpha: estimated best alpha 
     853        :param nterms: estimated number of terms 
     854        :param elapsed: computation time 
     855        """ 
     856        nterms, alpha, message, elapsed = output_tuple 
     857        self._calculator.elapsed += elapsed 
     858        self._calculator.suggested_alpha = alpha 
     859        self.nTermsSuggested = nterms 
     860        # Save useful info 
     861        self.updateDynamicGuiValues() 
     862        if message: 
     863            logger.info(message) 
     864        if self.isBatch: 
     865            self.acceptAlpha() 
     866            self.acceptNoTerms() 
     867            self.startThread() 
     868 
    752869    def _calculateCompleted(self, out, cov, pr, elapsed): 
    753870        ''' Send a signal to the main thread for model update''' 
  • src/sas/qtgui/Perspectives/Inversion/UI/DMaxExplorer.ui

    r8f83719f rcfd61f2  
    77    <x>0</x> 
    88    <y>0</y> 
    9     <width>394</width> 
    10     <height>426</height> 
     9    <width>586</width> 
     10    <height>538</height> 
    1111   </rect> 
    1212  </property> 
     
    2121       <property name="orientation"> 
    2222        <enum>Qt::Vertical</enum> 
    23        </property> 
    24        <property name="sizeHint" stdset="0"> 
    25         <size> 
    26          <width>20</width> 
    27          <height>305</height> 
    28         </size> 
    2923       </property> 
    3024      </spacer> 
  • src/sas/qtgui/Perspectives/Inversion/UI/TabbedInversionUI.ui

    r72ecbdf2 r68dc2873  
    77    <x>0</x> 
    88    <y>0</y> 
    9     <width>390</width> 
    10     <height>409</height> 
     9    <width>446</width> 
     10    <height>480</height> 
    1111   </rect> 
    1212  </property> 
     
    740740      <widget class="QPushButton" name="calculateAllButton"> 
    741741       <property name="enabled"> 
    742         <bool>true</bool> 
     742        <bool>false</bool> 
    743743       </property> 
    744744       <property name="sizePolicy"> 
  • src/sas/qtgui/Perspectives/Inversion/UnitTesting/InversionPerspectiveTest.py

    r144fe21 rccd2b87  
    9999    def baseBatchState(self): 
    100100        """ Testing the base batch fitting state """ 
    101         self.assertTrue(self.widget.allowBatch()) 
     101        self.assertFalse(self.widget.allowBatch()) 
    102102        self.assertFalse(self.widget.isBatch) 
    103103        self.assertFalse(self.widget.calculateAllButton.isEnabled()) 
     
    304304        self.assertIsNotNone(self.widget.dmaxWindow) 
    305305        self.assertTrue(self.widget.dmaxWindow.isVisible()) 
    306         self.assertTrue(self.widget.dmaxWindow.windowTitle() == "Dₐₓ Explorer") 
     306        self.assertTrue(self.widget.dmaxWindow.windowTitle() == "Dmax Explorer") 
    307307 
    308308 
  • src/sas/qtgui/Plotting/Plotter.py

    r5b144c6 r863ebca  
    88import numpy as np 
    99from matplotlib.font_manager import FontProperties 
     10 
    1011from sas.qtgui.Plotting.PlotterData import Data1D 
    1112from sas.qtgui.Plotting.PlotterBase import PlotterBase 
     
    8485            if self.data.ytransform is None: 
    8586                self.data.ytransform = 'log10(y)' 
    86  
     87            #Added condition to Dmax explorer from P(r) perspective 
     88            if self.data._xaxis == 'D_{max}': 
     89                self.xscale = 'linear' 
    8790            # Transform data if required. 
    8891            if transform and (self.data.xtransform is not None or self.data.ytransform is not None): 
     
    222225            self.contextMenu.addAction("Reset Graph Range") 
    223226        # Add the title change for dialogs 
    224         #if self.parent: 
    225227        self.contextMenu.addSeparator() 
    226228        self.actionWindowTitle = self.contextMenu.addAction("Window Title") 
  • src/sas/qtgui/Plotting/PlotterBase.py

    r343d7fd r863ebca  
    110110 
    111111        self.contextMenu = QtWidgets.QMenu(self) 
    112  
     112        self.toolbar = NavigationToolbar(self.canvas, self) 
     113        layout.addWidget(self.toolbar) 
    113114        if not quickplot: 
    114115            # Add the toolbar 
    115             self.toolbar = NavigationToolbar(self.canvas, self) 
    116             layout.addWidget(self.toolbar) 
     116            self.toolbar.show() 
    117117            # Notify PlotHelper about the new plot 
    118118            self.upatePlotHelper() 
     119        else: 
     120            self.toolbar.hide() 
    119121 
    120122        self.setLayout(layout) 
     
    219221        self.actionCopyToClipboard = self.contextMenu.addAction("Copy to Clipboard") 
    220222        self.contextMenu.addSeparator() 
     223        self.actionToggleMenu = self.contextMenu.addAction("Toggle Navigation Menu") 
     224        self.contextMenu.addSeparator() 
     225 
    221226 
    222227        # Define the callbacks 
     
    224229        self.actionPrintImage.triggered.connect(self.onImagePrint) 
    225230        self.actionCopyToClipboard.triggered.connect(self.onClipboardCopy) 
     231        self.actionToggleMenu.triggered.connect(self.onToggleMenu) 
    226232 
    227233    def createContextMenu(self): 
     
    371377        self.manager.communicator.activeGraphName.emit((current_title, title)) 
    372378 
     379    def onToggleMenu(self): 
     380        """ 
     381        Toggle navigation menu visibility in the chart 
     382        """ 
     383        if self.toolbar.isVisible(): 
     384            self.toolbar.hide() 
     385        else: 
     386            self.toolbar.show() 
     387 
    373388    def offset_graph(self): 
    374389        """ 
  • src/sas/qtgui/Plotting/UnitTesting/Plotter2DTest.py

    r144fe21 r863ebca  
    146146        self.plotter.createContextMenuQuick() 
    147147        actions = self.plotter.contextMenu.actions() 
    148         self.assertEqual(len(actions), 7) 
     148        self.assertEqual(len(actions), 9) 
    149149 
    150150        # Trigger Print Image and make sure the method is called 
     
    158158 
    159159        # Trigger Toggle Grid and make sure the method is called 
    160         self.assertEqual(actions[4].text(), "Toggle Grid On/Off") 
     160        self.assertEqual(actions[6].text(), "Toggle Grid On/Off") 
    161161        self.plotter.ax.grid = MagicMock() 
    162         actions[4].trigger() 
     162        actions[6].trigger() 
    163163        self.assertTrue(self.plotter.ax.grid.called) 
    164164 
    165165        # Trigger Change Scale and make sure the method is called 
    166         self.assertEqual(actions[6].text(), "Toggle Linear/Log Scale") 
    167         FigureCanvas.draw_idle = MagicMock() 
    168         actions[6].trigger() 
     166        self.assertEqual(actions[8].text(), "Toggle Linear/Log Scale") 
     167        FigureCanvas.draw_idle = MagicMock() 
     168        actions[8].trigger() 
    169169        self.assertTrue(FigureCanvas.draw_idle.called) 
    170170 
  • src/sas/qtgui/Plotting/UnitTesting/PlotterBaseTest.py

    r144fe21 r863ebca  
    8484        self.assertTrue(PlotHelper.deletePlot.called) 
    8585 
    86     def testOnImagePrint(self): 
     86    def notestOnImagePrint(self): 
    8787        ''' test the workspace print ''' 
    8888        QtGui.QPainter.end = MagicMock() 
     
    124124 
    125125        actions = self.plotter.contextMenu.actions() 
    126         self.assertEqual(len(actions), 4) 
     126        self.assertEqual(len(actions), 6) 
    127127 
    128128        # Trigger Print Image and make sure the method is called 
     
    146146        # Make sure clipboard got updated. 
    147147        self.assertTrue(self.clipboard_called) 
     148 
     149        # Trigger toggle navigation bar and make sure the method is called 
     150        self.assertEqual(actions[4].text(), "Toggle Navigation Menu") 
     151        isShown = self.plotter.toolbar.isVisible() 
     152        self.assertTrue(isShow) 
     153        actions[4].trigger() 
     154        isShown = self.plotter.toolbar.isVisible() 
     155        self.assertFalse(isShow) 
     156        actions[4].trigger() 
     157        isShown = self.plotter.toolbar.isVisible() 
     158        self.assertTrue(isShow) 
     159 
    148160 
    149161    def testOnWindowsTitle(self): 
  • src/sas/qtgui/Plotting/UnitTesting/PlotterTest.py

    r5b144c6 r863ebca  
    103103        self.plotter.createContextMenuQuick() 
    104104        actions = self.plotter.contextMenu.actions() 
    105         self.assertEqual(len(actions), 7) 
     105        self.assertEqual(len(actions), 9) 
    106106 
    107107        # Trigger Print Image and make sure the method is called 
     
    115115 
    116116        # Trigger Toggle Grid and make sure the method is called 
    117         self.assertEqual(actions[4].text(), "Toggle Grid On/Off") 
     117        self.assertEqual(actions[6].text(), "Toggle Grid On/Off") 
    118118        self.plotter.ax.grid = MagicMock() 
    119         actions[4].trigger() 
     119        actions[6].trigger() 
    120120        self.assertTrue(self.plotter.ax.grid.called) 
    121121 
    122122        # Trigger Change Scale and make sure the method is called 
    123         self.assertEqual(actions[6].text(), "Change Scale") 
     123        self.assertEqual(actions[8].text(), "Change Scale") 
    124124        self.plotter.properties.exec_ = MagicMock(return_value=QtWidgets.QDialog.Rejected) 
    125         actions[6].trigger() 
     125        actions[8].trigger() 
    126126        self.assertTrue(self.plotter.properties.exec_.called) 
    127127 
  • src/sas/qtgui/Utilities/GridPanel.py

    r3c6ecd9 r4fbf0db  
    5454        # Fill in the table from input data 
    5555        self.setupTable(widget=self.tblParams, data=output_data) 
     56        #TODO: This is not what was inteded to be. 
    5657        if output_data is not None: 
    5758            # Set a table tooltip describing the model 
    58             model_name = output_data[0][0].model.id 
     59            model_name = list(output_data.keys())[0] 
    5960            self.tabWidget.setTabToolTip(0, model_name) 
    6061 
     
    418419    def __init__(self, parent = None, output_data=None): 
    419420 
    420         super(BatchInversionOutputPanel, self).__init__(parent, output_data) 
     421        super(BatchInversionOutputPanel, self).__init__(parent._parent, output_data) 
    421422        _translate = QtCore.QCoreApplication.translate 
    422423        self.setWindowTitle(_translate("GridPanelUI", "Batch P(r) Results")) 
    423424 
    424     def setupTable(self, data): 
     425    def setupTable(self, widget=None,  data=None): 
    425426        """ 
    426427        Create tablewidget items and show them, based on params 
     
    433434                      'Calc. Time [sec]'] 
    434435 
     436        if data is None: 
     437            return 
    435438        keys = data.keys() 
    436439        rows = len(keys) 
  • src/sas/qtgui/Utilities/GuiUtils.py

    ra54bbf2b r9ce69ec  
    567567    The assumption - data stored in SasView standard, in child 0 
    568568    """ 
    569     return item.child(0).data() 
     569    try: 
     570        data = item.child(0).data() 
     571    except AttributeError: 
     572        data = None 
     573    return data 
    570574 
    571575def openLink(url): 
  • src/sas/sascalc/pr/invertor.py

    rb8080e1 reeea6a3  
    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        """ 
     
    268282            A[i][j] = (Fourier transformed base function for point j) 
    269283 
    270         We them choose a number of r-points, n_r, to evaluate the second 
     284        We then choose a number of r-points, n_r, to evaluate the second 
    271285        derivative of P(r) at. This is used as our regularization term. 
    272286        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.