summaryrefslogtreecommitdiff
path: root/silx/gui/fit/FitWidget.py
diff options
context:
space:
mode:
authorPicca Frédéric-Emmanuel <picca@synchrotron-soleil.fr>2017-08-18 14:48:52 +0200
committerPicca Frédéric-Emmanuel <picca@synchrotron-soleil.fr>2017-08-18 14:48:52 +0200
commitf7bdc2acff3c13a6d632c28c4569690ab106eed7 (patch)
tree9d67cdb7152ee4e711379e03fe0546c7c3b97303 /silx/gui/fit/FitWidget.py
Import Upstream version 0.5.0+dfsg
Diffstat (limited to 'silx/gui/fit/FitWidget.py')
-rw-r--r--silx/gui/fit/FitWidget.py727
1 files changed, 727 insertions, 0 deletions
diff --git a/silx/gui/fit/FitWidget.py b/silx/gui/fit/FitWidget.py
new file mode 100644
index 0000000..a5c3cfd
--- /dev/null
+++ b/silx/gui/fit/FitWidget.py
@@ -0,0 +1,727 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2004-2017 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.
+#
+# ######################################################################### */
+"""This module provides a widget designed to configure and run a fitting
+process with constraints on parameters.
+
+The main class is :class:`FitWidget`. It relies on
+:mod:`silx.math.fit.fitmanager`, which relies on :func:`silx.math.fit.leastsq`.
+
+The user can choose between functions before running the fit. These function can
+be user defined, or by default are loaded from
+:mod:`silx.math.fit.fittheories`.
+"""
+
+__authors__ = ["V.A. Sole", "P. Knobel"]
+__license__ = "MIT"
+__date__ = "15/02/2017"
+
+import logging
+import sys
+import traceback
+import warnings
+
+from silx.math.fit import fittheories
+from silx.math.fit import fitmanager, functions
+from silx.gui import qt
+from .FitWidgets import (FitActionsButtons, FitStatusLines,
+ FitConfigWidget, ParametersTab)
+from .FitConfig import getFitConfigDialog
+from .BackgroundWidget import getBgDialog, BackgroundDialog
+
+QTVERSION = qt.qVersion()
+DEBUG = 0
+_logger = logging.getLogger(__name__)
+
+
+__authors__ = ["V.A. Sole", "P. Knobel"]
+__license__ = "MIT"
+__date__ = "30/11/2016"
+
+
+class FitWidget(qt.QWidget):
+ """This widget can be used to configure, run and display results of a
+ fitting process.
+
+ The standard steps for using this widget is to initialize it, then load
+ the data to be fitted.
+
+ Optionally, you can also load user defined fit theories. If you skip this
+ step, a series of default fit functions will be presented (gaussian-like
+ functions), and you can later load your custom fit theories from an
+ external file using the GUI.
+
+ A fit theory is a fit function and its associated features:
+
+ - estimation function,
+ - list of parameter names
+ - numerical derivative algorithm
+ - configuration widget
+
+ Once the widget is up and running, the user may select a fit theory and a
+ background theory, change configuration parameters specific to the theory
+ run the estimation, set constraints on parameters and run the actual fit.
+
+ The results are displayed in a table.
+ """
+ sigFitWidgetSignal = qt.Signal(object)
+ """This signal is emitted by the estimation and fit methods.
+ It carries a dictionary with two items:
+
+ - *event*: one of the following strings
+
+ - *EstimateStarted*,
+ - *FitStarted*
+ - *EstimateFinished*,
+ - *FitFinished*
+ - *EstimateFailed*
+ - *FitFailed*
+
+ - *data*: None, or fit/estimate results (see documentation for
+ :attr:`silx.math.fit.fitmanager.FitManager.fit_results`)
+ """
+
+ def __init__(self, parent=None, title=None, fitmngr=None,
+ enableconfig=True, enablestatus=True, enablebuttons=True):
+ """
+
+ :param parent: Parent widget
+ :param title: Window title
+ :param fitmngr: User defined instance of
+ :class:`silx.math.fit.fitmanager.FitManager`, or ``None``
+ :param enableconfig: If ``True``, activate widgets to modify the fit
+ configuration (select between several fit functions or background
+ functions, apply global constraints, peak search parameters…)
+ :param enablestatus: If ``True``, add a fit status widget, to display
+ a message when fit estimation is available and when fit results
+ are available, as well as a measure of the fit error.
+ :param enablebuttons: If ``True``, add buttons to run estimation and
+ fitting.
+ """
+ if title is None:
+ title = "FitWidget"
+ qt.QWidget.__init__(self, parent)
+
+ self.setWindowTitle(title)
+ layout = qt.QVBoxLayout(self)
+
+ self.fitmanager = self._setFitManager(fitmngr)
+ """Instance of :class:`FitManager`.
+ This is the underlying data model of this FitWidget.
+
+ If no custom theories are defined, the default ones from
+ :mod:`silx.math.fit.fittheories` are imported.
+ """
+
+ # reference fitmanager.configure method for direct access
+ self.configure = self.fitmanager.configure
+ self.fitconfig = self.fitmanager.fitconfig
+
+ self.configdialogs = {}
+ """This dictionary defines the fit configuration widgets
+ associated with the fit theories in :attr:`fitmanager.theories`
+
+ Keys must correspond to existing theory names, i.e. existing keys
+ in :attr:`fitmanager.theories`.
+
+ Values must be instances of QDialog widgets with an additional
+ *output* attribute, a dictionary storing configuration parameters
+ interpreted by the corresponding fit theory.
+
+ The dialog can also define a *setDefault* method to initialize the
+ widget values with values in a dictionary passed as a parameter.
+ This will be executed first.
+
+ In case the widget does not actually inherit :class:`QDialog`, it
+ must at least implement the following methods (executed in this
+ particular order):
+
+ - :meth:`show`: should cause the widget to become visible to the
+ user)
+ - :meth:`exec_`: should run while the user is interacting with the
+ widget, interrupting the rest of the program. It should
+ typically end (*return*) when the user clicks an *OK*
+ or a *Cancel* button.
+ - :meth:`result`: must return ``True`` if the new configuration in
+ attribute :attr:`output` is to be accepted (user clicked *OK*),
+ or return ``False`` if :attr:`output` is to be rejected (user
+ clicked *Cancel*)
+
+ To associate a custom configuration widget with a fit theory, use
+ :meth:`associateConfigDialog`. E.g.::
+
+ fw = FitWidget()
+ my_config_widget = MyGaussianConfigWidget(parent=fw)
+ fw.associateConfigDialog(theory_name="Gaussians",
+ config_widget=my_config_widget)
+ """
+
+ self.bgconfigdialogs = {}
+ """Same as :attr:`configdialogs`, except that the widget is associated
+ with a background theory in :attr:`fitmanager.bgtheories`"""
+
+ self._associateDefaultConfigDialogs()
+
+ self.guiConfig = None
+ """Configuration widget at the top of FitWidget, to select
+ fit function, background function, and open an advanced
+ configuration dialog."""
+
+ self.guiParameters = ParametersTab(self)
+ """Table widget for display of fit parameters and constraints"""
+
+ if enableconfig:
+ self.guiConfig = FitConfigWidget(self)
+ """Function selector and configuration widget"""
+
+ self.guiConfig.FunConfigureButton.clicked.connect(
+ self.__funConfigureGuiSlot)
+ self.guiConfig.BgConfigureButton.clicked.connect(
+ self.__bgConfigureGuiSlot)
+
+ self.guiConfig.WeightCheckBox.setChecked(
+ self.fitconfig.get("WeightFlag", False))
+ self.guiConfig.WeightCheckBox.stateChanged[int].connect(self.weightEvent)
+
+ self.guiConfig.BkgComBox.activated[str].connect(self.bkgEvent)
+ self.guiConfig.FunComBox.activated[str].connect(self.funEvent)
+ self._populateFunctions()
+
+ layout.addWidget(self.guiConfig)
+
+ layout.addWidget(self.guiParameters)
+
+ if enablestatus:
+ self.guistatus = FitStatusLines(self)
+ """Status bar"""
+ layout.addWidget(self.guistatus)
+
+ if enablebuttons:
+ self.guibuttons = FitActionsButtons(self)
+ """Widget with estimate, start fit and dismiss buttons"""
+ self.guibuttons.EstimateButton.clicked.connect(self.estimate)
+ self.guibuttons.StartFitButton.clicked.connect(self.startFit)
+ self.guibuttons.DismissButton.clicked.connect(self.dismiss)
+ layout.addWidget(self.guibuttons)
+
+ def _setFitManager(self, fitinstance):
+ """Initialize a :class:`FitManager` instance, to be assigned to
+ :attr:`fitmanager`, or use a custom FitManager instance.
+
+ :param fitinstance: Existing instance of FitManager, possibly
+ customized by the user, or None to load a default instance."""
+ if isinstance(fitinstance, fitmanager.FitManager):
+ # customized
+ fitmngr = fitinstance
+ else:
+ # initialize default instance
+ fitmngr = fitmanager.FitManager()
+
+ # initialize the default fitting functions in case
+ # none is present
+ if not len(fitmngr.theories):
+ fitmngr.loadtheories(fittheories)
+
+ return fitmngr
+
+ def _associateDefaultConfigDialogs(self):
+ """Fill :attr:`bgconfigdialogs` and :attr:`configdialogs` by calling
+ :meth:`associateConfigDialog` with default config dialog widgets.
+ """
+ # associate silx.gui.fit.FitConfig with all theories
+ # Users can later associate their own custom dialogs to
+ # replace the default.
+ configdialog = getFitConfigDialog(parent=self,
+ default=self.fitconfig)
+ for theory in self.fitmanager.theories:
+ self.associateConfigDialog(theory, configdialog)
+ for bgtheory in self.fitmanager.bgtheories:
+ self.associateConfigDialog(bgtheory, configdialog,
+ theory_is_background=True)
+
+ # associate silx.gui.fit.BackgroundWidget with Strip and Snip
+ bgdialog = getBgDialog(parent=self,
+ default=self.fitconfig)
+ for bgtheory in ["Strip", "Snip"]:
+ if bgtheory in self.fitmanager.bgtheories:
+ self.associateConfigDialog(bgtheory, bgdialog,
+ theory_is_background=True)
+
+ def _populateFunctions(self):
+ """Fill combo-boxes with fit theories and background theories
+ loaded by :attr:`fitmanager`.
+ Run :meth:`fitmanager.configure` to ensure the custom configuration
+ of the selected theory has been loaded into :attr:`fitconfig`"""
+ for theory_name in self.fitmanager.bgtheories:
+ self.guiConfig.BkgComBox.addItem(theory_name)
+ self.guiConfig.BkgComBox.setItemData(
+ self.guiConfig.BkgComBox.findText(theory_name),
+ self.fitmanager.bgtheories[theory_name].description,
+ qt.Qt.ToolTipRole)
+
+ for theory_name in self.fitmanager.theories:
+ self.guiConfig.FunComBox.addItem(theory_name)
+ self.guiConfig.FunComBox.setItemData(
+ self.guiConfig.FunComBox.findText(theory_name),
+ self.fitmanager.theories[theory_name].description,
+ qt.Qt.ToolTipRole)
+
+ # - activate selected fit theory (if any)
+ # - activate selected bg theory (if any)
+ configuration = self.fitmanager.configure()
+ if self.fitmanager.selectedtheory is None:
+ # take the first one by default
+ self.guiConfig.FunComBox.setCurrentIndex(1)
+ self.funEvent(list(self.fitmanager.theories.keys())[0])
+ else:
+ idx = list(self.fitmanager.theories).index(self.fitmanager.selectedtheory)
+ self.guiConfig.FunComBox.setCurrentIndex(idx + 1)
+ self.funEvent(self.fitmanager.selectedtheory)
+
+ if self.fitmanager.selectedbg is None:
+ self.guiConfig.BkgComBox.setCurrentIndex(1)
+ self.bkgEvent(list(self.fitmanager.bgtheories.keys())[0])
+ else:
+ idx = list(self.fitmanager.bgtheories).index(self.fitmanager.selectedbg)
+ self.guiConfig.BkgComBox.setCurrentIndex(idx + 1)
+ self.bkgEvent(self.fitmanager.selectedbg)
+
+ configuration.update(self.configure())
+
+ def setdata(self, x, y, sigmay=None, xmin=None, xmax=None):
+ warnings.warn("Method renamed to setData",
+ DeprecationWarning)
+ self.setData(x, y, sigmay, xmin, xmax)
+
+ def setData(self, x, y, sigmay=None, xmin=None, xmax=None):
+ """Set data to be fitted.
+
+ :param x: Abscissa data. If ``None``, :attr:`xdata`` is set to
+ ``numpy.array([0.0, 1.0, 2.0, ..., len(y)-1])``
+ :type x: Sequence or numpy array or None
+ :param y: The dependant data ``y = f(x)``. ``y`` must have the same
+ shape as ``x`` if ``x`` is not ``None``.
+ :type y: Sequence or numpy array or None
+ :param sigmay: The uncertainties in the ``ydata`` array. These are
+ used as weights in the least-squares problem.
+ If ``None``, the uncertainties are assumed to be 1.
+ :type sigmay: Sequence or numpy array or None
+ :param xmin: Lower value of x values to use for fitting
+ :param xmax: Upper value of x values to use for fitting
+ """
+ self.fitmanager.setdata(x=x, y=y, sigmay=sigmay,
+ xmin=xmin, xmax=xmax)
+ for config_dialog in self.bgconfigdialogs.values():
+ if isinstance(config_dialog, BackgroundDialog):
+ config_dialog.setData(x, y, xmin=xmin, xmax=xmax)
+
+ def associateConfigDialog(self, theory_name, config_widget,
+ theory_is_background=False):
+ """Associate an instance of custom configuration dialog widget to
+ a fit theory or to a background theory.
+
+ This adds or modifies an item in the correspondence table
+ :attr:`configdialogs` or :attr:`bgconfigdialogs`.
+
+ :param str theory_name: Name of fit theory. This must be a key of dict
+ :attr:`fitmanager.theories`
+ :param config_widget: Custom configuration widget. See documentation
+ for :attr:`configdialogs`
+ :param bool theory_is_background: If flag is *True*, add dialog to
+ :attr:`bgconfigdialogs` rather than :attr:`configdialogs`
+ (default).
+ :raise: KeyError if parameter ``theory_name`` does not match an
+ existing fit theory or background theory in :attr:`fitmanager`.
+ :raise: AttributeError if the widget does not implement the mandatory
+ methods (*show*, *exec_*, *result*, *setDefault*) or the mandatory
+ attribute (*output*).
+ """
+ theories = self.fitmanager.bgtheories if theory_is_background else\
+ self.fitmanager.theories
+
+ if theory_name not in theories:
+ raise KeyError("%s does not match an existing fitmanager theory")
+
+ if config_widget is not None:
+ for mandatory_attr in ["show", "exec_", "result", "output"]:
+ if not hasattr(config_widget, mandatory_attr):
+ raise AttributeError(
+ "Custom configuration widget must define " +
+ "attribute or method " + mandatory_attr)
+
+ if theory_is_background:
+ self.bgconfigdialogs[theory_name] = config_widget
+ else:
+ self.configdialogs[theory_name] = config_widget
+
+ def _emitSignal(self, ddict):
+ """Emit pyqtSignal after estimation completed
+ (``ddict = {'event': 'EstimateFinished', 'data': fit_results}``)
+ and after fit completed
+ (``ddict = {'event': 'FitFinished', 'data': fit_results}``)"""
+ self.sigFitWidgetSignal.emit(ddict)
+
+ def __funConfigureGuiSlot(self):
+ """Open an advanced configuration dialog widget"""
+ self.__configureGui(dialog_type="function")
+
+ def __bgConfigureGuiSlot(self):
+ """Open an advanced configuration dialog widget"""
+ self.__configureGui(dialog_type="background")
+
+ def __configureGui(self, newconfiguration=None, dialog_type="function"):
+ """Open an advanced configuration dialog widget to get a configuration
+ dictionary, or use a supplied configuration dictionary. Call
+ :meth:`configure` with this dictionary as a parameter. Update the gui
+ accordingly. Reinitialize the fit results in the table and in
+ :attr:`fitmanager`.
+
+ :param newconfiguration: User supplied configuration dictionary. If ``None``,
+ open a dialog widget that returns a dictionary."""
+ configuration = self.configure()
+ # get new dictionary
+ if newconfiguration is None:
+ newconfiguration = self.configureDialog(configuration, dialog_type)
+ # update configuration
+ configuration.update(self.configure(**newconfiguration))
+ # set fit function theory
+ try:
+ i = 1 + \
+ list(self.fitmanager.theories.keys()).index(
+ self.fitmanager.selectedtheory)
+ self.guiConfig.FunComBox.setCurrentIndex(i)
+ self.funEvent(self.fitmanager.selectedtheory)
+ except ValueError:
+ _logger.error("Function not in list %s",
+ self.fitmanager.selectedtheory)
+ self.funEvent(list(self.fitmanager.theories.keys())[0])
+ # current background
+ try:
+ i = 1 + \
+ list(self.fitmanager.bgtheories.keys()).index(
+ self.fitmanager.selectedbg)
+ self.guiConfig.BkgComBox.setCurrentIndex(i)
+ self.bkgEvent(self.fitmanager.selectedbg)
+ except ValueError:
+ _logger.error("Background not in list %s",
+ self.fitmanager.selectedbg)
+ self.bkgEvent(list(self.fitmanager.bgtheories.keys())[0])
+
+ # update the Gui
+ self.__initialParameters()
+
+ def configureDialog(self, oldconfiguration, dialog_type="function"):
+ """Display a dialog, allowing the user to define fit configuration
+ parameters.
+
+ By default, a common dialog is used for all fit theories. But if the
+ defined a custom dialog using :meth:`associateConfigDialog`, it is
+ used instead.
+
+ :param dict oldconfiguration: Dictionary containing previous configuration
+ :param str dialog_type: "function" or "background"
+ :return: User defined parameters in a dictionary
+ """
+ newconfiguration = {}
+ newconfiguration.update(oldconfiguration)
+
+ if dialog_type == "function":
+ theory = self.fitmanager.selectedtheory
+ configdialog = self.configdialogs[theory]
+ elif dialog_type == "background":
+ theory = self.fitmanager.selectedbg
+ configdialog = self.bgconfigdialogs[theory]
+
+ # this should only happen if a user specifically associates None
+ # with a theory, to have no configuration option
+ if configdialog is None:
+ return {}
+
+ # update state of configdialog before showing it
+ if hasattr(configdialog, "setDefault"):
+ configdialog.setDefault(newconfiguration)
+ configdialog.show()
+ configdialog.exec_()
+ if configdialog.result():
+ newconfiguration.update(configdialog.output)
+
+ return newconfiguration
+
+ def estimate(self):
+ """Run parameter estimation function then emit
+ :attr:`sigFitWidgetSignal` with a dictionary containing a status
+ message and a list of fit parameters estimations
+ in the format defined in
+ :attr:`silx.math.fit.fitmanager.FitManager.fit_results`
+
+ The emitted dictionary has an *"event"* key that can have
+ following values:
+
+ - *'EstimateStarted'*
+ - *'EstimateFailed'*
+ - *'EstimateFinished'*
+ """
+ try:
+ theory_name = self.fitmanager.selectedtheory
+ estimation_function = self.fitmanager.theories[theory_name].estimate
+ if estimation_function is not None:
+ ddict = {'event': 'EstimateStarted',
+ 'data': None}
+ self._emitSignal(ddict)
+ self.fitmanager.estimate(callback=self.fitStatus)
+ else:
+ msg = qt.QMessageBox(self)
+ msg.setIcon(qt.QMessageBox.Information)
+ text = "Function does not define a way to estimate\n"
+ text += "the initial parameters. Please, fill them\n"
+ text += "yourself in the table and press Start Fit\n"
+ msg.setText(text)
+ msg.setWindowTitle('FitWidget Message')
+ msg.exec_()
+ return
+ except: # noqa (we want to catch and report all errors)
+ msg = qt.QMessageBox(self)
+ msg.setIcon(qt.QMessageBox.Critical)
+ msg.setText("Error on estimate: %s" % traceback.format_exc())
+ msg.exec_()
+ ddict = {
+ 'event': 'EstimateFailed',
+ 'data': None}
+ self._emitSignal(ddict)
+ return
+
+ self.guiParameters.fillFromFit(
+ self.fitmanager.fit_results, view='Fit')
+ self.guiParameters.removeAllViews(keep='Fit')
+ ddict = {
+ 'event': 'EstimateFinished',
+ 'data': self.fitmanager.fit_results}
+ self._emitSignal(ddict)
+
+ def startfit(self):
+ warnings.warn("Method renamed to startFit",
+ DeprecationWarning)
+ self.startFit()
+
+ def startFit(self):
+ """Run fit, then emit :attr:`sigFitWidgetSignal` with a dictionary
+ containing a status message and a list of fit
+ parameters results in the format defined in
+ :attr:`silx.math.fit.fitmanager.FitManager.fit_results`
+
+ The emitted dictionary has an *"event"* key that can have
+ following values:
+
+ - *'FitStarted'*
+ - *'FitFailed'*
+ - *'FitFinished'*
+ """
+ self.fitmanager.fit_results = self.guiParameters.getFitResults()
+ try:
+ ddict = {'event': 'FitStarted',
+ 'data': None}
+ self._emitSignal(ddict)
+ self.fitmanager.runfit(callback=self.fitStatus)
+ except: # noqa (we want to catch and report all errors)
+ msg = qt.QMessageBox(self)
+ msg.setIcon(qt.QMessageBox.Critical)
+ msg.setText("Error on Fit: %s" % traceback.format_exc())
+ msg.exec_()
+ ddict = {
+ 'event': 'FitFailed',
+ 'data': None
+ }
+ self._emitSignal(ddict)
+ return
+
+ self.guiParameters.fillFromFit(
+ self.fitmanager.fit_results, view='Fit')
+ self.guiParameters.removeAllViews(keep='Fit')
+ ddict = {
+ 'event': 'FitFinished',
+ 'data': self.fitmanager.fit_results
+ }
+ self._emitSignal(ddict)
+ return
+
+ def bkgEvent(self, bgtheory):
+ """Select background theory, then reinitialize parameters"""
+ bgtheory = str(bgtheory)
+ if bgtheory in self.fitmanager.bgtheories:
+ self.fitmanager.setbackground(bgtheory)
+ else:
+ functionsfile = qt.QFileDialog.getOpenFileName(
+ self, "Select python module with your function(s)", "",
+ "Python Files (*.py);;All Files (*)")
+
+ if len(functionsfile):
+ try:
+ self.fitmanager.loadbgtheories(functionsfile)
+ except ImportError:
+ qt.QMessageBox.critical(self, "ERROR",
+ "Function not imported")
+ return
+ else:
+ # empty the ComboBox
+ while self.guiConfig.BkgComBox.count() > 1:
+ self.guiConfig.BkgComBox.removeItem(1)
+ # and fill it again
+ for key in self.fitmanager.bgtheories:
+ self.guiConfig.BkgComBox.addItem(str(key))
+
+ i = 1 + \
+ list(self.fitmanager.bgtheories.keys()).index(
+ self.fitmanager.selectedbg)
+ self.guiConfig.BkgComBox.setCurrentIndex(i)
+ self.__initialParameters()
+
+ def funEvent(self, theoryname):
+ """Select a fit theory to be used for fitting. If this theory exists
+ in :attr:`fitmanager`, use it. Then, reinitialize table.
+
+ :param theoryname: Name of the fit theory to use for fitting. If this theory
+ exists in :attr:`fitmanager`, use it. Else, open a file dialog to open
+ a custom fit function definition file with
+ :meth:`fitmanager.loadtheories`.
+ """
+ theoryname = str(theoryname)
+ if theoryname in self.fitmanager.theories:
+ self.fitmanager.settheory(theoryname)
+ else:
+ # open a load file dialog
+ functionsfile = qt.QFileDialog.getOpenFileName(
+ self, "Select python module with your function(s)", "",
+ "Python Files (*.py);;All Files (*)")
+
+ if len(functionsfile):
+ try:
+ self.fitmanager.loadtheories(functionsfile)
+ except ImportError:
+ qt.QMessageBox.critical(self, "ERROR",
+ "Function not imported")
+ return
+ else:
+ # empty the ComboBox
+ while self.guiConfig.FunComBox.count() > 1:
+ self.guiConfig.FunComBox.removeItem(1)
+ # and fill it again
+ for key in self.fitmanager.theories:
+ self.guiConfig.FunComBox.addItem(str(key))
+
+ i = 1 + \
+ list(self.fitmanager.theories.keys()).index(
+ self.fitmanager.selectedtheory)
+ self.guiConfig.FunComBox.setCurrentIndex(i)
+ self.__initialParameters()
+
+ def weightEvent(self, flag):
+ """This is called when WeightCheckBox is clicked, to configure the
+ *WeightFlag* field in :attr:`fitmanager.fitconfig` and set weights
+ in the least-square problem."""
+ self.configure(WeightFlag=flag)
+ if flag:
+ self.fitmanager.enableweight()
+ else:
+ # set weights back to 1
+ self.fitmanager.disableweight()
+
+ def __initialParameters(self):
+ """Fill the fit parameters names with names of the parameters of
+ the selected background theory and the selected fit theory.
+ Initialize :attr:`fitmanager.fit_results` with these names, and
+ initialize the table with them. This creates a view called "Fit"
+ in :attr:`guiParameters`"""
+ self.fitmanager.parameter_names = []
+ self.fitmanager.fit_results = []
+ for pname in self.fitmanager.bgtheories[self.fitmanager.selectedbg].parameters:
+ self.fitmanager.parameter_names.append(pname)
+ self.fitmanager.fit_results.append({'name': pname,
+ 'estimation': 0,
+ 'group': 0,
+ 'code': 'FREE',
+ 'cons1': 0,
+ 'cons2': 0,
+ 'fitresult': 0.0,
+ 'sigma': 0.0,
+ 'xmin': None,
+ 'xmax': None})
+ if self.fitmanager.selectedtheory is not None:
+ theory = self.fitmanager.selectedtheory
+ for pname in self.fitmanager.theories[theory].parameters:
+ self.fitmanager.parameter_names.append(pname + "1")
+ self.fitmanager.fit_results.append({'name': pname + "1",
+ 'estimation': 0,
+ 'group': 1,
+ 'code': 'FREE',
+ 'cons1': 0,
+ 'cons2': 0,
+ 'fitresult': 0.0,
+ 'sigma': 0.0,
+ 'xmin': None,
+ 'xmax': None})
+
+ self.guiParameters.fillFromFit(
+ self.fitmanager.fit_results, view='Fit')
+
+ def fitStatus(self, data):
+ """Set *status* and *chisq* in status bar"""
+ if 'chisq' in data:
+ if data['chisq'] is None:
+ self.guistatus.ChisqLine.setText(" ")
+ else:
+ chisq = data['chisq']
+ self.guistatus.ChisqLine.setText("%6.2f" % chisq)
+
+ if 'status' in data:
+ status = data['status']
+ self.guistatus.StatusLine.setText(str(status))
+
+ def dismiss(self):
+ """Close FitWidget"""
+ self.close()
+
+
+if __name__ == "__main__":
+ import numpy
+
+ x = numpy.arange(1500).astype(numpy.float)
+ constant_bg = 3.14
+
+ p = [1000, 100., 30.0,
+ 500, 300., 25.,
+ 1700, 500., 35.,
+ 750, 700., 30.0,
+ 1234, 900., 29.5,
+ 302, 1100., 30.5,
+ 75, 1300., 21.]
+ y = functions.sum_gauss(x, *p) + constant_bg
+
+ a = qt.QApplication(sys.argv)
+ w = FitWidget()
+ w.setData(x=x, y=y)
+ w.show()
+ a.exec_()