Ignore:
Timestamp:
Nov 13, 2017 7:34:45 AM (6 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:
7c487846
Parents:
1543f0c
Message:

Initial commit of Celine's Invariant Perspective work SASVIEW-52

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/sas/qtgui/Perspectives/Invariant/InvariantPerspective.py

    • Property mode changed from 100644 to 100755
    r4992ff2 rf1f3e6a  
    22import sys 
    33import os 
     4import logging 
     5import copy 
     6import webbrowser 
     7 
    48from PyQt5 import QtCore 
    5 from PyQt5 import QtGui 
    6 from PyQt5 import QtWidgets 
    7 from PyQt5 import QtWebKitWidgets 
     9from PyQt5 import QtGui, QtWidgets 
     10from PyQt5 import QtWebKit 
    811 
    912from twisted.internet import threads 
     
    1417from sas.qtgui.Plotting.PlotterData import Data1D 
    1518import sas.qtgui.Utilities.GuiUtils as GuiUtils 
     19 
     20# import sas.qtgui.Plotting.PlotHelper as PlotHelper 
    1621 
    1722# local 
     
    2631# the ratio of maximum q value/(qmax of data) to plot the theory data 
    2732Q_MAXIMUM_PLOT = 3 
    28  
    29  
    30 class MyModel(object): 
    31     def __init__(self): 
    32         self._model = QtGui.QStandardItemModel(self) 
    33  
    34     def addItem(self, item): 
    35         item = QtGui.QStandardItem(str(item)) 
    36         self._model.appendRow(item) 
     33# Default number of points of interpolation: high and low range 
     34NPOINTS_Q_INTERP = 10 
     35# Default power law for interpolation 
     36DEFAULT_POWER_LOW = 4 
     37 
     38# Background of line edits if settings OK or wrong 
     39BG_WHITE = "background-color: rgb(255, 255, 255);" 
     40BG_RED = "background-color: rgb(244, 170, 164);" 
    3741 
    3842class InvariantWindow(QtWidgets.QDialog, Ui_tabbedInvariantUI): 
    3943    # The controller which is responsible for managing signal slots connections 
    4044    # for the gui and providing an interface to the data model. 
    41     name = "Invariant" # For displaying in the combo box 
    42     #def __init__(self, manager=None, parent=None): 
     45    name = "Invariant"  # For displaying in the combo box in DataExplorer 
     46 
    4347    def __init__(self, parent=None): 
    44         #super(InvariantWindow, self).__init__(parent) 
    4548        super(InvariantWindow, self).__init__() 
    4649        self.setupUi(self) 
     
    5255        self._scale = 1.0 
    5356        self._contrast = 1.0 
    54         self._porod = 1.0 
    55         self._npoints_low = 10 
    56         self._npoints_high = 10 
    57         self._power_low = 4 
     57        self._porod = None 
     58 
     59        self.parent = parent 
    5860 
    5961        self._manager = parent 
    60         #self._manager = manager 
    6162        self._reactor = reactor 
    6263        self._model_item = QtGui.QStandardItem() 
    6364 
    64         self._helpView = QtWebKitWidgets.QWebView() 
     65        #self._helpView = QtWebKit.QWebView() 
    6566        self.detailsDialog = DetailsDialog(self) 
     67        self.detailsDialog.cmdOK.clicked.connect(self.enabling) 
    6668 
    6769        self._low_extrapolate = False 
    68         self._low_guinier  = True 
    69         self._low_fit  = True 
     70        self._low_guinier = True 
     71        self._low_fit = False 
     72        self._low_power_value = False 
     73        self._low_points = NPOINTS_Q_INTERP 
     74        self._low_power_value = DEFAULT_POWER_LOW 
     75 
    7076        self._high_extrapolate = False 
    71         self._high_power_value  = False 
     77        self._high_power_value = False 
     78        self._high_fit = False 
     79        self._high_points = NPOINTS_Q_INTERP 
     80        self._high_power_value = DEFAULT_POWER_LOW 
    7281 
    7382        # no reason to have this widget resizable 
    7483        self.setFixedSize(self.minimumSizeHint()) 
    7584 
    76         self.communicate = GuiUtils.Communicate() 
     85        self.communicate = self._manager.communicator() 
    7786 
    7887        self._data = None 
     
    8190        self._allow_close = False 
    8291 
    83         # Mask file selector 
    84         ################################################### 
    85         #self._path = "cyl_400_20.txt" 
    86         #from sas.sascalc.dataloader.loader import  Loader 
    87         #loader = Loader() 
    88         #try: 
    89         #    self._data = loader.load(self._path) 
    90         #except: 
    91         #    raise 
    92         ################################################### 
    93  
    94         self.lineEdit_8.setText(str(Q_MINIMUM)) 
    95         self.lineEdit_9.setText(str(Q_MAXIMUM)) 
     92        # Modify font in order to display Angstrom symbol correctly 
     93        new_font = 'font-family: -apple-system, "Helvetica Neue", "Ubuntu";' 
     94        self.lblTotalQUnits.setStyleSheet(new_font) 
     95        self.lblSpecificSurfaceUnits.setStyleSheet(new_font) 
     96        self.lblInvariantTotalQUnits.setStyleSheet(new_font) 
     97        self.lblContrastUnits.setStyleSheet(new_font) 
     98        self.lblPorodCstUnits.setStyleSheet(new_font) 
     99        self.lblExtrapolQUnits.setStyleSheet(new_font) 
     100 
     101        # To remove blue square around line edits 
     102        self.txtBackgd.setAttribute(QtCore.Qt.WA_MacShowFocusRect, False) 
     103        self.txtContrast.setAttribute(QtCore.Qt.WA_MacShowFocusRect, False) 
     104        self.txtScale.setAttribute(QtCore.Qt.WA_MacShowFocusRect, False) 
     105        self.txtPorodCst.setAttribute(QtCore.Qt.WA_MacShowFocusRect, False) 
     106        self.txtNptsHighQ.setAttribute(QtCore.Qt.WA_MacShowFocusRect, False) 
     107        self.txtNptsLowQ.setAttribute(QtCore.Qt.WA_MacShowFocusRect, False) 
     108        self.txtPowerLowQ.setAttribute(QtCore.Qt.WA_MacShowFocusRect, False) 
     109        self.txtPowerHighQ.setAttribute(QtCore.Qt.WA_MacShowFocusRect, False) 
     110 
     111        self.txtExtrapolQMin.setText(str(Q_MINIMUM)) 
     112        self.txtExtrapolQMax.setText(str(Q_MAXIMUM)) 
    96113 
    97114        # Let's choose the Standard Item Model. 
     
    108125        self.setupMapper() 
    109126 
    110         new_font = 'font-family: -apple-system, "Helvetica Neue", "Ubuntu";' 
    111         self.label_2.setStyleSheet(new_font) 
    112         self.label_24.setStyleSheet(new_font) 
    113         self.label_27.setStyleSheet(new_font) 
     127        # validator: double 
     128        self.txtBackgd.setValidator(QtGui.QDoubleValidator()) 
     129        self.txtContrast.setValidator(QtGui.QDoubleValidator()) 
     130        self.txtScale.setValidator(QtGui.QDoubleValidator()) 
     131        self.txtPorodCst.setValidator(QtGui.QDoubleValidator()) 
     132 
     133        # validator: integer number 
     134        valid_regex_int = QtCore.QRegExp('^[+]?(\d+[.][0]*)$') 
     135        self.txtNptsLowQ.setValidator(QtGui.QRegExpValidator(valid_regex_int, 
     136                                                             self.txtNptsLowQ)) 
     137        self.txtNptsHighQ.setValidator(QtGui.QRegExpValidator(valid_regex_int, 
     138                                                             self.txtNptsHighQ)) 
     139        self.txtPowerLowQ.setValidator(QtGui.QRegExpValidator(valid_regex_int, 
     140                                                             self.txtPowerLowQ)) 
     141        self.txtPowerHighQ.setValidator(QtGui.QRegExpValidator(valid_regex_int, 
     142                                                             self.txtPowerHighQ)) 
     143 
     144    def enabling(self): 
     145        """ """ 
     146        self.cmdStatus.setEnabled(True) 
    114147 
    115148    def setClosable(self, value=True): 
    116         """ 
    117         Allow outsiders close this widget 
    118         """ 
     149        """ Allow outsiders close this widget """ 
    119150        assert isinstance(value, bool) 
    120151 
     
    134165            self.setWindowState(QtCore.Qt.WindowMinimized) 
    135166 
    136     def communicator(self): 
    137         """ Getter for the communicator """ 
    138         return self.communicate 
    139  
    140167    def updateFromModel(self): 
    141         """ 
    142         update the globals based on the data in the model 
    143         """ 
     168        """ Update the globals based on the data in the model """ 
    144169        self._background = float(self.model.item(WIDGETS.W_BACKGROUND).text()) 
    145         self._contrast   = float(self.model.item(WIDGETS.W_CONTRAST).text()) 
    146         self._scale      = float(self.model.item(WIDGETS.W_SCALE).text()) 
    147  
    148         # High extrapolate 
    149         self._low_extrapolate = ( str(self.model.item(WIDGETS.W_ENABLE_LOWQ).text()) == 'true') 
     170        self._contrast = float(self.model.item(WIDGETS.W_CONTRAST).text()) 
     171        self._scale = float(self.model.item(WIDGETS.W_SCALE).text()) 
     172        if self.model.item(WIDGETS.W_POROD_CST).text() != 'None' \ 
     173                and self.model.item(WIDGETS.W_POROD_CST).text() != '': 
     174            self._porod = float(self.model.item(WIDGETS.W_POROD_CST).text()) 
     175 
     176        # Low extrapolating 
     177        self._low_extrapolate = str(self.model.item(WIDGETS.W_ENABLE_LOWQ).text()) == 'true' 
    150178        self._low_points = float(self.model.item(WIDGETS.W_NPTS_LOWQ).text()) 
    151         self._low_guinier  = ( str(self.model.item(WIDGETS.W_LOWQ_GUINIER).text()) == 'true') 
    152         self._low_fit  = ( str(self.model.item(WIDGETS.W_LOWQ_FIT).text()) == 'true') 
    153         self._low_power_value  = float(self.model.item(WIDGETS.W_LOWQ_POWER_VALUE).text()) 
     179        self._low_guinier = str(self.model.item(WIDGETS.W_LOWQ_GUINIER).text()) == 'true' 
     180        self._low_fit = str(self.model.item(WIDGETS.W_LOWQ_FIT).text()) == 'true' 
     181        self._low_power_value = float(self.model.item(WIDGETS.W_LOWQ_POWER_VALUE).text()) 
    154182 
    155183        # High extrapolating 
    156         self._high_extrapolate = ( str(self.model.item(WIDGETS.W_ENABLE_HIGHQ).text()) == 'true') 
    157         self._high_points  = float(self.model.item(WIDGETS.W_NPTS_HIGHQ).text()) 
    158         self._high_fit  = ( str(self.model.item(WIDGETS.W_HIGHQ_FIT).text()) == 'true') 
    159         self._high_power_value  = float(self.model.item(WIDGETS.W_HIGHQ_POWER_VALUE).text()) 
     184        self._high_extrapolate = str(self.model.item(WIDGETS.W_ENABLE_HIGHQ).text()) == 'true' 
     185        self._high_points = float(self.model.item(WIDGETS.W_NPTS_HIGHQ).text()) 
     186        self._high_fit = str(self.model.item(WIDGETS.W_HIGHQ_FIT).text()) == 'true' 
     187        self._high_power_value = float(self.model.item(WIDGETS.W_HIGHQ_POWER_VALUE).text()) 
    160188 
    161189    def calculateInvariant(self): 
    162         """ 
    163         Use twisted to thread the calculations away. 
    164         """ 
     190        """ Use twisted to thread the calculations away """ 
    165191        # Find out if extrapolation needs to be used. 
    166192        extrapolation = None 
    167         if self._low_extrapolate  and not self._high_extrapolate: 
     193        if self._low_extrapolate and not self._high_extrapolate: 
    168194            extrapolation = "low" 
    169         elif not self._low_extrapolate  and self._high_extrapolate: 
     195        elif not self._low_extrapolate and self._high_extrapolate: 
    170196            extrapolation = "high" 
    171197        elif self._low_extrapolate and self._high_extrapolate: 
    172198            extrapolation = "both" 
    173         try: 
    174             # modify the Calculate button to indicate background process 
    175             self.pushButton.setText("Calculating...") 
    176             self.pushButton.setEnabled(False) 
    177             self.style = self.pushButton.styleSheet() 
    178             self.pushButton.setStyleSheet("background-color: rgb(255, 255, 0); color: rgb(0, 0, 0)") 
    179             # Send the calculations to separate thread. 
    180             d = threads.deferToThread(self.calculateThread, extrapolation) 
    181             # Add deferred callback for call return 
    182             d.addCallback(self.plotResult) 
    183             d.addErrback(self.plotFailed) 
    184         except Exception as ex: 
    185             # Set the button back to available 
    186             self.pushButton.setEnabled(True) 
    187             self.pushButton.setText("Calculate") 
    188             self.pushButton.setStyleSheet(self.style) 
    189  
    190     def plotFailed(self, reason): 
    191         """ 
    192         """ 
    193         print("plotFailed FAILED: ", reason) 
     199 
     200        # modify the Calculate button to indicate background process 
     201        self.cmdCalculate.setText("Calculating...") 
     202        self.cmdCalculate.setEnabled(False) 
     203 
     204        # Send the calculations to separate thread. 
     205        d = threads.deferToThread(self.calculateThread, extrapolation) 
     206 
     207        # Add deferred callback for call return 
     208        d.addCallback(self.deferredPlot) 
     209        d.addErrback(self.calculationFailed) 
     210 
     211    def calculationFailed(self, reason): 
     212        print("calculation failed: ", reason) 
    194213        pass 
    195214 
     215    def deferredPlot(self, model): 
     216        """ 
     217        Run the GUI/model update in the main thread 
     218        """ 
     219        reactor.callFromThread(lambda: self.plotResult(model)) 
     220 
    196221    def plotResult(self, model): 
    197         """ 
    198         """ 
     222        """ Plot result of calculation """ 
     223        # Set the button back to available 
     224        self.cmdCalculate.setEnabled(True) 
     225        self.cmdCalculate.setText("Calculate") 
     226        self.cmdStatus.setEnabled(True) 
     227 
    199228        self.model = model 
    200229        self.mapper.toFirst() 
    201  
    202         # Set the button back to available 
    203         self.pushButton.setEnabled(True) 
    204         self.pushButton.setText("Calculate") 
    205         self.pushButton.setStyleSheet(self.style) 
     230        self._data = GuiUtils.dataFromItem(self._model_item) 
    206231 
    207232        # Send the modified model item to DE for keeping in the model 
    208         self.communicate.updateModelFromPerspectiveSignal.emit(self._model_item) 
    209  
     233        # Currently -unused 
     234        #self.communicate.updateModelFromPerspectiveSignal.emit(self._model_item) 
     235 
     236        plot_data = GuiUtils.plotsFromCheckedItems(self._manager.filesWidget.model) 
     237 
     238        self._manager.filesWidget.plotData(plot_data) 
     239 
     240        # Update the details dialog in case it is open 
     241        self.updateDetailsWidget(model) 
     242 
     243    def updateDetailsWidget(self, model): 
     244        """ 
     245        On demand update of the details widget 
     246        """ 
     247        if self.detailsDialog.isVisible(): 
     248            self.onStatus() 
    210249 
    211250    def calculateThread(self, extrapolation): 
    212251        """ 
    213252        Perform Invariant calculations. 
    214  
    215253        TODO: Create a dictionary of results to be sent to DE on completion. 
    216254        """ 
    217255        self.updateFromModel() 
     256        msg = '' 
    218257 
    219258        qstar_low = 0.0 
     
    222261        qstar_high_err = 0.0 
    223262        qstar_total = 0.0 
    224         qstar_total_low_err = 0.0 
     263        qstar_total_error = 0.0 
     264 
     265        temp_data = copy.deepcopy(self._data) 
    225266 
    226267        # Prepare the invariant object 
    227         inv = invariant.InvariantCalculator(data=self._data, 
    228                                             background = self._background, 
    229                                             scale = self._scale) 
    230  
     268        inv = invariant.InvariantCalculator(data=temp_data, 
     269                                            background=self._background, 
     270                                            scale=self._scale) 
    231271        if self._low_extrapolate: 
     272 
    232273            function_low = "power_law" 
    233274            if self._low_guinier: 
     
    235276            if self._low_fit: 
    236277                self._low_power_value = None 
     278 
    237279            inv.set_extrapolation(range="low", 
    238                                   npts=self._low_points, 
     280                                  npts=int(self._low_points), 
    239281                                  function=function_low, 
    240282                                  power=self._low_power_value) 
     
    243285            function_low = "power_law" 
    244286            inv.set_extrapolation(range="high", 
    245                                   npts=self._high_points, 
     287                                  npts=int(self._high_points), 
    246288                                  function=function_low, 
    247                                   power=self._low_power_value) 
    248  
    249         #Compute invariant 
    250         # TODO: proper exception handling and logic - 
    251         # display info, update lineedits, don't run extrapolations etc. 
     289                                  power=self._high_power_value) 
     290        # Compute invariant 
    252291        calculation_failed = False 
     292 
    253293        try: 
    254             qstar_total, qstar_total_error         = inv.get_qstar_with_error() 
     294            qstar_total, qstar_total_error = inv.get_qstar_with_error() 
    255295        except Exception as ex: 
     296            msg += str(ex) 
    256297            calculation_failed = True 
    257298            # Display relevant information 
     
    260301            item = QtGui.QStandardItem("ERROR") 
    261302            self.model.setItem(WIDGETS.W_INVARIANT_ERR, item) 
     303 
    262304        try: 
    263305            volume_fraction, volume_fraction_error = \ 
    264306                inv.get_volume_fraction_with_error(self._contrast) 
     307 
    265308        except Exception as ex: 
    266309            calculation_failed = True 
     310            msg += str(ex) 
    267311            # Display relevant information 
    268312            item = QtGui.QStandardItem("ERROR") 
     
    270314            item = QtGui.QStandardItem("ERROR") 
    271315            self.model.setItem(WIDGETS.W_VOLUME_FRACTION_ERR, item) 
    272         try: 
    273             surface, surface_error = \ 
    274                 inv.get_surface_with_error(self._contrast, self._porod) 
    275         except Exception as ex: 
    276             calculation_failed = True 
    277             # Display relevant information 
    278             item = QtGui.QStandardItem("ERROR") 
    279             self.model.setItem(WIDGETS.W_SPECIFIC_SURFACE, item) 
    280             item = QtGui.QStandardItem("ERROR") 
    281             self.model.setItem(WIDGETS.W_SPECIFIC_SURFACE_ERR, item) 
    282  
    283         if(calculation_failed): 
    284             # TODO: NOTIFY THE GUI MANAGER!! 
     316 
     317        if self._porod: 
     318            try: 
     319                surface, surface_error = \ 
     320                    inv.get_surface_with_error(self._contrast, self._porod) 
     321            except Exception as ex: 
     322                calculation_failed = True 
     323                msg += str(ex) 
     324                # Display relevant information 
     325                item = QtGui.QStandardItem("ERROR") 
     326                self.model.setItem(WIDGETS.W_SPECIFIC_SURFACE, item) 
     327                item = QtGui.QStandardItem("ERROR") 
     328                self.model.setItem(WIDGETS.W_SPECIFIC_SURFACE_ERR, item) 
     329        else: 
     330            surface = None 
     331 
     332        if (calculation_failed): 
    285333            self.mapper.toFirst() 
     334            logging.warning('Calculation failed: {}'.format(msg)) 
    286335            return self.model 
    287  
    288         if self._low_extrapolate: 
    289             # for presentation in InvariantDetails 
    290             qstar_low, qstar_low_err = inv.get_qstar_low() 
    291             extrapolated_data = inv.get_extra_data_low(self._low_points) 
    292             power_low = inv.get_extrapolation_power(range='low') 
    293  
    294             #inv.data = extrapolated_data 
    295             #qstar_total, qstar_total_error = inv.get_qstar_with_error() 
    296  
    297             # Plot the chart 
    298             title = "Low-Q extrapolation" 
    299  
    300             # Convert the data into plottable 
    301             extrapolated_data = self._manager.createGuiData(extrapolated_data) 
    302             extrapolated_data.name = title 
    303             extrapolated_data.title = title 
    304  
    305             # Add the plot to the model item 
    306             # This needs to run in the main thread 
    307             reactor.callFromThread(GuiUtils.updateModelItemWithPlot, 
    308                     self._model_item, extrapolated_data, title) 
    309  
    310         if self._high_extrapolate: 
    311             # for presentation in InvariantDetails 
    312             qmax_plot = Q_MAXIMUM_PLOT * max(self._data.x) 
    313             if qmax_plot > Q_MAXIMUM: 
    314                 qmax_plot = Q_MAXIMUM 
    315             qstar_high, qstar_high_err = inv.get_qstar_high() 
    316             power_high = inv.get_extrapolation_power(range='high') 
    317             high_out_data = inv.get_extra_data_high(q_end=qmax_plot, npts=500) 
    318  
    319             # Plot the chart 
    320             title = "High-Q extrapolation" 
    321  
    322             # Convert the data into plottable 
    323             high_out_data = self._manager.createGuiData(high_out_data) 
    324             high_out_data.name = title 
    325             high_out_data.title = title 
    326  
    327             # Add the plot to the model item 
    328             # This needs to run in the main thread 
    329             reactor.callFromThread(GuiUtils.updateModelItemWithPlot, 
    330                     self._model_item, high_out_data, title) 
    331  
    332         item = QtGui.QStandardItem(str(float('%.5g'% volume_fraction))) 
    333         self.model.setItem(WIDGETS.W_VOLUME_FRACTION, item) 
    334         item = QtGui.QStandardItem(str(float('%.5g'% volume_fraction_error))) 
    335         self.model.setItem(WIDGETS.W_VOLUME_FRACTION_ERR, item) 
    336         item = QtGui.QStandardItem(str(float('%.5g'% surface))) 
    337         self.model.setItem(WIDGETS.W_SPECIFIC_SURFACE, item) 
    338         item = QtGui.QStandardItem(str(float('%.5g'% surface_error))) 
    339         self.model.setItem(WIDGETS.W_SPECIFIC_SURFACE_ERR, item) 
    340         item = QtGui.QStandardItem(str(float('%.5g'% qstar_total))) 
    341         self.model.setItem(WIDGETS.W_INVARIANT, item) 
    342         item = QtGui.QStandardItem(str(float('%.5g'% qstar_total_error))) 
    343         self.model.setItem(WIDGETS.W_INVARIANT_ERR, item) 
    344  
    345         #item = QtGui.QStandardItem(str(float('%.5g'% qstar_total))) 
    346         #self.model.setItem(WIDGETS.D_TOTAL_QSTAR, item) 
    347         #item = QtGui.QStandardItem(str(float('%.5g'% qstar_total_err))) 
    348         #self.model.setItem(WIDGETS.D_TOTAL_QSTAR_ERR, item) 
    349         item = QtGui.QStandardItem(str(float('%.5g'% qstar_low))) 
    350         self.model.setItem(WIDGETS.D_LOW_QSTAR, item) 
    351         item = QtGui.QStandardItem(str(float('%.5g'% qstar_low_err))) 
    352         self.model.setItem(WIDGETS.D_LOW_QSTAR_ERR, item) 
    353         item = QtGui.QStandardItem(str(float('%.5g'% qstar_high))) 
    354         self.model.setItem(WIDGETS.D_HIGH_QSTAR, item) 
    355         item = QtGui.QStandardItem(str(float('%.5g'% qstar_high_err))) 
    356         self.model.setItem(WIDGETS.D_HIGH_QSTAR_ERR, item) 
    357  
    358         self.mapper.toFirst() 
    359  
    360         return self.model 
    361                  
     336        else: 
     337 
     338            if self._low_extrapolate: 
     339                # for presentation in InvariantDetails 
     340                qstar_low, qstar_low_err = inv.get_qstar_low() 
     341                extrapolated_data = inv.get_extra_data_low(self._low_points) 
     342                power_low = inv.get_extrapolation_power(range='low') 
     343 
     344                # Plot the chart 
     345                title = "Low-Q extrapolation" 
     346 
     347                # Convert the data into plottable 
     348                extrapolated_data = self._manager.createGuiData(extrapolated_data) 
     349 
     350                extrapolated_data.name = title 
     351                extrapolated_data.title = title 
     352 
     353                # copy labels and units of axes for plotting 
     354                extrapolated_data._xaxis = temp_data._xaxis 
     355                extrapolated_data._xunit = temp_data._xunit 
     356                extrapolated_data._yaxis = temp_data._yaxis 
     357                extrapolated_data._yunit = temp_data._yunit 
     358 
     359                # Add the plot to the model item 
     360                # This needs to run in the main thread 
     361                reactor.callFromThread(GuiUtils.updateModelItemWithPlot, 
     362                                       self._model_item, 
     363                                       extrapolated_data, 
     364                                       title) 
     365 
     366            if self._high_extrapolate: 
     367                # for presentation in InvariantDetails 
     368                qmax_plot = Q_MAXIMUM_PLOT * max(temp_data.x) # self._data.x) 
     369 
     370                if qmax_plot > Q_MAXIMUM: 
     371                    qmax_plot = Q_MAXIMUM 
     372                qstar_high, qstar_high_err = inv.get_qstar_high() 
     373                power_high = inv.get_extrapolation_power(range='high') 
     374                high_out_data = inv.get_extra_data_high(q_end=qmax_plot, npts=500) 
     375 
     376                # Plot the chart 
     377                title = "High-Q extrapolation" 
     378 
     379                # Convert the data into plottable 
     380                high_out_data = self._manager.createGuiData(high_out_data) 
     381                high_out_data.name = title 
     382                high_out_data.title = title 
     383 
     384                # copy labels and units of axes for plotting 
     385                high_out_data._xaxis = temp_data._xaxis 
     386                high_out_data._xunit = temp_data._xunit 
     387                high_out_data._yaxis = temp_data._yaxis 
     388                high_out_data._yunit = temp_data._yunit 
     389 
     390                # Add the plot to the model item 
     391                # This needs to run in the main thread 
     392                reactor.callFromThread(GuiUtils.updateModelItemWithPlot, 
     393                                       self._model_item, high_out_data, title) 
     394 
     395            item = QtGui.QStandardItem(str(float('%.3g'% volume_fraction))) 
     396            self.model.setItem(WIDGETS.W_VOLUME_FRACTION, item) 
     397            item = QtGui.QStandardItem(str(float('%.3g'% volume_fraction_error))) 
     398            self.model.setItem(WIDGETS.W_VOLUME_FRACTION_ERR, item) 
     399            if surface: 
     400                item = QtGui.QStandardItem(str(float('%.3g'% surface))) 
     401                self.model.setItem(WIDGETS.W_SPECIFIC_SURFACE, item) 
     402                item = QtGui.QStandardItem(str(float('%.3g'% surface_error))) 
     403                self.model.setItem(WIDGETS.W_SPECIFIC_SURFACE_ERR, item) 
     404            item = QtGui.QStandardItem(str(float('%.3g'% qstar_total))) 
     405            self.model.setItem(WIDGETS.W_INVARIANT, item) 
     406            item = QtGui.QStandardItem(str(float('%.3g'% qstar_total_error))) 
     407            self.model.setItem(WIDGETS.W_INVARIANT_ERR, item) 
     408 
     409            item = QtGui.QStandardItem(str(float('%.3g'% qstar_low))) 
     410            self.model.setItem(WIDGETS.D_LOW_QSTAR, item) 
     411            item = QtGui.QStandardItem(str(float('%.3g'% qstar_low_err))) 
     412            self.model.setItem(WIDGETS.D_LOW_QSTAR_ERR, item) 
     413            item = QtGui.QStandardItem(str(float('%.3g'% qstar_high))) 
     414            self.model.setItem(WIDGETS.D_HIGH_QSTAR, item) 
     415            item = QtGui.QStandardItem(str(float('%.3g'% qstar_high_err))) 
     416            self.model.setItem(WIDGETS.D_HIGH_QSTAR_ERR, item) 
     417 
     418            self.mapper.toFirst() 
     419 
     420            return self.model 
     421 
    362422    def title(self): 
    363         """ 
    364         Perspective name 
    365         """ 
     423        """ Perspective name """ 
    366424        return "Invariant panel" 
    367425 
    368     def status(self): 
    369         """ 
     426    def onStatus(self): 
     427        """ 
     428        Display Invariant Details panel when clicking on Status button 
    370429        """ 
    371430        self.detailsDialog.setModel(self.model) 
    372431        self.detailsDialog.showDialog() 
    373  
    374     def help(self): 
    375         """ 
    376         """ 
    377         _TreeLocation = GuiUtils.HELP_DIRECTORY_LOCATION + \ 
     432        self.cmdStatus.setEnabled(False) 
     433 
     434    def onHelp(self): 
     435        """ Display help when clicking on Help button """ 
     436        treeLocation = GuiUtils.HELP_DIRECTORY_LOCATION + \ 
    378437            "/user/sasgui/perspectives/invariant/invariant_help.html" 
    379         self._helpView.load(QtCore.QUrl(_TreeLocation)) 
    380         self._helpView.show() 
     438        webbrowser.open('file://' + treeLocation) 
    381439 
    382440    def setupSlots(self): 
    383         self.pushButton.clicked.connect(self.calculateInvariant) 
    384         self.pushButton_2.clicked.connect(self.status) 
    385         self.pushButton_3.clicked.connect(self.help) 
    386  
    387         self.radioButton.toggled.connect(self.lowGuinierAndPowerToggle) 
    388         self.radioButton_8.toggled.connect(self.hiFitAndFixToggle) 
    389  
    390         self.radioButton_3.toggled.connect(self.lowFitAndFixToggle) 
    391  
    392         # Bug workaround for QDataWidgetMapper() not reacting to checkbox clicks. 
    393         # https://bugreports.qt.io/browse/QTBUG-1818 
    394         self.checkBox.clicked.connect(self.setFocus) 
    395         self.checkBox_2.clicked.connect(self.setFocus) 
     441        """ """ 
     442        self.cmdCalculate.clicked.connect(self.calculateInvariant) 
     443        self.cmdStatus.clicked.connect(self.onStatus) 
     444        self.cmdHelp.clicked.connect(self.onHelp) 
     445 
     446        self.chkLowQ.stateChanged.connect(self.stateChanged) 
     447        self.chkLowQ.stateChanged.connect(self.checkQExtrapolatedData) 
     448 
     449        self.chkHighQ.stateChanged.connect(self.stateChanged) 
     450        self.chkHighQ.stateChanged.connect(self.checkQExtrapolatedData) 
     451 
     452        # slots for the Guinier and PowerLaw radio buttons at low Q 
     453        # since they are not auto-exclusive 
     454        self.rbGuinier.toggled.connect(self.lowGuinierAndPowerToggle) 
     455 
     456        self.rbPowerLawLowQ.toggled.connect(self.lowGuinierAndPowerToggle) 
     457 
     458        self.rbFitHighQ.toggled.connect(self.hiFitAndFixToggle) 
     459 
     460        self.rbFitLowQ.toggled.connect(self.lowFitAndFixToggle) 
    396461 
    397462        self.model.itemChanged.connect(self.modelChanged) 
    398463 
     464        # update model from gui editing by users 
     465        self.txtBackgd.textChanged.connect(self.updateFromGui) 
     466 
     467        self.txtScale.textChanged.connect(self.updateFromGui) 
     468 
     469        self.txtContrast.textChanged.connect(self.updateFromGui) 
     470 
     471        self.txtPorodCst.textChanged.connect(self.updateFromGui) 
     472 
     473        self.txtPowerLowQ.textChanged.connect(self.updateFromGui) 
     474 
     475        self.txtPowerHighQ.textChanged.connect(self.updateFromGui) 
     476 
     477        self.txtNptsLowQ.textChanged.connect(self.updateFromGui) 
     478 
     479        self.txtNptsHighQ.textChanged.connect(self.updateFromGui) 
     480 
     481        # check values of n_points compared to distribution length 
     482        if self.txtNptsLowQ.isEnabled(): 
     483            self.txtNptsLowQ.textChanged.connect(self.checkLength) 
     484 
     485        if self.txtNptsHighQ.isEnabled(): 
     486            self.txtNptsHighQ.textChanged.connect(self.checkLength) 
     487 
     488    def stateChanged(self): 
     489        """ 
     490        Catch modifications from low- and high-Q extrapolation check boxes 
     491        """ 
     492        sender = self.sender() 
     493 
     494        itemf = QtGui.QStandardItem(str(sender.isChecked()).lower()) 
     495        if sender.text() == 'Enable Low-Q extrapolation': 
     496            self.model.setItem(WIDGETS.W_ENABLE_LOWQ, itemf) 
     497 
     498        if sender.text() == 'Enable High-Q extrapolation': 
     499            self.model.setItem(WIDGETS.W_ENABLE_HIGHQ, itemf) 
     500 
     501    def checkLength(self): 
     502        """ 
     503        Validators of number of points for extrapolation. 
     504        Error if it is larger than the distribution length 
     505        """ 
     506        if self._data: 
     507            if len(self._data.x) < int(self.sender().text()): 
     508                self.sender().setStyleSheet(QtCore.QString(BG_RED)) 
     509                logging.warning('The number of points must be smaller than {}'.format(len(self._data.x))) 
     510                self.cmdCalculate.setEnabled(False) 
     511            else: 
     512                self.sender().setStyleSheet(QtCore.QString(BG_WHITE)) 
     513                self.cmdCalculate.setEnabled(True) 
     514        else: 
     515            # logging.info('no data is loaded') 
     516            self.cmdCalculate.setEnabled(False) 
     517 
    399518    def modelChanged(self, item): 
    400         """ 
    401         """ 
     519        """ Update when model changed """ 
    402520        if item.row() == WIDGETS.W_ENABLE_LOWQ: 
    403521            toggle = (str(item.text()) == 'true') 
     
    408526            self._high_extrapolate = toggle 
    409527            self.highQToggle(toggle) 
    410          
     528 
     529    def checkQExtrapolatedData(self): 
     530        """ 
     531        Match status of low or high-Q extrapolated data checkbox in 
     532        DataExplorer with low or high-Q extrapolation checkbox in invariant 
     533        panel 
     534        """ 
     535        # name to search in DataExplorer 
     536        if 'Low' in str(self.sender().text()): 
     537            name = "Low-Q extrapolation" 
     538        if 'High' in str(self.sender().text()): 
     539            name = "High-Q extrapolation" 
     540 
     541        GuiUtils.updateModelItemStatus(self._manager.filesWidget.model, 
     542                                       self._path, name, 
     543                                       self.sender().checkState()) 
     544 
     545    def updateFromGui(self): 
     546        """ Update model when new user inputs """ 
     547        possible_senders = ['txtBackgd', 'txtContrast', 'txtPorodCst', 
     548                            'txtScale', 'txtPowerLowQ', 'txtPowerHighQ', 
     549                            'txtNptsLowQ', 'txtNptsHighQ'] 
     550 
     551        related_widgets = [WIDGETS.W_BACKGROUND, WIDGETS.W_CONTRAST, 
     552                           WIDGETS.W_POROD_CST, WIDGETS.W_SCALE, 
     553                           WIDGETS.W_LOWQ_POWER_VALUE, WIDGETS.W_HIGHQ_POWER_VALUE, 
     554                           WIDGETS.W_NPTS_LOWQ, WIDGETS.W_NPTS_HIGHQ] 
     555 
     556        related_internal_values = [self._background, self._contrast, 
     557                                   self._porod, self._scale, 
     558                                   self._low_power_value, 
     559                                   self._high_power_value, 
     560                                   self._low_points, self._high_points] 
     561 
     562        item = QtGui.QStandardItem(self.sender().text()) 
     563 
     564        index_elt = possible_senders.index(self.sender().objectName()) 
     565 
     566        self.model.setItem(related_widgets[index_elt], item) 
     567 
     568        related_internal_values[index_elt] = float(self.sender().text()) 
     569 
     570        # print possible_senders[index_elt], related_internal_values[index_elt] 
     571 
     572        self.mapper.toFirst() 
     573 
    411574    def lowGuinierAndPowerToggle(self, toggle): 
    412575        """ 
    413         """ 
    414         self._low_guinier = toggle 
     576        Guinier and Power radio buttons cannot be selected at the same time 
     577        If Power is selected, Fit and Fix radio buttons are visible and 
     578        Power line edit can be edited if Fix is selected 
     579        """ 
     580        if self.sender().text() == 'Guinier': 
     581            self._low_guinier = toggle 
     582 
     583            toggle = not toggle 
     584            self.rbPowerLawLowQ.setChecked(toggle) 
     585 
     586            self.rbFitLowQ.toggled.connect(self.lowFitAndFixToggle) 
     587            self.rbFitLowQ.setVisible(toggle) 
     588            self.rbFixLowQ.setVisible(toggle) 
     589 
     590            self.txtPowerLowQ.setEnabled(toggle and (not self._low_fit)) 
     591 
     592        else: 
     593            self._low_guinier = not toggle 
     594 
     595            self.rbGuinier.setChecked(not toggle) 
     596 
     597            self.rbFitLowQ.toggled.connect(self.lowFitAndFixToggle) 
     598            self.rbFitLowQ.setVisible(toggle) 
     599            self.rbFixLowQ.setVisible(toggle) 
     600 
     601            self.txtPowerLowQ.setEnabled(toggle and (not self._low_fit)) 
     602 
     603    def lowFitAndFixToggle(self, toggle): 
     604        """ Fit and Fix radiobuttons cannot be selected at the same time """ 
     605        self._low_fit = toggle 
     606 
    415607        toggle = not toggle 
    416         self.lineEdit_11.setEnabled(toggle) 
    417         self.radioButton_3.setEnabled(toggle) 
    418         self.radioButton_4.setEnabled(toggle) 
    419         self.lineEdit_11.setEnabled(toggle and (not self._low_fit)) 
    420  
    421     def lowFitAndFixToggle(self, toggle): 
    422         """ 
    423         """ 
    424         self._low_fit = toggle 
    425         toggle = not toggle 
    426         self.lineEdit_11.setEnabled(toggle) 
     608        self.txtPowerLowQ.setEnabled(toggle) 
    427609 
    428610    def hiFitAndFixToggle(self, toggle): 
    429611        """ 
    430         """ 
    431         self.lineEdit_13.setEnabled(toggle) 
     612        Enable editing of power exponent if Fix for high Q is checked 
     613        Disable otherwise 
     614        """ 
     615        self.txtPowerHighQ.setEnabled(not toggle) 
    432616 
    433617    def highQToggle(self, clicked): 
    434         """ 
    435         Disable/enable High Q extrapolation 
    436         """ 
    437         self.radioButton_7.setEnabled(clicked) 
    438         self.radioButton_8.setEnabled(clicked) 
    439         self.lineEdit_12.setEnabled(clicked) 
    440         self.lineEdit_13.setEnabled(clicked) 
     618        """ Disable/enable High Q extrapolation """ 
     619        self.rbFitHighQ.setEnabled(clicked) 
     620        self.rbFixHighQ.setEnabled(clicked) 
     621        self.txtNptsHighQ.setEnabled(clicked) 
     622        self.txtPowerHighQ.setEnabled(clicked) 
    441623 
    442624    def lowQToggle(self, clicked): 
    443         """ 
    444         Disable/enable Low Q extrapolation 
    445         """ 
    446         self.radioButton.setEnabled(clicked) 
    447         self.radioButton_2.setEnabled(clicked) 
    448         self.lineEdit_11.setEnabled(clicked and not self._low_fit) 
    449  
     625        """ Disable / enable Low Q extrapolation """ 
     626        self.rbGuinier.setEnabled(clicked) 
     627        self.rbPowerLawLowQ.setEnabled(clicked) 
     628        self.txtNptsLowQ.setEnabled(clicked) 
    450629        # Enable subelements 
    451         self.radioButton_3.setEnabled(clicked and not self._low_guinier) 
    452         self.radioButton_4.setEnabled(clicked and not self._low_guinier) 
    453         self.lineEdit_10.setEnabled(clicked and not self._low_guinier) 
     630        self.rbFitLowQ.setVisible(self.rbPowerLawLowQ.isChecked()) 
     631        self.rbFixLowQ.setVisible(self.rbPowerLawLowQ.isChecked()) 
     632        self.rbFitLowQ.setEnabled(clicked)  # and not self._low_guinier) 
     633        self.rbFixLowQ.setEnabled(clicked)  # and not self._low_guinier) 
     634 
     635        self.txtPowerLowQ.setEnabled(clicked 
     636                                    and not self._low_guinier 
     637                                    and not self._low_fit) 
    454638 
    455639    def setupModel(self): 
    456  
     640        """ """ 
    457641        # filename 
    458642        item = QtGui.QStandardItem(self._path) 
     
    460644 
    461645        # add Q parameters to the model 
    462         #qmin = min(self._data.x) 
    463646        qmin = 0.0 
    464647        item = QtGui.QStandardItem(str(qmin)) 
     
    475658        item = QtGui.QStandardItem(str(self._scale)) 
    476659        self.model.setItem(WIDGETS.W_SCALE, item) 
    477          
     660        # leave line edit empty if Porod constant not defined 
     661        if self._porod != None: 
     662            item = QtGui.QStandardItem(str(self._porod)) 
     663        else: 
     664            item = QtGui.QStandardItem(str('')) 
     665        self.model.setItem(WIDGETS.W_POROD_CST, item) 
     666 
    478667        # Dialog elements 
    479668        itemf = QtGui.QStandardItem("false") 
     
    482671        self.model.setItem(WIDGETS.W_ENABLE_LOWQ, itemf) 
    483672 
    484         item = QtGui.QStandardItem(str(self._npoints_low)) 
     673        item = QtGui.QStandardItem(str(NPOINTS_Q_INTERP)) 
    485674        self.model.setItem(WIDGETS.W_NPTS_LOWQ, item) 
    486         item = QtGui.QStandardItem(str(self._npoints_high)) 
     675        item = QtGui.QStandardItem(str(NPOINTS_Q_INTERP)) 
    487676        self.model.setItem(WIDGETS.W_NPTS_HIGHQ, item) 
    488677 
     
    492681        itemt = QtGui.QStandardItem("true") 
    493682        self.model.setItem(WIDGETS.W_LOWQ_FIT, itemt) 
    494         item = QtGui.QStandardItem(str(self._power_low)) 
     683        item = QtGui.QStandardItem(str(DEFAULT_POWER_LOW)) 
    495684        self.model.setItem(WIDGETS.W_LOWQ_POWER_VALUE, item) 
    496685 
    497686        itemt = QtGui.QStandardItem("true") 
    498687        self.model.setItem(WIDGETS.W_HIGHQ_FIT, itemt) 
    499         item = QtGui.QStandardItem(str(self._power_low)) 
     688        item = QtGui.QStandardItem(str(DEFAULT_POWER_LOW)) 
    500689        self.model.setItem(WIDGETS.W_HIGHQ_POWER_VALUE, item) 
    501  
    502690 
    503691    def setupMapper(self): 
     
    507695        self.mapper.setModel(self.model) 
    508696 
    509         # Set up the view on the model for testing 
    510         # self.tableView.setModel(self.model) 
    511  
    512697        # Filename 
    513         self.mapper.addMapping(self.lineEdit, WIDGETS.W_FILENAME) 
     698        self.mapper.addMapping(self.txtName, WIDGETS.W_FILENAME) 
     699 
    514700        # Qmin/Qmax 
    515         self.mapper.addMapping(self.lineEdit_2, WIDGETS.W_QMIN) 
    516         self.mapper.addMapping(self.lineEdit_3, WIDGETS.W_QMAX) 
     701        self.mapper.addMapping(self.txtTotalQMin, WIDGETS.W_QMIN) 
     702        self.mapper.addMapping(self.txtTotalQMax, WIDGETS.W_QMAX) 
    517703 
    518704        # Background 
    519         self.mapper.addMapping(self.lineEdit_4, WIDGETS.W_BACKGROUND) 
     705        self.mapper.addMapping(self.txtBackgd, WIDGETS.W_BACKGROUND) 
     706 
    520707        # Scale 
    521         self.mapper.addMapping(self.lineEdit_5, WIDGETS.W_SCALE) 
     708        self.mapper.addMapping(self.txtScale, WIDGETS.W_SCALE) 
     709 
    522710        # Contrast 
    523         self.mapper.addMapping(self.lineEdit_6, WIDGETS.W_CONTRAST) 
     711        self.mapper.addMapping(self.txtContrast, WIDGETS.W_CONTRAST) 
     712 
     713        # Porod constant 
     714        self.mapper.addMapping(self.txtPorodCst, WIDGETS.W_POROD_CST) 
    524715 
    525716        # Lowq/Highq items 
    526         self.mapper.addMapping(self.checkBox, WIDGETS.W_ENABLE_LOWQ) 
    527         self.mapper.addMapping(self.checkBox_2, WIDGETS.W_ENABLE_HIGHQ) 
    528  
    529         self.mapper.addMapping(self.lineEdit_10, WIDGETS.W_NPTS_LOWQ) 
    530         self.mapper.addMapping(self.radioButton, WIDGETS.W_LOWQ_GUINIER) 
    531  
    532         self.mapper.addMapping(self.radioButton_3, WIDGETS.W_LOWQ_FIT) 
    533         self.mapper.addMapping(self.lineEdit_11, WIDGETS.W_LOWQ_POWER_VALUE) 
    534  
    535         self.mapper.addMapping(self.radioButton_7, WIDGETS.W_HIGHQ_FIT) 
    536         self.mapper.addMapping(self.lineEdit_13, WIDGETS.W_HIGHQ_POWER_VALUE) 
     717        self.mapper.addMapping(self.chkLowQ, WIDGETS.W_ENABLE_LOWQ) 
     718        self.mapper.addMapping(self.chkHighQ, WIDGETS.W_ENABLE_HIGHQ) 
     719 
     720        self.mapper.addMapping(self.txtNptsLowQ, WIDGETS.W_NPTS_LOWQ) 
     721        self.mapper.addMapping(self.rbGuinier, WIDGETS.W_LOWQ_GUINIER) 
     722        self.mapper.addMapping(self.rbFitLowQ, WIDGETS.W_LOWQ_FIT) 
     723        self.mapper.addMapping(self.txtPowerLowQ, WIDGETS.W_LOWQ_POWER_VALUE) 
     724 
     725        self.mapper.addMapping(self.txtNptsHighQ, WIDGETS.W_NPTS_HIGHQ) 
     726        self.mapper.addMapping(self.rbFitHighQ, WIDGETS.W_HIGHQ_FIT) 
     727        self.mapper.addMapping(self.txtPowerHighQ, WIDGETS.W_HIGHQ_POWER_VALUE) 
    537728     
    538729        # Output 
    539         self.mapper.addMapping(self.lineEdit_14, WIDGETS.W_VOLUME_FRACTION) 
    540         self.mapper.addMapping(self.lineEdit_15, WIDGETS.W_VOLUME_FRACTION_ERR) 
    541         self.mapper.addMapping(self.lineEdit_16, WIDGETS.W_SPECIFIC_SURFACE) 
    542         self.mapper.addMapping(self.lineEdit_17, WIDGETS.W_SPECIFIC_SURFACE_ERR) 
    543         self.mapper.addMapping(self.lineEdit_19, WIDGETS.W_INVARIANT) 
    544         self.mapper.addMapping(self.lineEdit_18, WIDGETS.W_INVARIANT_ERR) 
    545  
    546         # FIXME DOESNT WORK WITH QT5 
    547         #self.mapper.toFirst() 
    548  
    549     def setData(self, data_item, is_batch=False): 
     730        self.mapper.addMapping(self.txtVolFract, WIDGETS.W_VOLUME_FRACTION) 
     731        self.mapper.addMapping(self.txtVolFractErr, WIDGETS.W_VOLUME_FRACTION_ERR) 
     732        self.mapper.addMapping(self.txtSpecSurf, WIDGETS.W_SPECIFIC_SURFACE) 
     733        self.mapper.addMapping(self.txtSpecSurfErr, WIDGETS.W_SPECIFIC_SURFACE_ERR) 
     734        self.mapper.addMapping(self.txtInvariantTot, WIDGETS.W_INVARIANT) 
     735        self.mapper.addMapping(self.txtInvariantTotErr, WIDGETS.W_INVARIANT_ERR) 
     736 
     737        self.mapper.toFirst() 
     738 
     739    def setData(self, data_item=None, is_batch=False): 
    550740        """ 
    551741        Obtain a QStandardItem object and dissect it to get Data1D/2D 
    552742        Pass it over to the calculator 
    553743        """ 
     744        assert data_item is not None 
     745 
     746        if self.txtName.text() == data_item[0].text(): 
     747            logging.info('This file is already loaded in Invariant panel.') 
     748            return 
     749 
    554750        if not isinstance(data_item, list): 
    555751            msg = "Incorrect type passed to the Invariant Perspective" 
     
    560756            raise AttributeError(msg) 
    561757 
     758        # only 1 file can be loaded 
    562759        self._model_item = data_item[0] 
    563760 
    564761        # Extract data on 1st child - this is the Data1D/2D component 
    565762        data = GuiUtils.dataFromItem(self._model_item) 
    566         self.model.item(WIDGETS.W_FILENAME).setData(self._model_item.text()) 
    567  
    568         ##### DEBUG #### 
    569         # set data in the debug tree view window 
    570         self.treeView.setModel(self.model) 
    571  
    572         self.calculate(data_list=[data]) 
    573          
    574     def calculate(self, data_list=None): 
    575         """ 
    576         receive a list of data and compute invariant 
    577  
    578         TODO: pass warnings/messages to log 
    579         """ 
    580         msg = "" 
    581         data = None 
    582         if data_list is None: 
    583             data_list = [] 
    584         if len(data_list) >= 1: 
    585             if len(data_list) == 1: 
    586                 data = data_list[0] 
    587             else: 
    588                 data_1d_list = [] 
    589                 data_2d_list = [] 
    590                 error_msg = "" 
    591                 # separate data into data1d and data2d list 
    592                 for data in data_list: 
    593                     if data is not None: 
    594                         if issubclass(data.__class__, Data1D): 
    595                             data_1d_list.append(data) 
    596                         else: 
    597                             error_msg += " %s  type %s \n" % (str(data.name), 
    598                                                               str(data.__class__.__name__)) 
    599                             data_2d_list.append(data) 
    600                 if len(data_2d_list) > 0: 
    601                     msg = "Invariant does not support the following data types:\n" 
    602                     msg += error_msg 
    603                 if len(data_1d_list) == 0: 
    604                     # remake this as a qt event 
    605                     #wx.PostEvent(self.parent, StatusEvent(status=msg, info='error')) 
    606                     return 
    607  
    608                 # TODO: add msgbox for data choice 
    609                 #msg += "Invariant panel does not allow multiple data!\n" 
    610                 #msg += "Please select one.\n" 
    611                 #if len(data_list) > 1: 
    612                     #from invariant_widgets import DataDialog 
    613                     #dlg = DataDialog(data_list=data_1d_list, text=msg) 
    614                     #if dlg.ShowModal() == wx.ID_OK: 
    615                     #    data = dlg.get_data() 
    616                     #else: 
    617                     #    data = None 
    618                     #dlg.Destroy() 
    619  
    620             if data is None: 
    621                 msg += "invariant receives no data. \n" 
    622                 #wx.PostEvent(self.parent, StatusEvent(status=msg, info='error')) 
    623                 return 
    624             if not issubclass(data.__class__, Data1D): 
    625                 msg += "invariant cannot be computed for data of " 
    626                 msg += "type %s\n" % (data.__class__.__name__) 
    627                 #wx.PostEvent(self.parent, StatusEvent(status=msg, info='error')) 
    628                 return 
    629             else: 
    630                 #wx.PostEvent(self.parent, NewPlotEvent(plot=data, title=data.title)) 
    631                 try: 
    632                     self._data = data 
    633                     self._path = "unique path" 
    634                     self.calculateInvariant() 
    635                 except: 
    636                     msg = "Invariant Set_data: " + str(sys.exc_info()[1]) 
    637                     #wx.PostEvent(self.parent, StatusEvent(status=msg, info="error")) 
    638         else: 
    639             msg = "invariant cannot be computed for data of " 
    640             msg += "type %s" % (data.__class__.__name__) 
    641             #wx.PostEvent(self.parent, StatusEvent(status=msg, info='error')) 
     763        self.model.item(WIDGETS.W_FILENAME).setData(QtCore.QVariant(self._model_item.text())) 
     764        # update GUI and model with info from loaded data 
     765        self.updateGuiFromFile(data=data) 
     766 
     767    def updateGuiFromFile(self, data=None): 
     768        """ 
     769        update display in GUI and plot 
     770        """ 
     771        self._data = data 
     772 
     773        # plot loaded file 
     774        if not isinstance(self._data, Data1D): 
     775            msg = "Error(s) occurred: Invariant cannot be computed with 2D data." 
     776            raise AttributeError(msg) 
     777 
     778        try: 
     779            filename = data.filename 
     780        except: 
     781            msg = 'No filename' 
     782            raise ValueError(msg) 
     783        try: 
     784            qmin = min(self._data.x) 
     785            qmax = max(self._data.x) 
     786        except: 
     787            msg = "Unable to find q min/max of \n data named %s" % \ 
     788                  data.filename 
     789            raise ValueError(msg) 
     790 
     791        # update model with input form files: filename, qmin, qmax 
     792        self.model.item(WIDGETS.W_FILENAME).setText(filename) 
     793        self.model.item(WIDGETS.W_QMIN).setText(str(qmin)) 
     794        self.model.item(WIDGETS.W_QMAX).setText(str(qmax)) 
     795        self._path = filename 
     796 
     797        # Calculate and add to GUI: volume fraction, invariant total, 
     798        # and specific surface if porod checked 
     799        self.calculateInvariant() 
    642800 
    643801    def allowBatch(self): 
Note: See TracChangeset for help on using the changeset viewer.