Source code for PyMca5.PyMcaGui.math.fitting.SimpleFitGui

#/*##########################################################################
# Copyright (C) 2004-2014 V.A. Sole, European Synchrotron Radiation Facility
#
# This file is part of the PyMca X-ray Fluorescence Toolkit developed at
# the ESRF by the Software group.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#############################################################################*/
__author__ = "V.A. Sole - ESRF Data Analysis"
__contact__ = "sole@esrf.fr"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
import sys
import os
from PyMca5.PyMcaGui import PyMcaQt as qt
from PyMca5.PyMcaMath.fitting import SimpleFitModule
from . import SimpleFitConfigurationGui
from PyMca5.PyMcaMath.fitting import SimpleFitUserEstimatedFunctions
from . import Parameters
from PyMca5.PyMcaGui import PlotWindow

DEBUG = 0

[docs]class TopWidget(qt.QWidget): def __init__(self, parent=None): qt.QWidget.__init__(self, parent) self.mainLayout = qt.QGridLayout(self) self.mainLayout.setContentsMargins(2, 2, 2, 2) self.mainLayout.setSpacing(2) font = qt.QFont(self.font()) font.setBold(True) #Function handling self.functionLabel = qt.QLabel(self) self.functionLabel.setFont(font) self.functionLabel.setText("Function:") self.addFunctionButton = qt.QPushButton(self) self.addFunctionButton.setText("ADD") #fit function self.fitFunctionLabel = qt.QLabel(self) self.fitFunctionLabel.setFont(font) self.fitFunctionLabel.setText("Fit:") self.fitFunctionCombo = qt.QComboBox(self) self.fitFunctionCombo.addItem(qt.safe_str("None")) self.fitFunctionCombo.setSizeAdjustPolicy(qt.QComboBox.AdjustToContents) self.fitFunctionCombo.setMinimumWidth(100) #background function self.backgroundLabel = qt.QLabel(self) self.backgroundLabel.setFont(font) self.backgroundLabel.setText("Background:") self.backgroundCombo = qt.QComboBox(self) self.backgroundCombo.addItem(qt.safe_str("None")) self.backgroundCombo.setSizeAdjustPolicy(qt.QComboBox.AdjustToContents) self.backgroundCombo.setMinimumWidth(100) #arrange everything self.mainLayout.addWidget(self.functionLabel, 0, 0) self.mainLayout.addWidget(self.addFunctionButton, 0, 1) self.mainLayout.addWidget(qt.HorizontalSpacer(self), 0, 2) self.mainLayout.addWidget(self.fitFunctionLabel, 0, 3) self.mainLayout.addWidget(self.fitFunctionCombo, 0, 4) self.mainLayout.addWidget(qt.HorizontalSpacer(self), 0, 5) self.mainLayout.addWidget(self.backgroundLabel, 0, 6) self.mainLayout.addWidget(self.backgroundCombo, 0, 7) self.configureButton = qt.QPushButton(self) self.configureButton.setText("CONFIGURE") self.mainLayout.addWidget(self.configureButton, 0, 8)
[docs] def setFunctions(self, functionList): currentFunction = qt.safe_str(self.fitFunctionCombo.currentText()) currentBackground = qt.safe_str(self.backgroundCombo.currentText()) self.fitFunctionCombo.clear() self.backgroundCombo.clear() self.fitFunctionCombo.addItem('None') self.backgroundCombo.addItem('None') for key in functionList: self.fitFunctionCombo.addItem(qt.safe_str(key)) self.backgroundCombo.addItem(qt.safe_str(key)) #restore previous values idx = self.fitFunctionCombo.findText(currentFunction) self.fitFunctionCombo.setCurrentIndex(idx) idx = self.backgroundCombo.findText(currentBackground) self.backgroundCombo.setCurrentIndex(idx)
[docs]class StatusWidget(qt.QWidget): def __init__(self, parent=None): qt.QWidget.__init__(self, parent) self.mainLayout = qt.QHBoxLayout(self) self.mainLayout.setContentsMargins(2, 2, 2, 2) self.mainLayout.setSpacing(2) self.statusLabel = qt.QLabel(self) self.statusLabel.setText(qt.safe_str("Status:")) self.statusLine = qt.QLineEdit(self) self.statusLine.setText(qt.safe_str("Ready")) self.statusLine.setReadOnly(1) self.chi2Label = qt.QLabel(self) self.chi2Label.setText(qt.safe_str("Reduced Chi Square:")) self.chi2Line = qt.QLineEdit(self) self.chi2Line.setText(qt.safe_str("")) self.chi2Line.setReadOnly(1) self.mainLayout.addWidget(self.statusLabel) self.mainLayout.addWidget(self.statusLine) self.mainLayout.addWidget(self.chi2Label) self.mainLayout.addWidget(self.chi2Line)
[docs]class SimpleFitGui(qt.QWidget): sigSimpleFitSignal = qt.pyqtSignal(object) def __init__(self, parent=None, fit=None, graph=None, actions=True): qt.QWidget.__init__(self, parent) self.setWindowTitle("SimpleFitGui") if fit is None: self.fitModule = SimpleFitModule.SimpleFit() self.fitModule.importFunctions(SimpleFitUserEstimatedFunctions) else: self.fitModule = fit if graph is None: self.__useTab = True self.graph = PlotWindow.PlotWindow(newplot=False, plugins=False, fit=False) else: self.__useTab = False self.graph = graph self._configurationDialog = None self.mainLayout = qt.QVBoxLayout(self) self.mainLayout.setContentsMargins(2, 2, 2, 2) self.mainLayout.setSpacing(2) self.topWidget = TopWidget(self) config = self.fitModule.getConfiguration() self.topWidget.setFunctions(config['fit']['functions']) config = None if self.__useTab: self.mainTab = qt.QTabWidget(self) self.mainLayout.addWidget(self.mainTab) self.parametersTable = Parameters.Parameters() self.mainTab.addTab(self.graph, 'GRAPH') self.mainTab.addTab(self.parametersTable, 'FIT') else: self.parametersTable = Parameters.Parameters(self) self.statusWidget = StatusWidget(self) self.mainLayout.addWidget(self.topWidget) if self.__useTab: self.mainLayout.addWidget(self.mainTab) else: self.mainLayout.addWidget(self.parametersTable) self.mainLayout.addWidget(self.statusWidget) if actions: #build the actions widget self.fitActions = qt.QWidget(self) self.fitActions.mainLayout = qt.QHBoxLayout(self.fitActions) self.fitActions.mainLayout.setContentsMargins(2, 2, 2, 2) self.fitActions.mainLayout.setSpacing(2) self.fitActions.estimateButton = qt.QPushButton(self.fitActions) self.fitActions.estimateButton.setText("Estimate") self.fitActions.startFitButton = qt.QPushButton(self.fitActions) self.fitActions.startFitButton.setText("Start Fit") self.fitActions.dismissButton = qt.QPushButton(self.fitActions) self.fitActions.dismissButton.setText("Dismiss") self.fitActions.mainLayout.addWidget(self.fitActions.estimateButton) self.fitActions.mainLayout.addWidget(self.fitActions.startFitButton) self.fitActions.mainLayout.addWidget(self.fitActions.dismissButton) self.mainLayout.addWidget(self.fitActions) #connect top widget self.topWidget.addFunctionButton.clicked.connect(\ self.importFunctions) self.topWidget.fitFunctionCombo.currentIndexChanged[int].connect(\ self.fitFunctionComboSlot) self.topWidget.backgroundCombo.currentIndexChanged[int].connect(\ self.backgroundComboSlot) self.topWidget.configureButton.clicked.connect(\ self.configureButtonSlot) if actions: #connect actions self.fitActions.estimateButton.clicked.connect(self.estimate) self.fitActions.startFitButton.clicked.connect(self.startFit) self.fitActions.dismissButton.clicked.connect(self.dismiss)
[docs] def importFunctions(self, functionsfile=None): if functionsfile is None: fn = qt.QFileDialog.getOpenFileName() if fn.isEmpty(): functionsfile = "" else: functionsfile= qt.safe_str(fn) if not len(functionsfile): return if DEBUG: self.fitModule.importFunctions(functionsfile) else: try: self.fitModule.importFunctions(functionsfile) except: qt.QMessageBox.critical(self, "ERROR", "Function not imported") config = self.fitModule.getConfiguration() self.topWidget.setFunctions(config['fit']['functions'])
[docs] def fitFunctionComboSlot(self, idx): if idx <= 0: fname = "None" else: fname = qt.safe_str(self.topWidget.fitFunctionCombo.itemText(idx)) self.fitModule.setFitFunction(fname)
[docs] def backgroundComboSlot(self, idx): if idx <= 0: fname = "None" else: fname = qt.safe_str(self.topWidget.backgroundCombo.itemText(idx)) self.setBackgroundFunction(fname)
[docs] def configureButtonSlot(self): if self._configurationDialog is None: self._configurationDialog =\ SimpleFitConfigurationGui.SimpleFitConfigurationGui() self._configurationDialog.setSimpleFitInstance(self.fitModule) if not self._configurationDialog.exec_(): if DEBUG: print("NOT UPDATING CONFIGURATION") oldConfig = self.fitModule.getConfiguration() self._configurationDialog.setConfiguration(oldConfig) return newConfig = self._configurationDialog.getConfiguration() self.topWidget.setFunctions(newConfig['fit']['functions']) self.fitModule.setConfiguration(newConfig) newConfig = self.fitModule.getConfiguration() #self.topWidget.setFunctions(newConfig['fit']['functions']) fname = self.fitModule.getFitFunction() if fname in [None, "None", "NONE"]: idx = 0 else: idx = newConfig['fit']['functions'].index(fname) + 1 self.topWidget.fitFunctionCombo.setCurrentIndex(idx) fname = self.fitModule.getBackgroundFunction() if fname in [None, "None", "NONE"]: idx = 0 else: idx = newConfig['fit']['functions'].index(fname) + 1 idx = self.topWidget.backgroundCombo.findText(fname) self.topWidget.backgroundCombo.setCurrentIndex(idx) if DEBUG: print("TABLE TO BE CLEANED")
#self.estimate()
[docs] def setFitFunction(self, fname): current = self.fitModule.getFitFunction() if current != fname: self.fitModule.setFitFunction(fname) idx = self.topWidget.fitFunctionCombo.findText(fname) self.topWidget.fitFunctionCombo.setCurrentIndex(idx)
[docs] def setBackgroundFunction(self, fname): current = self.fitModule.getBackgroundFunction() if current != fname: self.fitModule.setBackgroundFunction(fname) idx = self.topWidget.backgroundCombo.findText(fname) self.topWidget.backgroundCombo.setCurrentIndex(idx)
[docs] def setData(self, *var, **kw): returnValue = self.fitModule.setData(*var, **kw) if self.__useTab: if hasattr(self.graph, "addCurve"): self.graph.addCurve(self.fitModule._x, self.fitModule._y, legend='Data', replace=True) elif hasattr(self.graph, "newCurve"): self.graph.clearCurves() self.graph.newCurve('Data', self.fitModule._x, self.fitModule._y) self.graph.replot() return returnValue
[docs] def estimate(self): self.setStatus("Estimate started") self.statusWidget.chi2Line.setText("") try: x = self.fitModule._x y = self.fitModule._y if hasattr(self.graph, "addCurve"): self.graph.addCurve(x, y, 'Data') elif hasattr(self.graph, "newCurve"): self.graph.newCurve('Data', x, y) self.graph.removeCurve("Fit") self.graph.removeCurve("Background", replot=True) self.fitModule.estimate() self.setStatus() self.parametersTable.fillTableFromFit(self.fitModule.paramlist) except: if DEBUG: raise text = "%s:%s" % (sys.exc_info()[0], sys.exc_info()[1]) msg = qt.QMessageBox(self) msg.setIcon(qt.QMessageBox.Critical) msg.setText(text) msg.exec_() self.setStatus("Ready (after estimate error)")
[docs] def setStatus(self, text=None): if text is None: text = "%s" % self.fitModule.getStatus() self.statusWidget.statusLine.setText(text)
[docs] def startFit(self):
#get parameters from table self.fitModule.paramlist = self.parametersTable.fillFitFromTable() try: values,chisq,sigma,niter,lastdeltachi = self.fitModule.startFit() self.setStatus() except: if DEBUG: raise text = "%s:%s" % (sys.exc_info()[0], sys.exc_info()[1]) msg = qt.QMessageBox(self) msg.setIcon(qt.QMessageBox.Critical) msg.setText(text) msg.exec_() self.setStatus("Ready (after fit error)") return self.parametersTable.fillTableFromFit(self.fitModule.paramlist) self.statusWidget.chi2Line.setText("%f" % chisq) ddict = {} ddict['event'] = "FitFinished" ddict['x'] = self.fitModule._x ddict['y'] = self.fitModule._y ddict['yfit'] = self.evaluateDefinedFunction() self.sigSimpleFitSignal.emit(ddict) self.updateGraph()
[docs] def updateGraph(self):
#this is to be overwritten and for test purposes if self.graph is None: return ddict = {} ddict['event'] = "FitFinished" ddict['x'] = self.fitModule._x ddict['y'] = self.fitModule._y ddict['yfit'] = self.evaluateDefinedFunction() ddict['background'] = self.fitModule._evaluateBackground() if hasattr(self.graph, "addCurve"): self.graph.addCurve(ddict['x'], ddict['y'], 'Data') self.graph.addCurve(ddict['x'], ddict['yfit'], 'Fit') self.graph.addCurve(ddict['x'], ddict['background'], 'Background') elif hasattr(self.graph, "newCurve"): self.graph.newCurve('Data', ddict['x'], ddict['y']) self.graph.newCurve('Fit', ddict['x'], ddict['yfit']) self.graph.newCurve('Background', ddict['x'], ddict['background']) self.graph.replot() self.graph.show()
[docs] def dismiss(self): self.close()
[docs] def evaluateDefinedFunction(self, x=None): return self.fitModule.evaluateDefinedFunction()
[docs]def test(): import numpy #import DefaultFitFunctions as SpecfitFunctions from PyMca import SpecfitFunctions a=SpecfitFunctions.SpecfitFunctions() x = numpy.arange(1000).astype(numpy.float) p1 = numpy.array([1500,100.,50.0]) p2 = numpy.array([1500,700.,50.0]) y = a.gauss(p1, x) y = y + a.gauss(p2,x) + x * 5. if 0: fit = SimpleFitModule.SimpleFit() fit.importFunctions(SpecfitFunctions) fit.setFitFunction('Gaussians') #fit.setBackgroundFunction('Gaussians') #fit.setBackgroundFunction('Constant') fit.setData(x, y) w = SimpleFitGui(fit=fit) w.show() else: fit=None w = SimpleFitGui(fit=fit) w.setData(x, y, xmin=x[0], xmax=x[-1]) w.show() import SimpleFitUserEstimatedFunctions fname = SimpleFitUserEstimatedFunctions.__file__ w.importFunctions(fname) w.setFitFunction('User Estimated Gaussians') return w
if __name__=="__main__": DEBUG = 0 app = qt.QApplication([]) w = test() app.exec_()