Changeset d48cc19 in sasview


Ignore:
Timestamp:
May 2, 2017 9:04:48 AM (2 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:
02ddfb4
Parents:
0a6e097
Message:

Compute/Show? Plot button logic: SASVIEW-271
Unit tests for plotting in fitting: SASVIEW-501

Location:
src/sas/qtgui
Files:
6 edited

Legend:

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

    r83eb5208 rd48cc19  
    433433        self.chkBatch.setEnabled(self.parent.perspective().allowBatch()) 
    434434 
     435    def displayData(self, data_list): 
     436        """ 
     437        Forces display of charts for the given filename 
     438        """ 
     439        # Assure no multiple plots for the same ID 
     440        plot_to_show = data_list[0] 
     441        for plot in PlotHelper.currentPlots(): 
     442            if plot == plot_to_show.id: 
     443                return 
     444 
     445        new_plot = Plotter(self) 
     446        # Now query the model item for available plots 
     447        filename = plot_to_show.filename 
     448        model = self.model if plot_to_show.is_data else self.theory_model 
     449        plots = GuiUtils.plotsFromFilename(filename, model) 
     450        for plot in plots: 
     451            new_plot.plot(plot) 
     452        self.plotAdd(new_plot) 
     453 
    435454    def newPlot(self): 
    436455        """ 
  • src/sas/qtgui/MainWindow/GuiManager.py

    r28a09b0 rd48cc19  
    335335        self.communicate.perspectiveChangedSignal.connect(self.perspectiveChanged) 
    336336        self.communicate.updateTheoryFromPerspectiveSignal.connect(self.updateTheoryFromPerspective) 
     337        self.communicate.plotRequestedSignal.connect(self.showPlot) 
    337338 
    338339    def addTriggers(self): 
     
    727728        self.filesWidget.updateTheoryFromPerspective(index) 
    728729 
    729  
    730  
     730    def showPlot(self, plot): 
     731        """ 
     732        Pass the show plot request to the data explorer 
     733        """ 
     734        if hasattr(self, "filesWidget"): 
     735            self.filesWidget.displayData(plot) 
     736 
  • src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py

    r180bd54 rd48cc19  
    262262        residuals.y = -y 
    263263    else: 
     264        # TODO: fix case where applying new data from file on top of existing model data 
    264265        y = (fn - gn[index][0]) / en 
    265266        residuals.y = y 
  • src/sas/qtgui/Perspectives/Fitting/FittingWidget.py

    r61a92d4 rd48cc19  
    7474        self.weighting = 0 
    7575 
     76        # Data for chosen model 
     77        self.model_data = None 
     78 
    7679        # Which tab is this widget displayed in? 
    7780        self.tab_id = id 
     
    250253        """ 
    251254        self.cmdFit.setEnabled(False) 
    252         self.cmdPlot.setEnabled(True) 
     255        self.cmdPlot.setEnabled(False) 
    253256        self.options_widget.cmdComputePoints.setVisible(False) # probably redundant 
    254257        self.chkPolydispersity.setEnabled(True) 
     
    305308        self.has_error_column = False 
    306309 
     310        # Set enablement on calculate/plot 
     311        self.cmdPlot.setEnabled(True) 
     312 
    307313        # SasModel -> QModel 
    308314        self.SASModelToQModel(model) 
    309315 
    310316        if self.data_is_loaded: 
     317            self.cmdPlot.setText("Show Plot") 
    311318            self.calculateQGridForModel() 
    312319        else: 
     320            self.cmdPlot.setText("Calculate") 
    313321            # Create default datasets if no data passed 
    314322            self.createDefaultDataset() 
     
    569577        Plot the current set of data 
    570578        """ 
     579        # Regardless of previous state, this should now be `plot show` functionality only 
     580        self.cmdPlot.setText("Show Plot") 
     581        self.recalculatePlotData() 
     582        self.showPlot() 
     583 
     584    def recalculatePlotData(self): 
     585        """ 
     586        Generate a new dataset for model 
     587        """ 
    571588        if not self.data_is_loaded: 
    572589            self.createDefaultDataset() 
    573590        self.calculateQGridForModel() 
     591 
     592    def showPlot(self): 
     593        """ 
     594        Show the current plot in MPL 
     595        """ 
     596        # Show the chart if ready 
     597        data_to_show = self.data if self.data_is_loaded else self.model_data 
     598        if data_to_show is not None: 
     599            self.communicate.plotRequestedSignal.emit([data_to_show]) 
    574600 
    575601    def onOptionsUpdate(self): 
     
    582608        self.lblMinRangeDef.setText(str(self.q_range_min)) 
    583609        self.lblMaxRangeDef.setText(str(self.q_range_max)) 
    584         self.onPlot() 
     610        self.recalculatePlotData() 
    585611 
    586612    def setDefaultStructureCombo(self): 
     
    766792        # Force the chart update when actual parameters changed 
    767793        if model_column == 1: 
    768             self.onPlot() 
     794            self.recalculatePlotData() 
    769795 
    770796    def checkboxSelected(self, item): 
     
    891917        Plot the current 1D data 
    892918        """ 
    893         fitted_plot = self.logic.new1DPlot(return_data, self.tab_id) 
    894         self.calculateResiduals(fitted_plot) 
     919        fitted_data = self.logic.new1DPlot(return_data, self.tab_id) 
     920        self.calculateResiduals(fitted_data) 
     921        self.model_data = fitted_data 
    895922 
    896923    def complete2D(self, return_data): 
     
    900927        fitted_data = self.logic.new2DPlot(return_data) 
    901928        self.calculateResiduals(fitted_data) 
     929        self.model_data = fitted_data 
    902930 
    903931    def calculateResiduals(self, fitted_data): 
     
    914942        self.lblChi2Value.setText(chi2_repr) 
    915943 
     944        self.communicate.plotUpdateSignal.emit([fitted_data]) 
     945 
    916946        # Plot residuals if actual data 
    917947        if self.data_is_loaded: 
     
    920950            self.createNewIndex(residuals_plot) 
    921951            self.communicate.plotUpdateSignal.emit([residuals_plot]) 
    922  
    923         self.communicate.plotUpdateSignal.emit([fitted_data]) 
    924952 
    925953    def calcException(self, etype, value, tb): 
  • src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingWidgetTest.py

    r83eb5208 rd48cc19  
    11import sys 
    22import unittest 
     3import time 
    34 
    45from PyQt4 import QtGui 
     
    2122 
    2223class dummy_manager(object): 
    23     def communicate(self): 
    24         return Communicate() 
     24    communicate = Communicate() 
    2525 
    2626class FittingWidgetTest(unittest.TestCase): 
     
    8080        self.assertFalse(widget_with_data.acceptsData()) 
    8181 
    82     def notestSelectModel(self): 
     82    def testSelectModel(self): 
    8383        """ 
    8484        Test if models have been loaded properly 
    85         :return: 
    8685        """ 
    8786        fittingWindow =  self.widget 
     
    280279        pass 
    281280 
    282     def notestCreateTheoryIndex(self): 
     281    def testCreateTheoryIndex(self): 
    283282        """ 
    284283        Test the data->QIndex conversion 
     
    292291 
    293292        # Create the index 
    294         self.widget.createTheoryIndex() 
    295  
    296         # Check the signal sent 
    297         print spy 
    298  
    299         # Check the index 
     293        self.widget.createTheoryIndex(Data1D(x=[1,2], y=[1,2])) 
     294 
     295        # Make sure the signal has been emitted 
     296        self.assertEqual(spy.count(), 1) 
     297 
     298        # Check the argument type 
     299        self.assertIsInstance(spy.called()[0]['args'][0], QtGui.QStandardItem) 
    300300 
    301301    def testCalculateQGridForModel(self): 
     
    305305        # Mock the thread creation 
    306306        threads.deferToThread = MagicMock() 
     307        # Model for theory 
     308        self.widget.SASModelToQModel("cylinder") 
    307309        # Call the tested method 
    308310        self.widget.calculateQGridForModel() 
     311        time.sleep(1) 
    309312        # Test the mock 
    310313        self.assertTrue(threads.deferToThread.called) 
    311314        self.assertEqual(threads.deferToThread.call_args_list[0][0][0].__name__, "compute") 
    312315 
    313     def testComplete1D(self): 
    314         """ 
    315         Check that a new 1D plot is generated 
    316         """ 
    317         # TODO when chisqr method coded 
    318         pass 
    319  
    320     def testComplete2D(self): 
    321         """ 
    322         Check that a new 2D plot is generated 
    323         """ 
    324         # TODO when chisqr method coded 
    325         pass 
     316    def testCalculateResiduals(self): 
     317        """ 
     318        Check that the residuals are calculated and plots updated 
     319        """ 
     320        test_data = Data1D(x=[1,2], y=[1,2]) 
     321 
     322        # Model for theory 
     323        self.widget.SASModelToQModel("cylinder") 
     324        # Invoke the tested method 
     325        self.widget.calculateResiduals(test_data) 
     326        # Check the Chi2 value - should be undetermined 
     327        self.assertEqual(self.widget.lblChi2Value.text(), '---') 
     328 
     329        # Force same data into logic 
     330        self.widget.logic.data = test_data 
     331        self.widget.calculateResiduals(test_data) 
     332        # Now, the difference is 0, as data is the same 
     333        self.assertEqual(self.widget.lblChi2Value.text(), '0') 
     334 
     335        # Change data 
     336        test_data_2 = Data1D(x=[1,2], y=[2.1,3.49]) 
     337        self.widget.logic.data = test_data_2 
     338        self.widget.calculateResiduals(test_data) 
     339        # Now, the difference is non-zero 
     340        self.assertEqual(float(self.widget.lblChi2Value.text()), 1.715) 
    326341 
    327342    def testSetPolyModel(self): 
     
    410425        self.assertEqual(self.widget._model_model.rowCount(), last_row) 
    411426 
     427    def testPlotTheory(self): 
     428        """ 
     429        See that theory item can produce a chart 
     430        """ 
     431        # By default, the compute/plot button is disabled 
     432        self.assertFalse(self.widget.cmdPlot.isEnabled()) 
     433        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot') 
     434 
     435        # Assign a model 
     436        self.widget.show() 
     437        # Change the category index so we have a model available 
     438        category_index = self.widget.cbCategory.findText("Sphere") 
     439        self.widget.cbCategory.setCurrentIndex(category_index) 
     440 
     441        # Check the enablement/text 
     442        self.assertTrue(self.widget.cmdPlot.isEnabled()) 
     443        self.assertEqual(self.widget.cmdPlot.text(), 'Calculate') 
     444 
     445        # Spying on plot update signal 
     446        spy = QtSignalSpy(self.widget, self.widget.communicate.plotRequestedSignal) 
     447 
     448        # Press Calculate 
     449        QtTest.QTest.mouseClick(self.widget.cmdPlot, QtCore.Qt.LeftButton) 
     450 
     451        # Observe cmdPlot caption change 
     452        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot') 
     453 
     454        # Make sure the signal has NOT been emitted 
     455        self.assertEqual(spy.count(), 0) 
     456 
     457        # Click again 
     458        QtTest.QTest.mouseClick(self.widget.cmdPlot, QtCore.Qt.LeftButton) 
     459 
     460        # This time, we got the update signal 
     461        self.assertEqual(spy.count(), 0) 
     462 
     463    def testPlotData(self): 
     464        """ 
     465        See that data item can produce a chart 
     466        """ 
     467        # By default, the compute/plot button is disabled 
     468        self.assertFalse(self.widget.cmdPlot.isEnabled()) 
     469        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot') 
     470 
     471        self.widget.show() 
     472 
     473        # Set data 
     474        test_data = Data1D(x=[1,2], y=[1,2]) 
     475 
     476        # Force same data into logic 
     477        self.widget.logic.data = test_data 
     478        self.widget.data_is_loaded = True 
     479 
     480        # Change the category index so we have a model available 
     481        category_index = self.widget.cbCategory.findText("Sphere") 
     482        self.widget.cbCategory.setCurrentIndex(category_index) 
     483 
     484        # Check the enablement/text 
     485        self.assertTrue(self.widget.cmdPlot.isEnabled()) 
     486        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot') 
     487 
     488        # Spying on plot update signal 
     489        spy = QtSignalSpy(self.widget, self.widget.communicate.plotRequestedSignal) 
     490 
     491        # Press Calculate 
     492        QtTest.QTest.mouseClick(self.widget.cmdPlot, QtCore.Qt.LeftButton) 
     493 
     494        # Observe cmdPlot caption did not change 
     495        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot') 
     496 
     497        # Make sure the signal has been emitted == new plot 
     498        self.assertEqual(spy.count(), 1) 
     499 
    412500 
    413501if __name__ == "__main__": 
  • src/sas/qtgui/Utilities/GuiUtils.py

    r83eb5208 rd48cc19  
    229229    # New plot requested from the GUI manager 
    230230    # Old "NewPlotEvent" 
    231     plotRequestedSignal = QtCore.pyqtSignal(str) 
     231    plotRequestedSignal = QtCore.pyqtSignal(list) 
    232232 
    233233    # Plot update requested from a perspective 
     
    323323    item.appendRow(object_item) 
    324324 
     325def plotsFromFilename(filename, model_item): 
     326    """ 
     327    Returns the list of plots for the item with text=filename in the model 
     328    """ 
     329    assert isinstance(model_item, QtGui.QStandardItemModel) 
     330    assert isinstance(filename, basestring) 
     331 
     332    plot_data = [] 
     333    # Iterate over model looking for named items 
     334    for index in range(model_item.rowCount()): 
     335        item = model_item.item(index) 
     336        if str(item.text()) == filename: 
     337            # TODO: assure item type is correct (either data1/2D or Plotter) 
     338            plot_data.append(item.child(0).data().toPyObject()) 
     339        # Going 1 level deeper only 
     340        for index_2 in range(item.rowCount()): 
     341            item_2 = item.child(index_2) 
     342            if item_2 and item_2.isCheckable(): 
     343                # TODO: assure item type is correct (either data1/2D or Plotter) 
     344                plot_data.append(item_2.child(0).data().toPyObject()) 
     345 
     346    return plot_data 
    325347 
    326348def plotsFromCheckedItems(model_item): 
Note: See TracChangeset for help on using the changeset viewer.