source: sasview/src/sas/qtgui/Perspectives/Inversion/InversionPerspective.py @ ded5e77

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since ded5e77 was e90988c, checked in by Piotr Rozyczko <rozyczko@…>, 7 years ago

Show help pages in default browser. Fixed some help links and modified unit tests. SASVIEW-800

  • Property mode set to 100644
File size: 25.1 KB
RevLine 
[fa81e94]1import sys
2import logging
3import pylab
4import numpy as np
5
6from PyQt5 import QtGui, QtCore, QtWidgets
7
8# sas-global
9import sas.qtgui.Utilities.GuiUtils as GuiUtils
10
11# pr inversion GUI elements
12from .InversionUtils import WIDGETS
13from .UI.TabbedInversionUI import Ui_PrInversion
14from .InversionLogic import InversionLogic
15
16# pr inversion calculation elements
17from sas.sascalc.dataloader.data_info import Data1D
18from sas.sascalc.pr.invertor import Invertor
19
20def is_float(value):
21    """Converts text input values to floats. Empty strings throw ValueError"""
22    try:
23        return float(value)
24    except ValueError:
25        return 0.0
26
[50bfab0]27NUMBER_OF_TERMS = 10
28REGULARIZATION = 0.0001
29BACKGROUND_INPUT = 0.0
30MAX_DIST = 140.0
[fa81e94]31
32# TODO: Modify plot references, don't just send new
33# TODO: Update help with batch capabilities
34# TODO: Method to export results in some meaningful way
[d4881f6a]35class InversionWindow(QtWidgets.QDialog, Ui_PrInversion):
[fa81e94]36    """
37    The main window for the P(r) Inversion perspective.
38    """
39
40    name = "Inversion"
[f1ec901]41    estimateSignal = QtCore.pyqtSignal(tuple)
42    estimateNTSignal = QtCore.pyqtSignal(tuple)
43    calculateSignal = QtCore.pyqtSignal(tuple)
[fa81e94]44
45    def __init__(self, parent=None, data=None):
46        super(InversionWindow, self).__init__()
47        self.setupUi(self)
48
49        self.setWindowTitle("P(r) Inversion Perspective")
50
51        self._manager = parent
52        self._model_item = QtGui.QStandardItem()
53        self.communicate = GuiUtils.Communicate()
54
55        self.logic = InversionLogic()
56
[8f83719f]57        # Reference to Dmax window
58        self.dmaxWindow = None
59
[fa81e94]60        # The window should not close
61        self._allow_close = False
62
63        # current QStandardItem showing on the panel
64        self._data = None
65        # current Data1D as referenced by self._data
66        self._data_set = None
67
68        # p(r) calculator
69        self._calculator = Invertor()
70        self._last_calculator = None
71        self.calc_thread = None
72        self.estimation_thread = None
73
74        # Current data object in view
75        self._data_index = 0
76        # list mapping data to p(r) calculation
77        self._data_list = {}
78        if not isinstance(data, list):
79            data_list = [data]
80        if data is not None:
81            for datum in data_list:
82                self._data_list[datum] = self._calculator.clone()
83
[f1ec901]84        # dict of models for quick update after calculation
85        # {item:model}
86        self._models = {}
87
[8f83719f]88        self.calculateAllButton.setEnabled(False)
89        self.calculateThisButton.setEnabled(False)
90
[fa81e94]91        # plots for current data
92        self.pr_plot = None
93        self.data_plot = None
94        # plot references for all data in perspective
95        self.pr_plot_list = {}
96        self.data_plot_list = {}
97
98        self.model = QtGui.QStandardItemModel(self)
99        self.mapper = QtWidgets.QDataWidgetMapper(self)
[8f83719f]100
101        # Add validators
102        self.setupValidators()
[fa81e94]103        # Link user interactions with methods
104        self.setupLinks()
105        # Set values
106        self.setupModel()
107        # Set up the Widget Map
108        self.setupMapper()
109        # Set base window state
110        self.setupWindow()
111
112    ######################################################################
113    # Base Perspective Class Definitions
114
115    def communicator(self):
116        return self.communicate
117
118    def allowBatch(self):
119        return True
120
121    def setClosable(self, value=True):
122        """
123        Allow outsiders close this widget
124        """
125        assert isinstance(value, bool)
126        self._allow_close = value
127
128    def closeEvent(self, event):
129        """
130        Overwrite QDialog close method to allow for custom widget close
131        """
132        if self._allow_close:
133            # reset the closability flag
134            self.setClosable(value=False)
[d4881f6a]135            # Tell the MdiArea to close the container
136            self.parentWidget().close()
[fa81e94]137            event.accept()
138        else:
139            event.ignore()
140            # Maybe we should just minimize
141            self.setWindowState(QtCore.Qt.WindowMinimized)
142
143    ######################################################################
144    # Initialization routines
145
146    def setupLinks(self):
147        """Connect the use controls to their appropriate methods"""
148        self.dataList.currentIndexChanged.connect(self.displayChange)
[f1ec901]149        self.calculateAllButton.clicked.connect(self.startThreadAll)
150        self.calculateThisButton.clicked.connect(self.startThread)
[fa81e94]151        self.removeButton.clicked.connect(self.removeData)
152        self.helpButton.clicked.connect(self.help)
153        self.estimateBgd.toggled.connect(self.toggleBgd)
154        self.manualBgd.toggled.connect(self.toggleBgd)
155        self.regConstantSuggestionButton.clicked.connect(self.acceptAlpha)
156        self.noOfTermsSuggestionButton.clicked.connect(self.acceptNoTerms)
157        self.explorerButton.clicked.connect(self.openExplorerWindow)
[f1ec901]158
159        self.backgroundInput.editingFinished.connect(
[8f83719f]160            lambda: self._calculator.set_est_bck(int(is_float(self.backgroundInput.text()))))
[f1ec901]161        self.minQInput.editingFinished.connect(
[8f83719f]162            lambda: self._calculator.set_qmin(is_float(self.minQInput.text())))
[f1ec901]163        self.regularizationConstantInput.editingFinished.connect(
[8f83719f]164            lambda: self._calculator.set_alpha(is_float(self.regularizationConstantInput.text())))
[f1ec901]165        self.maxDistanceInput.editingFinished.connect(
[8f83719f]166            lambda: self._calculator.set_dmax(is_float(self.maxDistanceInput.text())))
[f1ec901]167        self.maxQInput.editingFinished.connect(
[8f83719f]168            lambda: self._calculator.set_qmax(is_float(self.maxQInput.text())))
[f1ec901]169        self.slitHeightInput.editingFinished.connect(
[8f83719f]170            lambda: self._calculator.set_slit_height(is_float(self.slitHeightInput.text())))
[f1ec901]171        self.slitWidthInput.editingFinished.connect(
[8f83719f]172            lambda: self._calculator.set_slit_width(is_float(self.slitHeightInput.text())))
[f1ec901]173
[fa81e94]174        self.model.itemChanged.connect(self.model_changed)
[f1ec901]175        self.estimateNTSignal.connect(self._estimateNTUpdate)
176        self.estimateSignal.connect(self._estimateUpdate)
177        self.calculateSignal.connect(self._calculateUpdate)
[fa81e94]178
179    def setupMapper(self):
180        # Set up the mapper.
181        self.mapper.setOrientation(QtCore.Qt.Vertical)
182        self.mapper.setModel(self.model)
183
184        # Filename
185        self.mapper.addMapping(self.dataList, WIDGETS.W_FILENAME)
186        # Background
187        self.mapper.addMapping(self.backgroundInput, WIDGETS.W_BACKGROUND_INPUT)
188        self.mapper.addMapping(self.estimateBgd, WIDGETS.W_ESTIMATE)
189        self.mapper.addMapping(self.manualBgd, WIDGETS.W_MANUAL_INPUT)
190
191        # Qmin/Qmax
192        self.mapper.addMapping(self.minQInput, WIDGETS.W_QMIN)
193        self.mapper.addMapping(self.maxQInput, WIDGETS.W_QMAX)
194
195        # Slit Parameter items
196        self.mapper.addMapping(self.slitWidthInput, WIDGETS.W_SLIT_WIDTH)
197        self.mapper.addMapping(self.slitHeightInput, WIDGETS.W_SLIT_HEIGHT)
198
199        # Parameter Items
[8f83719f]200        self.mapper.addMapping(self.regularizationConstantInput, WIDGETS.W_REGULARIZATION)
201        self.mapper.addMapping(self.regConstantSuggestionButton, WIDGETS.W_REGULARIZATION_SUGGEST)
[fa81e94]202        self.mapper.addMapping(self.explorerButton, WIDGETS.W_EXPLORE)
203        self.mapper.addMapping(self.maxDistanceInput, WIDGETS.W_MAX_DIST)
204        self.mapper.addMapping(self.noOfTermsInput, WIDGETS.W_NO_TERMS)
[8f83719f]205        self.mapper.addMapping(self.noOfTermsSuggestionButton, WIDGETS.W_NO_TERMS_SUGGEST)
[fa81e94]206
207        # Output
208        self.mapper.addMapping(self.rgValue, WIDGETS.W_RG)
209        self.mapper.addMapping(self.iQ0Value, WIDGETS.W_I_ZERO)
210        self.mapper.addMapping(self.backgroundValue, WIDGETS.W_BACKGROUND_OUTPUT)
211        self.mapper.addMapping(self.computationTimeValue, WIDGETS.W_COMP_TIME)
212        self.mapper.addMapping(self.chiDofValue, WIDGETS.W_CHI_SQUARED)
213        self.mapper.addMapping(self.oscillationValue, WIDGETS.W_OSCILLATION)
214        self.mapper.addMapping(self.posFractionValue, WIDGETS.W_POS_FRACTION)
[8f83719f]215        self.mapper.addMapping(self.sigmaPosFractionValue, WIDGETS.W_SIGMA_POS_FRACTION)
[fa81e94]216
217        # Main Buttons
218        self.mapper.addMapping(self.removeButton, WIDGETS.W_REMOVE)
[f1ec901]219        self.mapper.addMapping(self.calculateAllButton, WIDGETS.W_CALCULATE_ALL)
[8f83719f]220        self.mapper.addMapping(self.calculateThisButton, WIDGETS.W_CALCULATE_VISIBLE)
[fa81e94]221        self.mapper.addMapping(self.helpButton, WIDGETS.W_HELP)
222
223        self.mapper.toFirst()
224
225    def setupModel(self):
226        """
227        Update boxes with initial values
228        """
229        item = QtGui.QStandardItem("")
230        self.model.setItem(WIDGETS.W_FILENAME, item)
[50bfab0]231        item = QtGui.QStandardItem(str(BACKGROUND_INPUT))
[fa81e94]232        self.model.setItem(WIDGETS.W_BACKGROUND_INPUT, item)
233        item = QtGui.QStandardItem("")
234        self.model.setItem(WIDGETS.W_QMIN, item)
235        item = QtGui.QStandardItem("")
236        self.model.setItem(WIDGETS.W_QMAX, item)
237        item = QtGui.QStandardItem("")
238        self.model.setItem(WIDGETS.W_SLIT_WIDTH, item)
239        item = QtGui.QStandardItem("")
240        self.model.setItem(WIDGETS.W_SLIT_HEIGHT, item)
[50bfab0]241        item = QtGui.QStandardItem(str(NUMBER_OF_TERMS))
[fa81e94]242        self.model.setItem(WIDGETS.W_NO_TERMS, item)
[50bfab0]243        item = QtGui.QStandardItem(str(REGULARIZATION))
[fa81e94]244        self.model.setItem(WIDGETS.W_REGULARIZATION, item)
[50bfab0]245        item = QtGui.QStandardItem(str(MAX_DIST))
[fa81e94]246        self.model.setItem(WIDGETS.W_MAX_DIST, item)
247        item = QtGui.QStandardItem("")
248        self.model.setItem(WIDGETS.W_RG, item)
249        item = QtGui.QStandardItem("")
250        self.model.setItem(WIDGETS.W_I_ZERO, item)
251        item = QtGui.QStandardItem("")
252        self.model.setItem(WIDGETS.W_BACKGROUND_OUTPUT, item)
253        item = QtGui.QStandardItem("")
254        self.model.setItem(WIDGETS.W_COMP_TIME, item)
255        item = QtGui.QStandardItem("")
256        self.model.setItem(WIDGETS.W_CHI_SQUARED, item)
257        item = QtGui.QStandardItem("")
258        self.model.setItem(WIDGETS.W_OSCILLATION, item)
259        item = QtGui.QStandardItem("")
260        self.model.setItem(WIDGETS.W_POS_FRACTION, item)
261        item = QtGui.QStandardItem("")
262        self.model.setItem(WIDGETS.W_SIGMA_POS_FRACTION, item)
263
264    def setupWindow(self):
265        """Initialize base window state on init"""
266        self.enableButtons()
267        self.estimateBgd.setChecked(True)
268
[8f83719f]269    def setupValidators(self):
270        """Apply validators to editable line edits"""
271        self.noOfTermsInput.setValidator(QtGui.QIntValidator())
272        self.regularizationConstantInput.setValidator(GuiUtils.DoubleValidator())
273        self.maxDistanceInput.setValidator(GuiUtils.DoubleValidator())
274        self.minQInput.setValidator(GuiUtils.DoubleValidator())
275        self.maxQInput.setValidator(GuiUtils.DoubleValidator())
276        self.slitHeightInput.setValidator(GuiUtils.DoubleValidator())
277        self.slitWidthInput.setValidator(GuiUtils.DoubleValidator())
278
[fa81e94]279    ######################################################################
280    # Methods for updating GUI
281
282    def enableButtons(self):
283        """
284        Enable buttons when data is present, else disable them
285        """
[8f83719f]286        self.calculateAllButton.setEnabled(self.logic.data_is_loaded)
287        self.calculateThisButton.setEnabled(self.logic.data_is_loaded)
[fa81e94]288        self.removeButton.setEnabled(self.logic.data_is_loaded)
289        self.explorerButton.setEnabled(self.logic.data_is_loaded)
290
291    def populateDataComboBox(self, filename, data_ref):
292        """
293        Append a new file name to the data combobox
294        :param filename: data filename
295        :param data_ref: QStandardItem reference for data set to be added
296        """
[6a3e1fe]297        self.dataList.addItem(filename, data_ref)
[fa81e94]298
299    def acceptNoTerms(self):
300        """Send estimated no of terms to input"""
301        self.model.setItem(WIDGETS.W_NO_TERMS, QtGui.QStandardItem(
302            self.noOfTermsSuggestionButton.text()))
303
304    def acceptAlpha(self):
305        """Send estimated alpha to input"""
306        self.model.setItem(WIDGETS.W_REGULARIZATION, QtGui.QStandardItem(
307            self.regConstantSuggestionButton.text()))
308
309    def displayChange(self):
[f1ec901]310        ref_item = self.dataList.itemData(self.dataList.currentIndex())
311        self._model_item = ref_item
312        self.setCurrentData(ref_item)
313        self.setCurrentModel(ref_item)
[fa81e94]314
315    def removeData(self):
316        """Remove the existing data reference from the P(r) Persepective"""
317        self._data_list.pop(self._data)
318        self.pr_plot_list.pop(self._data)
319        self.data_plot_list.pop(self._data)
[8f83719f]320        if self.dmaxWindow is not None:
321            self.dmaxWindow = None
[fa81e94]322        self.dataList.removeItem(self.dataList.currentIndex())
323        self.dataList.setCurrentIndex(0)
[8f83719f]324        # Last file removed
325        if not self._data_list:
326            self._data = None
327            self.pr_plot = None
328            self._data_set = None
329            self.calculateThisButton.setEnabled(False)
330            self.calculateAllButton.setEnabled(False)
331            self.explorerButton.setEnabled(False)
[fa81e94]332
333    ######################################################################
334    # GUI Interaction Events
335
[f1ec901]336    def setCurrentModel(self, ref_item):
337        '''update the current model with stored values'''
338        if ref_item in self._models:
339            self.model = self._models[ref_item]
340
[fa81e94]341    def update_calculator(self):
342        """Update all p(r) params"""
343        self._calculator.set_x(self._data_set.x)
344        self._calculator.set_y(self._data_set.y)
345        self._calculator.set_err(self._data_set.dy)
346
347    def model_changed(self):
348        """Update the values when user makes changes"""
349        if not self.mapper:
350            msg = "Unable to update P{r}. The connection between the main GUI "
351            msg += "and P(r) was severed. Attempting to restart P(r)."
352            logging.warning(msg)
353            self.setClosable(True)
354            self.close()
355            InversionWindow.__init__(self.parent(), list(self._data_list.keys()))
356            exit(0)
357        # TODO: Only send plot first time - otherwise, update in complete
358        if self.pr_plot is not None:
359            title = self.pr_plot.name
[6a3e1fe]360            GuiUtils.updateModelItemWithPlot(self._data, self.pr_plot, title)
[fa81e94]361        if self.data_plot is not None:
362            title = self.data_plot.name
[6a3e1fe]363            GuiUtils.updateModelItemWithPlot(self._data, self.data_plot, title)
[8f83719f]364        if self.dmaxWindow is not None:
365             self.dmaxWindow.pr_state = self._calculator
366             self.dmaxWindow.nfunc = self.getNFunc()
367
[fa81e94]368        self.mapper.toFirst()
369
370    def help(self):
371        """
372        Open the P(r) Inversion help browser
373        """
[e90988c]374        tree_location = "/user/sasgui/perspectives/pr/pr_help.html"
[fa81e94]375
376        # Actual file anchor will depend on the combo box index
377        # Note that we can be clusmy here, since bad current_fitter_id
378        # will just make the page displayed from the top
[e90988c]379        self._manager.showHelp(tree_location)
[fa81e94]380
381    def toggleBgd(self):
382        """
383        Toggle the background between manual and estimated
384        """
385        sender = self.sender()
386        if sender is self.estimateBgd:
387            self.backgroundInput.setEnabled(False)
388        else:
389            self.backgroundInput.setEnabled(True)
390
391    def openExplorerWindow(self):
392        """
393        Open the Explorer window to see correlations between params and results
394        """
395        from .DMaxExplorerWidget import DmaxWindow
396        self.dmaxWindow = DmaxWindow(self._calculator, self.getNFunc(), self)
397        self.dmaxWindow.show()
398
399    ######################################################################
400    # Response Actions
401
402    def setData(self, data_item=None, is_batch=False):
403        """
404        Assign new data set(s) to the P(r) perspective
405        Obtain a QStandardItem object and parse it to get Data1D/2D
406        Pass it over to the calculator
407        """
408        assert data_item is not None
409
410        if not isinstance(data_item, list):
411            msg = "Incorrect type passed to the P(r) Perspective"
412            raise AttributeError
413
414        for data in data_item:
[8f83719f]415            if data in self._data_list.keys():
416                # Don't add data if it's already in
417                return
[fa81e94]418            # Create initial internal mappings
[6a3e1fe]419            self._data_list[data] = self._calculator.clone()
[fa81e94]420            self._data_set = GuiUtils.dataFromItem(data)
421            self.data_plot_list[data] = self.data_plot
422            self.pr_plot_list[data] = self.pr_plot
[6a3e1fe]423            self.populateDataComboBox(self._data_set.filename, data)
[fa81e94]424            self.setCurrentData(data)
425
426            # Estimate initial values from data
427            self.performEstimate()
428            self.logic = InversionLogic(self._data_set)
429
430            # Estimate q range
431            qmin, qmax = self.logic.computeDataRange()
[6a3e1fe]432            self.model.setItem(WIDGETS.W_QMIN, QtGui.QStandardItem("{:.4g}".format(qmin)))
433            self.model.setItem(WIDGETS.W_QMAX, QtGui.QStandardItem("{:.4g}".format(qmax)))
[f1ec901]434            self._models[data] = self.model
435            self.model_item = data
[fa81e94]436
437        self.enableButtons()
438
439    def getNFunc(self):
440        """Get the n_func value from the GUI object"""
[50bfab0]441        try:
442            nfunc = int(self.noOfTermsInput.text())
443        except ValueError:
444            logging.error("Incorrect number of terms specified: %s" %self.noOfTermsInput.text())
445            self.noOfTermsInput.setText(str(NUMBER_OF_TERMS))
446            nfunc = NUMBER_OF_TERMS
447        return nfunc
[fa81e94]448
449    def setCurrentData(self, data_ref):
450        """Get the current data and display as necessary"""
451
[6a3e1fe]452        if data_ref is None:
453            return
454
[fa81e94]455        if not isinstance(data_ref, QtGui.QStandardItem):
456            msg = "Incorrect type passed to the P(r) Perspective"
457            raise AttributeError
458
459        # Data references
460        self._data = data_ref
461        self._data_set = GuiUtils.dataFromItem(data_ref)
462        self._calculator = self._data_list[data_ref]
463        self.pr_plot = self.pr_plot_list[data_ref]
464        self.data_plot = self.data_plot_list[data_ref]
465
466    ######################################################################
467    # Thread Creators
468    def startThreadAll(self):
469        for data_ref, pr in list(self._data_list.items()):
470            self._data_set = GuiUtils.dataFromItem(data_ref)
471            self._calculator = pr
472            self.startThread()
473
474    def startThread(self):
475        """
476            Start a calculation thread
477        """
478        from .Thread import CalcPr
479
480        # Set data before running the calculations
481        self.update_calculator()
482
483        # If a thread is already started, stop it
484        if self.calc_thread is not None and self.calc_thread.isrunning():
485            self.calc_thread.stop()
486        pr = self._calculator.clone()
487        nfunc = self.getNFunc()
488        self.calc_thread = CalcPr(pr, nfunc,
489                                  error_func=self._threadError,
[f1ec901]490                                  completefn=self._calculateCompleted,
491                                  updatefn=None)
[fa81e94]492        self.calc_thread.queue()
493        self.calc_thread.ready(2.5)
494
495    def performEstimateNT(self):
496        """
[f1ec901]497        Perform parameter estimation
[fa81e94]498        """
499        from .Thread import EstimateNT
500
501        # If a thread is already started, stop it
502        if (self.estimation_thread is not None and
503                self.estimation_thread.isrunning()):
504            self.estimation_thread.stop()
505        pr = self._calculator.clone()
506        # Skip the slit settings for the estimation
507        # It slows down the application and it doesn't change the estimates
508        pr.slit_height = 0.0
509        pr.slit_width = 0.0
510        nfunc = self.getNFunc()
[f1ec901]511
[fa81e94]512        self.estimation_thread = EstimateNT(pr, nfunc,
513                                            error_func=self._threadError,
514                                            completefn=self._estimateNTCompleted,
515                                            updatefn=None)
516        self.estimation_thread.queue()
517        self.estimation_thread.ready(2.5)
518
519    def performEstimate(self):
520        """
521            Perform parameter estimation
522        """
523        from .Thread import EstimatePr
524
525        self.startThread()
526
527        # If a thread is already started, stop it
528        if (self.estimation_thread is not None and
529                self.estimation_thread.isrunning()):
530            self.estimation_thread.stop()
531        pr = self._calculator.clone()
532        nfunc = self.getNFunc()
533        self.estimation_thread = EstimatePr(pr, nfunc,
534                                            error_func=self._threadError,
535                                            completefn=self._estimateCompleted,
536                                            updatefn=None)
537        self.estimation_thread.queue()
538        self.estimation_thread.ready(2.5)
539
540    ######################################################################
541    # Thread Complete
542
543    def _estimateCompleted(self, alpha, message, elapsed):
[f1ec901]544        ''' Send a signal to the main thread for model update'''
545        self.estimateSignal.emit((alpha, message, elapsed))
546
547    def _estimateUpdate(self, output_tuple):
[fa81e94]548        """
549        Parameter estimation completed,
550        display the results to the user
551
552        :param alpha: estimated best alpha
553        :param elapsed: computation time
554        """
[f1ec901]555        alpha, message, elapsed = output_tuple
[fa81e94]556        # Save useful info
[8f83719f]557        self.model.setItem(WIDGETS.W_COMP_TIME, QtGui.QStandardItem("{:.4g}".format(elapsed)))
[6a3e1fe]558        self.regConstantSuggestionButton.setText("{:-3.2g}".format(alpha))
[fa81e94]559        self.regConstantSuggestionButton.setEnabled(True)
560        if message:
561            logging.info(message)
562        self.performEstimateNT()
563
564    def _estimateNTCompleted(self, nterms, alpha, message, elapsed):
[f1ec901]565        ''' Send a signal to the main thread for model update'''
566        self.estimateNTSignal.emit((nterms, alpha, message, elapsed))
567
568    def _estimateNTUpdate(self, output_tuple):
[fa81e94]569        """
570        Parameter estimation completed,
571        display the results to the user
572
573        :param alpha: estimated best alpha
574        :param nterms: estimated number of terms
575        :param elapsed: computation time
576        """
[f1ec901]577        nterms, alpha, message, elapsed = output_tuple
[fa81e94]578        # Save useful info
[6a3e1fe]579        self.noOfTermsSuggestionButton.setText("{:n}".format(nterms))
[fa81e94]580        self.noOfTermsSuggestionButton.setEnabled(True)
[6a3e1fe]581        self.regConstantSuggestionButton.setText("{:.3g}".format(alpha))
[fa81e94]582        self.regConstantSuggestionButton.setEnabled(True)
[19fce84]583        self.model.setItem(WIDGETS.W_COMP_TIME, QtGui.QStandardItem("{:.2g}".format(elapsed)))
[fa81e94]584        if message:
585            logging.info(message)
586
[f1ec901]587    def _calculateCompleted(self, out, cov, pr, elapsed):
588        ''' Send a signal to the main thread for model update'''
589        self.calculateSignal.emit((out, cov, pr, elapsed))
590
591    def _calculateUpdate(self, output_tuple):
[fa81e94]592        """
593        Method called with the results when the inversion is done
594
595        :param out: output coefficient for the base functions
596        :param cov: covariance matrix
597        :param pr: Invertor instance
598        :param elapsed: time spent computing
599        """
[f1ec901]600        out, cov, pr, elapsed = output_tuple
[fa81e94]601        # Save useful info
602        cov = np.ascontiguousarray(cov)
603        pr.cov = cov
604        pr.out = out
605        pr.elapsed = elapsed
606
607        # Show result on control panel
[8f83719f]608        self.model.setItem(WIDGETS.W_RG, QtGui.QStandardItem("{:.3g}".format(pr.rg(out))))
609        self.model.setItem(WIDGETS.W_I_ZERO, QtGui.QStandardItem("{:.3g}".format(pr.iq0(out))))
[fa81e94]610        self.model.setItem(WIDGETS.W_BACKGROUND_INPUT,
611                           QtGui.QStandardItem("{:.3f}".format(pr.est_bck)))
[8f83719f]612        self.model.setItem(WIDGETS.W_BACKGROUND_OUTPUT, QtGui.QStandardItem("{:.3g}".format(pr.background)))
613        self.model.setItem(WIDGETS.W_CHI_SQUARED, QtGui.QStandardItem("{:.3g}".format(pr.chi2[0])))
614        self.model.setItem(WIDGETS.W_COMP_TIME, QtGui.QStandardItem("{:.2g}".format(elapsed)))
615        self.model.setItem(WIDGETS.W_OSCILLATION, QtGui.QStandardItem("{:.3g}".format(pr.oscillations(out))))
616        self.model.setItem(WIDGETS.W_POS_FRACTION, QtGui.QStandardItem("{:.3g}".format(pr.get_positive(out))))
[fa81e94]617        self.model.setItem(WIDGETS.W_SIGMA_POS_FRACTION,
[8f83719f]618                           QtGui.QStandardItem("{:.3g}".format(pr.get_pos_err(out, cov))))
[fa81e94]619
620        # Save Pr invertor
621        self._calculator = pr
622        # Append data to data list
623        self._data_list[self._data] = self._calculator.clone()
624
[f1ec901]625        # Update model dict
626        self._models[self.model_item] = self.model
627
[fa81e94]628        # Create new P(r) and fit plots
629        if self.pr_plot is None:
630            self.pr_plot = self.logic.newPRPlot(out, self._calculator, cov)
631            self.pr_plot_list[self._data] = self.pr_plot
632        else:
633            # FIXME: this should update the existing plot, not create a new one
634            self.pr_plot = self.logic.newPRPlot(out, self._calculator, cov)
635            self.pr_plot_list[self._data] = self.pr_plot
636        if self.data_plot is None:
637            self.data_plot = self.logic.new1DPlot(out, self._calculator)
638            self.data_plot_list[self._data] = self.data_plot
639        else:
640            # FIXME: this should update the existing plot, not create a new one
641            self.data_plot = self.logic.new1DPlot(out, self._calculator)
642            self.data_plot_list[self._data] = self.data_plot
643
644    def _threadError(self, error):
645        """
646            Call-back method for calculation errors
647        """
648        logging.warning(error)
Note: See TracBrowser for help on using the repository browser.