diff options
Diffstat (limited to 'silx/gui/fit/FitConfig.py')
-rw-r--r-- | silx/gui/fit/FitConfig.py | 543 |
1 files changed, 0 insertions, 543 deletions
diff --git a/silx/gui/fit/FitConfig.py b/silx/gui/fit/FitConfig.py deleted file mode 100644 index 479e469..0000000 --- a/silx/gui/fit/FitConfig.py +++ /dev/null @@ -1,543 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# Copyright (C) 2004-2018 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. -# -# ######################################################################### */ -"""This module defines widgets used to build a fit configuration dialog. -The resulting dialog widget outputs a dictionary of configuration parameters. -""" -from silx.gui import qt - -__authors__ = ["P. Knobel"] -__license__ = "MIT" -__date__ = "30/11/2016" - - -class TabsDialog(qt.QDialog): - """Dialog widget containing a QTabWidget :attr:`tabWidget` - and a buttons: - - # - buttonHelp - - buttonDefaults - - buttonOk - - buttonCancel - - This dialog defines a __len__ returning the number of tabs, - and an __iter__ method yielding the tab widgets. - """ - def __init__(self, parent=None): - qt.QDialog.__init__(self, parent) - self.tabWidget = qt.QTabWidget(self) - - layout = qt.QVBoxLayout(self) - layout.addWidget(self.tabWidget) - - layout2 = qt.QHBoxLayout(None) - - # self.buttonHelp = qt.QPushButton(self) - # self.buttonHelp.setText("Help") - # layout2.addWidget(self.buttonHelp) - - self.buttonDefault = qt.QPushButton(self) - self.buttonDefault.setText("Undo changes") - layout2.addWidget(self.buttonDefault) - - spacer = qt.QSpacerItem(20, 20, - qt.QSizePolicy.Expanding, - qt.QSizePolicy.Minimum) - layout2.addItem(spacer) - - self.buttonOk = qt.QPushButton(self) - self.buttonOk.setText("OK") - layout2.addWidget(self.buttonOk) - - self.buttonCancel = qt.QPushButton(self) - self.buttonCancel.setText("Cancel") - layout2.addWidget(self.buttonCancel) - - layout.addLayout(layout2) - - self.buttonOk.clicked.connect(self.accept) - self.buttonCancel.clicked.connect(self.reject) - - def __len__(self): - """Return number of tabs""" - return self.tabWidget.count() - - def __iter__(self): - """Return the next tab widget in :attr:`tabWidget` every - time this method is called. - - :return: Tab widget - :rtype: QWidget - """ - for widget_index in range(len(self)): - yield self.tabWidget.widget(widget_index) - - def addTab(self, page, label): - """Add a new tab - - :param page: Content of new page. Must be a widget with - a get() method returning a dictionary. - :param str label: Tab label - """ - self.tabWidget.addTab(page, label) - - def getTabLabels(self): - """ - Return a list of all tab labels in :attr:`tabWidget` - """ - return [self.tabWidget.tabText(i) for i in range(len(self))] - - -class TabsDialogData(TabsDialog): - """This dialog adds a data attribute to :class:`TabsDialog`. - - Data input in widgets, such as text entries or checkboxes, is stored in an - attribute :attr:`output` when the user clicks the OK button. - - A default dictionary can be supplied when this dialog is initialized, to - be used as default data for :attr:`output`. - """ - def __init__(self, parent=None, modal=True, default=None): - """ - - :param parent: Parent :class:`QWidget` - :param modal: If `True`, dialog is modal, meaning this dialog remains - in front of it's parent window and disables it until the user is - done interacting with the dialog - :param default: Default dictionary, used to initialize and reset - :attr:`output`. - """ - TabsDialog.__init__(self, parent) - self.setModal(modal) - self.setWindowTitle("Fit configuration") - - self.output = {} - - self.default = {} if default is None else default - - self.buttonDefault.clicked.connect(self._resetDefault) - # self.keyPressEvent(qt.Qt.Key_Enter). - - def keyPressEvent(self, event): - """Redefining this method to ignore Enter key - (for some reason it activates buttonDefault callback which - resets all widgets) - """ - if event.key() in [qt.Qt.Key_Enter, qt.Qt.Key_Return]: - return - TabsDialog.keyPressEvent(self, event) - - def accept(self): - """When *OK* is clicked, update :attr:`output` with data from - various widgets - """ - self.output.update(self.default) - - # loop over all tab widgets (uses TabsDialog.__iter__) - for tabWidget in self: - self.output.update(tabWidget.get()) - - # avoid pathological None cases - for key in self.output.keys(): - if self.output[key] is None: - if key in self.default: - self.output[key] = self.default[key] - super(TabsDialogData, self).accept() - - def reject(self): - """When the *Cancel* button is clicked, reinitialize :attr:`output` - and quit - """ - self.setDefault() - super(TabsDialogData, self).reject() - - def _resetDefault(self, checked): - self.setDefault() - - def setDefault(self, newdefault=None): - """Reinitialize :attr:`output` with :attr:`default` or with - new dictionary ``newdefault`` if provided. - Call :meth:`setDefault` for each tab widget, if available. - """ - self.output = {} - if newdefault is None: - newdefault = self.default - else: - self.default = newdefault - self.output.update(newdefault) - - for tabWidget in self: - if hasattr(tabWidget, "setDefault"): - tabWidget.setDefault(self.output) - - -class ConstraintsPage(qt.QGroupBox): - """Checkable QGroupBox widget filled with QCheckBox widgets, - to configure the fit estimation for standard fit theories. - """ - def __init__(self, parent=None, title="Set constraints"): - super(ConstraintsPage, self).__init__(parent) - self.setTitle(title) - self.setToolTip("Disable 'Set constraints' to remove all " + - "constraints on all fit parameters") - self.setCheckable(True) - - layout = qt.QVBoxLayout(self) - self.setLayout(layout) - - self.positiveHeightCB = qt.QCheckBox("Force positive height/area", self) - self.positiveHeightCB.setToolTip("Fit must find positive peaks") - layout.addWidget(self.positiveHeightCB) - - self.positionInIntervalCB = qt.QCheckBox("Force position in interval", self) - self.positionInIntervalCB.setToolTip( - "Fit must position peak within X limits") - layout.addWidget(self.positionInIntervalCB) - - self.positiveFwhmCB = qt.QCheckBox("Force positive FWHM", self) - self.positiveFwhmCB.setToolTip("Fit must find a positive FWHM") - layout.addWidget(self.positiveFwhmCB) - - self.sameFwhmCB = qt.QCheckBox("Force same FWHM for all peaks", self) - self.sameFwhmCB.setToolTip("Fit must find same FWHM for all peaks") - layout.addWidget(self.sameFwhmCB) - - self.quotedEtaCB = qt.QCheckBox("Force Eta between 0 and 1", self) - self.quotedEtaCB.setToolTip( - "Fit must find Eta between 0 and 1 for pseudo-Voigt function") - layout.addWidget(self.quotedEtaCB) - - layout.addStretch() - - self.setDefault() - - def setDefault(self, default_dict=None): - """Set default state for all widgets. - - :param default_dict: If a default config dictionary is provided as - a parameter, its values are used as default state.""" - if default_dict is None: - default_dict = {} - # this one uses reverse logic: if checked, NoConstraintsFlag must be False - self.setChecked( - not default_dict.get('NoConstraintsFlag', False)) - self.positiveHeightCB.setChecked( - default_dict.get('PositiveHeightAreaFlag', True)) - self.positionInIntervalCB.setChecked( - default_dict.get('QuotedPositionFlag', False)) - self.positiveFwhmCB.setChecked( - default_dict.get('PositiveFwhmFlag', True)) - self.sameFwhmCB.setChecked( - default_dict.get('SameFwhmFlag', False)) - self.quotedEtaCB.setChecked( - default_dict.get('QuotedEtaFlag', False)) - - def get(self): - """Return a dictionary of constraint flags, to be processed by the - :meth:`configure` method of the selected fit theory.""" - ddict = { - 'NoConstraintsFlag': not self.isChecked(), - 'PositiveHeightAreaFlag': self.positiveHeightCB.isChecked(), - 'QuotedPositionFlag': self.positionInIntervalCB.isChecked(), - 'PositiveFwhmFlag': self.positiveFwhmCB.isChecked(), - 'SameFwhmFlag': self.sameFwhmCB.isChecked(), - 'QuotedEtaFlag': self.quotedEtaCB.isChecked(), - } - return ddict - - -class SearchPage(qt.QWidget): - def __init__(self, parent=None): - super(SearchPage, self).__init__(parent) - layout = qt.QVBoxLayout(self) - - self.manualFwhmGB = qt.QGroupBox("Define FWHM manually", self) - self.manualFwhmGB.setCheckable(True) - self.manualFwhmGB.setToolTip( - "If disabled, the FWHM parameter used for peak search is " + - "estimated based on the highest peak in the data") - layout.addWidget(self.manualFwhmGB) - # ------------ GroupBox fwhm-------------------------- - layout2 = qt.QHBoxLayout(self.manualFwhmGB) - self.manualFwhmGB.setLayout(layout2) - - label = qt.QLabel("Fwhm Points", self.manualFwhmGB) - layout2.addWidget(label) - - self.fwhmPointsSpin = qt.QSpinBox(self.manualFwhmGB) - self.fwhmPointsSpin.setRange(0, 999999) - self.fwhmPointsSpin.setToolTip("Typical peak fwhm (number of data points)") - layout2.addWidget(self.fwhmPointsSpin) - # ---------------------------------------------------- - - self.manualScalingGB = qt.QGroupBox("Define scaling manually", self) - self.manualScalingGB.setCheckable(True) - self.manualScalingGB.setToolTip( - "If disabled, the Y scaling used for peak search is " + - "estimated automatically") - layout.addWidget(self.manualScalingGB) - # ------------ GroupBox scaling----------------------- - layout3 = qt.QHBoxLayout(self.manualScalingGB) - self.manualScalingGB.setLayout(layout3) - - label = qt.QLabel("Y Scaling", self.manualScalingGB) - layout3.addWidget(label) - - self.yScalingEntry = qt.QLineEdit(self.manualScalingGB) - self.yScalingEntry.setToolTip( - "Data values will be multiplied by this value prior to peak" + - " search") - self.yScalingEntry.setValidator(qt.QDoubleValidator(self)) - layout3.addWidget(self.yScalingEntry) - # ---------------------------------------------------- - - # ------------------- grid layout -------------------- - containerWidget = qt.QWidget(self) - layout4 = qt.QHBoxLayout(containerWidget) - containerWidget.setLayout(layout4) - - label = qt.QLabel("Sensitivity", containerWidget) - layout4.addWidget(label) - - self.sensitivityEntry = qt.QLineEdit(containerWidget) - self.sensitivityEntry.setToolTip( - "Peak search sensitivity threshold, expressed as a multiple " + - "of the standard deviation of the noise.\nMinimum value is 1 " + - "(to be detected, peak must be higher than the estimated noise)") - sensivalidator = qt.QDoubleValidator(self) - sensivalidator.setBottom(1.0) - self.sensitivityEntry.setValidator(sensivalidator) - layout4.addWidget(self.sensitivityEntry) - # ---------------------------------------------------- - layout.addWidget(containerWidget) - - self.forcePeakPresenceCB = qt.QCheckBox("Force peak presence", self) - self.forcePeakPresenceCB.setToolTip( - "If peak search algorithm is unsuccessful, place one peak " + - "at the maximum of the curve") - layout.addWidget(self.forcePeakPresenceCB) - - layout.addStretch() - - self.setDefault() - - def setDefault(self, default_dict=None): - """Set default values for all widgets. - - :param default_dict: If a default config dictionary is provided as - a parameter, its values are used as default values.""" - if default_dict is None: - default_dict = {} - self.manualFwhmGB.setChecked( - not default_dict.get('AutoFwhm', True)) - self.fwhmPointsSpin.setValue( - default_dict.get('FwhmPoints', 8)) - self.sensitivityEntry.setText( - str(default_dict.get('Sensitivity', 1.0))) - self.manualScalingGB.setChecked( - not default_dict.get('AutoScaling', False)) - self.yScalingEntry.setText( - str(default_dict.get('Yscaling', 1.0))) - self.forcePeakPresenceCB.setChecked( - default_dict.get('ForcePeakPresence', False)) - - def get(self): - """Return a dictionary of peak search parameters, to be processed by - the :meth:`configure` method of the selected fit theory.""" - ddict = { - 'AutoFwhm': not self.manualFwhmGB.isChecked(), - 'FwhmPoints': self.fwhmPointsSpin.value(), - 'Sensitivity': safe_float(self.sensitivityEntry.text()), - 'AutoScaling': not self.manualScalingGB.isChecked(), - 'Yscaling': safe_float(self.yScalingEntry.text()), - 'ForcePeakPresence': self.forcePeakPresenceCB.isChecked() - } - return ddict - - -class BackgroundPage(qt.QGroupBox): - """Background subtraction configuration, specific to fittheories - estimation functions.""" - def __init__(self, parent=None, - title="Subtract strip background prior to estimation"): - super(BackgroundPage, self).__init__(parent) - self.setTitle(title) - self.setCheckable(True) - self.setToolTip( - "The strip algorithm strips away peaks to compute the " + - "background signal.\nAt each iteration, a sample is compared " + - "to the average of the two samples at a given distance in both" + - " directions,\n and if its value is higher than the average," - "it is replaced by the average.") - - layout = qt.QGridLayout(self) - self.setLayout(layout) - - for i, label_text in enumerate( - ["Strip width (in samples)", - "Number of iterations", - "Strip threshold factor"]): - label = qt.QLabel(label_text) - layout.addWidget(label, i, 0) - - self.stripWidthSpin = qt.QSpinBox(self) - self.stripWidthSpin.setToolTip( - "Width, in number of samples, of the strip operator") - self.stripWidthSpin.setRange(1, 999999) - - layout.addWidget(self.stripWidthSpin, 0, 1) - - self.numIterationsSpin = qt.QSpinBox(self) - self.numIterationsSpin.setToolTip( - "Number of iterations of the strip algorithm") - self.numIterationsSpin.setRange(1, 999999) - layout.addWidget(self.numIterationsSpin, 1, 1) - - self.thresholdFactorEntry = qt.QLineEdit(self) - self.thresholdFactorEntry.setToolTip( - "Factor used by the strip algorithm to decide whether a sample" + - "value should be stripped.\nThe value must be higher than the " + - "average of the 2 samples at +- w times this factor.\n") - self.thresholdFactorEntry.setValidator(qt.QDoubleValidator(self)) - layout.addWidget(self.thresholdFactorEntry, 2, 1) - - self.smoothStripGB = qt.QGroupBox("Apply smoothing prior to strip", self) - self.smoothStripGB.setCheckable(True) - self.smoothStripGB.setToolTip( - "Apply a smoothing before subtracting strip background" + - " in fit and estimate processes") - smoothlayout = qt.QHBoxLayout(self.smoothStripGB) - label = qt.QLabel("Smoothing width (Savitsky-Golay)") - smoothlayout.addWidget(label) - self.smoothingWidthSpin = qt.QSpinBox(self) - self.smoothingWidthSpin.setToolTip( - "Width parameter for Savitsky-Golay smoothing (number of samples, must be odd)") - self.smoothingWidthSpin.setRange(3, 101) - self.smoothingWidthSpin.setSingleStep(2) - smoothlayout.addWidget(self.smoothingWidthSpin) - - layout.addWidget(self.smoothStripGB, 3, 0, 1, 2) - - layout.setRowStretch(4, 1) - - self.setDefault() - - def setDefault(self, default_dict=None): - """Set default values for all widgets. - - :param default_dict: If a default config dictionary is provided as - a parameter, its values are used as default values.""" - if default_dict is None: - default_dict = {} - - self.setChecked( - default_dict.get('StripBackgroundFlag', True)) - - self.stripWidthSpin.setValue( - default_dict.get('StripWidth', 2)) - self.numIterationsSpin.setValue( - default_dict.get('StripIterations', 5000)) - self.thresholdFactorEntry.setText( - str(default_dict.get('StripThreshold', 1.0))) - self.smoothStripGB.setChecked( - default_dict.get('SmoothingFlag', False)) - self.smoothingWidthSpin.setValue( - default_dict.get('SmoothingWidth', 3)) - - def get(self): - """Return a dictionary of background subtraction parameters, to be - processed by the :meth:`configure` method of the selected fit theory. - """ - ddict = { - 'StripBackgroundFlag': self.isChecked(), - 'StripWidth': self.stripWidthSpin.value(), - 'StripIterations': self.numIterationsSpin.value(), - 'StripThreshold': safe_float(self.thresholdFactorEntry.text()), - 'SmoothingFlag': self.smoothStripGB.isChecked(), - 'SmoothingWidth': self.smoothingWidthSpin.value() - } - return ddict - - -def safe_float(string_, default=1.0): - """Convert a string into a float. - If the conversion fails, return the default value. - """ - try: - ret = float(string_) - except ValueError: - return default - else: - return ret - - -def safe_int(string_, default=1): - """Convert a string into a integer. - If the conversion fails, return the default value. - """ - try: - ret = int(float(string_)) - except ValueError: - return default - else: - return ret - - -def getFitConfigDialog(parent=None, default=None, modal=True): - """Instantiate and return a fit configuration dialog, adapted - for configuring standard fit theories from - :mod:`silx.math.fit.fittheories`. - - :return: Instance of :class:`TabsDialogData` with 3 tabs: - :class:`ConstraintsPage`, :class:`SearchPage` and - :class:`BackgroundPage` - """ - tdd = TabsDialogData(parent=parent, default=default) - tdd.addTab(ConstraintsPage(), label="Constraints") - tdd.addTab(SearchPage(), label="Peak search") - tdd.addTab(BackgroundPage(), label="Background") - # apply default to newly added pages - tdd.setDefault() - - return tdd - - -def main(): - a = qt.QApplication([]) - - mw = qt.QMainWindow() - mw.show() - - tdd = getFitConfigDialog(mw, default={"a": 1}) - tdd.show() - tdd.exec_() - print("TabsDialogData result: ", tdd.result()) - print("TabsDialogData output: ", tdd.output) - - a.exec_() - -if __name__ == "__main__": - main() |