summaryrefslogtreecommitdiff
path: root/src/silx/gui/fit
diff options
context:
space:
mode:
Diffstat (limited to 'src/silx/gui/fit')
-rw-r--r--src/silx/gui/fit/BackgroundWidget.py150
-rw-r--r--src/silx/gui/fit/FitConfig.py186
-rw-r--r--src/silx/gui/fit/FitWidget.py287
-rw-r--r--src/silx/gui/fit/FitWidgets.py102
-rw-r--r--src/silx/gui/fit/Parameters.py476
-rw-r--r--src/silx/gui/fit/__init__.py1
-rw-r--r--src/silx/gui/fit/setup.py43
-rw-r--r--src/silx/gui/fit/test/__init__.py1
-rw-r--r--src/silx/gui/fit/test/testBackgroundWidget.py20
-rw-r--r--src/silx/gui/fit/test/testFitConfig.py37
-rw-r--r--src/silx/gui/fit/test/testFitWidget.py19
11 files changed, 688 insertions, 634 deletions
diff --git a/src/silx/gui/fit/BackgroundWidget.py b/src/silx/gui/fit/BackgroundWidget.py
index 7703ee1..d9cfcc8 100644
--- a/src/silx/gui/fit/BackgroundWidget.py
+++ b/src/silx/gui/fit/BackgroundWidget.py
@@ -1,5 +1,4 @@
-# coding: utf-8
-#/*##########################################################################
+# /*##########################################################################
# Copyright (C) 2004-2021 V.A. Sole, European Synchrotron Radiation Facility
#
# This file is part of the PyMca X-ray Fluorescence Toolkit developed at
@@ -45,8 +44,9 @@ __date__ = "28/06/2017"
class HorizontalSpacer(qt.QWidget):
def __init__(self, *args):
qt.QWidget.__init__(self, *args)
- self.setSizePolicy(qt.QSizePolicy(qt.QSizePolicy.Expanding,
- qt.QSizePolicy.Fixed))
+ self.setSizePolicy(
+ qt.QSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Fixed)
+ )
class BackgroundParamWidget(qt.QWidget):
@@ -57,6 +57,7 @@ class BackgroundParamWidget(qt.QWidget):
Updating the widgets causes :attr:`sigBackgroundParamWidgetSignal` to
be emitted.
"""
+
sigBackgroundParamWidgetSignal = qt.pyqtSignal(object)
def __init__(self, parent=None):
@@ -71,8 +72,7 @@ class BackgroundParamWidget(qt.QWidget):
self.algorithmCombo = qt.QComboBox(self)
self.algorithmCombo.addItem("Strip")
self.algorithmCombo.addItem("Snip")
- self.algorithmCombo.activated[int].connect(
- self._algorithmComboActivated)
+ self.algorithmCombo.activated[int].connect(self._algorithmComboActivated)
# Strip parameters ---------------------------------------------------
self.stripWidthLabel = qt.QLabel(self)
@@ -91,9 +91,10 @@ class BackgroundParamWidget(qt.QWidget):
self.stripIterValue.setText("0")
self.stripIterValue.editingFinished[()].connect(self._emitSignal)
self.stripIterValue.setToolTip(
- "Number of iterations for strip algorithm.\n" +
- "If greater than 999, an 2nd pass of strip filter is " +
- "applied to remove artifacts created by first pass.")
+ "Number of iterations for strip algorithm.\n"
+ + "If greater than 999, an 2nd pass of strip filter is "
+ + "applied to remove artifacts created by first pass."
+ )
# Snip parameters ----------------------------------------------------
self.snipWidthLabel = qt.QLabel(self)
@@ -104,7 +105,6 @@ class BackgroundParamWidget(qt.QWidget):
self.snipWidthSpin.setMinimum(0)
self.snipWidthSpin.valueChanged[int].connect(self._emitSignal)
-
# Smoothing parameters -----------------------------------------------
self.smoothingFlagCheck = qt.QCheckBox(self)
self.smoothingFlagCheck.setText("Smoothing Width (Savitsky-Golay)")
@@ -112,7 +112,7 @@ class BackgroundParamWidget(qt.QWidget):
self.smoothingSpin = qt.QSpinBox(self)
self.smoothingSpin.setMinimum(3)
- #self.smoothingSpin.setMaximum(40)
+ # self.smoothingSpin.setMaximum(40)
self.smoothingSpin.setSingleStep(2)
self.smoothingSpin.valueChanged[int].connect(self._emitSignal)
@@ -126,12 +126,12 @@ class BackgroundParamWidget(qt.QWidget):
self.anchorsFlagCheck = qt.QCheckBox(self.anchorsGroup)
self.anchorsFlagCheck.setText("Use anchors")
self.anchorsFlagCheck.setToolTip(
- "Define X coordinates of points that must remain fixed")
- self.anchorsFlagCheck.stateChanged[int].connect(
- self._anchorsToggled)
+ "Define X coordinates of points that must remain fixed"
+ )
+ self.anchorsFlagCheck.stateChanged[int].connect(self._anchorsToggled)
anchorsLayout.addWidget(self.anchorsFlagCheck)
- maxnchannel = 16384 * 4 # Fixme ?
+ maxnchannel = 16384 * 4 # Fixme ?
self.anchorsList = []
num_anchors = 4
for i in range(num_anchors):
@@ -171,8 +171,7 @@ class BackgroundParamWidget(qt.QWidget):
:param algorithm: "snip" or "strip"
"""
if algorithm not in ["strip", "snip"]:
- raise ValueError(
- "Unknown background filter algorithm %s" % algorithm)
+ raise ValueError("Unknown background filter algorithm %s" % algorithm)
self.algorithm = algorithm
self.stripWidthSpin.setEnabled(algorithm == "strip")
@@ -221,7 +220,7 @@ class BackgroundParamWidget(qt.QWidget):
if "AnchorsList" in ddict:
anchorslist = ddict["AnchorsList"]
- if anchorslist in [None, 'None']:
+ if anchorslist in [None, "None"]:
anchorslist = []
for spin in self.anchorsList:
spin.setValue(0)
@@ -249,20 +248,22 @@ class BackgroundParamWidget(qt.QWidget):
stripitertext = self.stripIterValue.text()
stripiter = int(stripitertext) if len(stripitertext) else 0
- return {"algorithm": self.algorithm,
- "StripThreshold": 1.0,
- "SnipWidth": self.snipWidthSpin.value(),
- "StripIterations": stripiter,
- "StripWidth": self.stripWidthSpin.value(),
- "SmoothingFlag": self.smoothingFlagCheck.isChecked(),
- "SmoothingWidth": self.smoothingSpin.value(),
- "AnchorsFlag": self.anchorsFlagCheck.isChecked(),
- "AnchorsList": [spin.value() for spin in self.anchorsList]}
+ return {
+ "algorithm": self.algorithm,
+ "StripThreshold": 1.0,
+ "SnipWidth": self.snipWidthSpin.value(),
+ "StripIterations": stripiter,
+ "StripWidth": self.stripWidthSpin.value(),
+ "SmoothingFlag": self.smoothingFlagCheck.isChecked(),
+ "SmoothingWidth": self.smoothingSpin.value(),
+ "AnchorsFlag": self.anchorsFlagCheck.isChecked(),
+ "AnchorsList": [spin.value() for spin in self.anchorsList],
+ }
def _emitSignal(self, dummy=None):
self.sigBackgroundParamWidgetSignal.emit(
- {'event': 'ParametersChanged',
- 'parameters': self.getParameters()})
+ {"event": "ParametersChanged", "parameters": self.getParameters()}
+ )
class BackgroundWidget(qt.QWidget):
@@ -271,6 +272,7 @@ class BackgroundWidget(qt.QWidget):
Strip and snip filters parameters can be adjusted using input widgets,
and the computed backgrounds are plotted next to the original data to
show the result."""
+
def __init__(self, parent=None):
qt.QWidget.__init__(self, parent)
self.setWindowTitle("Strip and SNIP Configuration Window")
@@ -329,8 +331,7 @@ class BackgroundWidget(qt.QWidget):
self._update()
def _update(self, resetzoom=False):
- """Compute strip and snip backgrounds, update the curves
- """
+ """Compute strip and snip backgrounds, update the curves"""
if self._y is None:
return
@@ -339,7 +340,7 @@ class BackgroundWidget(qt.QWidget):
# smoothed data
y = numpy.ravel(numpy.array(self._y)).astype(numpy.float64)
if pars["SmoothingFlag"]:
- ysmooth = filters.savitsky_golay(y, pars['SmoothingWidth'])
+ ysmooth = filters.savitsky_golay(y, pars["SmoothingWidth"])
f = [0.25, 0.5, 0.25]
ysmooth[1:-1] = numpy.convolve(ysmooth, f, mode=0)
ysmooth[0] = 0.5 * (ysmooth[0] + ysmooth[1])
@@ -347,14 +348,13 @@ class BackgroundWidget(qt.QWidget):
else:
ysmooth = y
-
# loop for anchors
x = self._x
- niter = pars['StripIterations']
+ niter = pars["StripIterations"]
anchors_indices = []
- if pars['AnchorsFlag'] and pars['AnchorsList'] is not None:
+ if pars["AnchorsFlag"] and pars["AnchorsList"] is not None:
ravelled = x
- for channel in pars['AnchorsList']:
+ for channel in pars["AnchorsList"]:
if channel <= ravelled[0]:
continue
index = numpy.nonzero(ravelled >= channel)[0]
@@ -363,52 +363,56 @@ class BackgroundWidget(qt.QWidget):
if index > 0:
anchors_indices.append(index)
- stripBackground = filters.strip(ysmooth,
- w=pars['StripWidth'],
- niterations=niter,
- factor=pars['StripThreshold'],
- anchors=anchors_indices)
+ stripBackground = filters.strip(
+ ysmooth,
+ w=pars["StripWidth"],
+ niterations=niter,
+ factor=pars["StripThreshold"],
+ anchors=anchors_indices,
+ )
if niter >= 1000:
# final smoothing
- stripBackground = filters.strip(stripBackground,
- w=1,
- niterations=50*pars['StripWidth'],
- factor=pars['StripThreshold'],
- anchors=anchors_indices)
+ stripBackground = filters.strip(
+ stripBackground,
+ w=1,
+ niterations=50 * pars["StripWidth"],
+ factor=pars["StripThreshold"],
+ anchors=anchors_indices,
+ )
if len(anchors_indices) == 0:
- anchors_indices = [0, len(ysmooth)-1]
+ anchors_indices = [0, len(ysmooth) - 1]
anchors_indices.sort()
snipBackground = 0.0 * ysmooth
lastAnchor = 0
for anchor in anchors_indices:
if (anchor > lastAnchor) and (anchor < len(ysmooth)):
- snipBackground[lastAnchor:anchor] =\
- filters.snip1d(ysmooth[lastAnchor:anchor],
- pars['SnipWidth'])
+ snipBackground[lastAnchor:anchor] = filters.snip1d(
+ ysmooth[lastAnchor:anchor], pars["SnipWidth"]
+ )
lastAnchor = anchor
if lastAnchor < len(ysmooth):
- snipBackground[lastAnchor:] =\
- filters.snip1d(ysmooth[lastAnchor:],
- pars['SnipWidth'])
-
- self.graphWidget.addCurve(x, y,
- legend='Input Data',
- replace=True,
- resetzoom=resetzoom)
- self.graphWidget.addCurve(x, stripBackground,
- legend='Strip Background',
- resetzoom=False)
- self.graphWidget.addCurve(x, snipBackground,
- legend='SNIP Background',
- resetzoom=False)
+ snipBackground[lastAnchor:] = filters.snip1d(
+ ysmooth[lastAnchor:], pars["SnipWidth"]
+ )
+
+ self.graphWidget.addCurve(
+ x, y, legend="Input Data", replace=True, resetzoom=resetzoom
+ )
+ self.graphWidget.addCurve(
+ x, stripBackground, legend="Strip Background", resetzoom=False
+ )
+ self.graphWidget.addCurve(
+ x, snipBackground, legend="SNIP Background", resetzoom=False
+ )
if self._xmin is not None and self._xmax is not None:
self.graphWidget.getXAxis().setLimits(self._xmin, self._xmax)
class BackgroundDialog(qt.QDialog):
"""QDialog window featuring a :class:`BackgroundWidget`"""
+
def __init__(self, parent=None):
qt.QDialog.__init__(self, parent)
self.setWindowTitle("Strip and Snip Configuration Window")
@@ -453,14 +457,15 @@ class BackgroundDialog(qt.QDialog):
# self.output = ddict
def accept(self):
- """Update :attr:`output`, then call :meth:`QDialog.accept`
- """
+ """Update :attr:`output`, then call :meth:`QDialog.accept`"""
self.output = self.getParameters()
super(BackgroundDialog, self).accept()
def sizeHint(self):
- return qt.QSize(int(1.5*qt.QDialog.sizeHint(self).width()),
- qt.QDialog.sizeHint(self).height())
+ return qt.QSize(
+ int(1.5 * qt.QDialog.sizeHint(self).width()),
+ qt.QDialog.sizeHint(self).height(),
+ )
def setData(self, x, y, xmin=None, xmax=None):
"""See :meth:`BackgroundWidget.setData`"""
@@ -499,11 +504,7 @@ def main():
x = numpy.arange(5000)
# (height1, center1, fwhm1, ...) 5 peaks
- params1 = (50, 500, 100,
- 20, 2000, 200,
- 50, 2250, 100,
- 40, 3000, 75,
- 23, 4000, 150)
+ params1 = (50, 500, 100, 20, 2000, 200, 50, 2250, 100, 40, 3000, 75, 23, 4000, 150)
y0 = sum_gauss(x, *params1)
# random values between [-1;1]
@@ -528,7 +529,8 @@ def main():
w.parametersWidget.parametersWidget.sigBackgroundParamWidgetSignal.connect(mySlot)
w.setData(x, y)
w.exec()
- #a.exec()
+ # a.exec()
+
if __name__ == "__main__":
main()
diff --git a/src/silx/gui/fit/FitConfig.py b/src/silx/gui/fit/FitConfig.py
index 48ebca2..5887b4a 100644
--- a/src/silx/gui/fit/FitConfig.py
+++ b/src/silx/gui/fit/FitConfig.py
@@ -1,4 +1,3 @@
-# coding: utf-8
# /*##########################################################################
# Copyright (C) 2004-2021 V.A. Sole, European Synchrotron Radiation Facility
#
@@ -46,6 +45,7 @@ class TabsDialog(qt.QDialog):
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)
@@ -63,9 +63,9 @@ class TabsDialog(qt.QDialog):
self.buttonDefault.setText("Undo changes")
layout2.addWidget(self.buttonDefault)
- spacer = qt.QSpacerItem(20, 20,
- qt.QSizePolicy.Expanding,
- qt.QSizePolicy.Minimum)
+ spacer = qt.QSpacerItem(
+ 20, 20, qt.QSizePolicy.Expanding, qt.QSizePolicy.Minimum
+ )
layout2.addItem(spacer)
self.buttonOk = qt.QPushButton(self)
@@ -120,6 +120,7 @@ class TabsDialogData(TabsDialog):
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):
"""
@@ -198,11 +199,14 @@ 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.setToolTip(
+ "Disable 'Set constraints' to remove all "
+ + "constraints on all fit parameters"
+ )
self.setCheckable(True)
layout = qt.QVBoxLayout(self)
@@ -213,8 +217,7 @@ class ConstraintsPage(qt.QGroupBox):
layout.addWidget(self.positiveHeightCB)
self.positionInIntervalCB = qt.QCheckBox("Force position in interval", self)
- self.positionInIntervalCB.setToolTip(
- "Fit must position peak within X limits")
+ self.positionInIntervalCB.setToolTip("Fit must position peak within X limits")
layout.addWidget(self.positionInIntervalCB)
self.positiveFwhmCB = qt.QCheckBox("Force positive FWHM", self)
@@ -227,7 +230,8 @@ class ConstraintsPage(qt.QGroupBox):
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")
+ "Fit must find Eta between 0 and 1 for pseudo-Voigt function"
+ )
layout.addWidget(self.quotedEtaCB)
layout.addStretch()
@@ -242,29 +246,27 @@ class ConstraintsPage(qt.QGroupBox):
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.setChecked(not default_dict.get("NoConstraintsFlag", False))
self.positiveHeightCB.setChecked(
- default_dict.get('PositiveHeightAreaFlag', True))
+ 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))
+ 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(),
+ "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
@@ -277,8 +279,9 @@ class SearchPage(qt.QWidget):
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")
+ "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)
@@ -296,8 +299,9 @@ class SearchPage(qt.QWidget):
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")
+ "If disabled, the Y scaling used for peak search is "
+ + "estimated automatically"
+ )
layout.addWidget(self.manualScalingGB)
# ------------ GroupBox scaling-----------------------
layout3 = qt.QHBoxLayout(self.manualScalingGB)
@@ -308,8 +312,8 @@ class SearchPage(qt.QWidget):
self.yScalingEntry = qt.QLineEdit(self.manualScalingGB)
self.yScalingEntry.setToolTip(
- "Data values will be multiplied by this value prior to peak" +
- " search")
+ "Data values will be multiplied by this value prior to peak" + " search"
+ )
self.yScalingEntry.setValidator(qt.QDoubleValidator(self))
layout3.addWidget(self.yScalingEntry)
# ----------------------------------------------------
@@ -324,9 +328,10 @@ class SearchPage(qt.QWidget):
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)")
+ "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)
@@ -336,8 +341,9 @@ class SearchPage(qt.QWidget):
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")
+ "If peak search algorithm is unsuccessful, place one peak "
+ + "at the maximum of the curve"
+ )
layout.addWidget(self.forcePeakPresenceCB)
layout.addStretch()
@@ -351,29 +357,25 @@ class SearchPage(qt.QWidget):
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.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))
+ 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()
+ "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
@@ -381,60 +383,69 @@ class SearchPage(qt.QWidget):
class BackgroundPage(qt.QGroupBox):
"""Background subtraction configuration, specific to fittheories
estimation functions."""
- def __init__(self, parent=None,
- title="Subtract strip background prior to estimation"):
+
+ 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.")
+ "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"]):
+ [
+ "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")
+ "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.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")
+ "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")
+ "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)")
+ "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)
@@ -453,31 +464,25 @@ class BackgroundPage(qt.QGroupBox):
if default_dict is None:
default_dict = {}
- self.setChecked(
- default_dict.get('StripBackgroundFlag', True))
+ 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))
+ 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()
+ "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
@@ -539,5 +544,6 @@ def main():
a.exec()
+
if __name__ == "__main__":
main()
diff --git a/src/silx/gui/fit/FitWidget.py b/src/silx/gui/fit/FitWidget.py
index 52ecafe..2487c23 100644
--- a/src/silx/gui/fit/FitWidget.py
+++ b/src/silx/gui/fit/FitWidget.py
@@ -1,7 +1,6 @@
-# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2004-2021 European Synchrotron Radiation Facility
+# Copyright (c) 2004-2023 European Synchrotron Radiation Facility
#
# This file is part of the PyMca X-ray Fluorescence Toolkit developed at
# the ESRF by the Software group.
@@ -47,11 +46,14 @@ import traceback
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 .FitWidgets import (
+ FitActionsButtons,
+ FitStatusLines,
+ FitConfigWidget,
+ ParametersTab,
+)
from .FitConfig import getFitConfigDialog
from .BackgroundWidget import getBgDialog, BackgroundDialog
-from ...utils.deprecation import deprecated
DEBUG = 0
_logger = logging.getLogger(__name__)
@@ -89,6 +91,7 @@ class FitWidget(qt.QWidget):
.. image:: img/FitWidget.png
"""
+
sigFitWidgetSignal = qt.Signal(object)
"""This signal is emitted by the estimation and fit methods.
It carries a dictionary with two items:
@@ -106,8 +109,15 @@ class FitWidget(qt.QWidget):
:attr:`silx.math.fit.fitmanager.FitManager.fit_results`)
"""
- def __init__(self, parent=None, title=None, fitmngr=None,
- enableconfig=True, enablestatus=True, enablebuttons=True):
+ def __init__(
+ self,
+ parent=None,
+ title=None,
+ fitmngr=None,
+ enableconfig=True,
+ enablestatus=True,
+ enablebuttons=True,
+ ):
"""
:param parent: Parent widget
@@ -200,15 +210,16 @@ class FitWidget(qt.QWidget):
"""Function selector and configuration widget"""
self.guiConfig.FunConfigureButton.clicked.connect(
- self.__funConfigureGuiSlot)
- self.guiConfig.BgConfigureButton.clicked.connect(
- self.__bgConfigureGuiSlot)
+ self.__funConfigureGuiSlot
+ )
+ self.guiConfig.BgConfigureButton.clicked.connect(self.__bgConfigureGuiSlot)
self.guiConfig.WeightCheckBox.setChecked(
- self.fitconfig.get("WeightFlag", False))
+ self.fitconfig.get("WeightFlag", False)
+ )
self.guiConfig.WeightCheckBox.stateChanged[int].connect(self.weightEvent)
- if qt.BINDING in ('PySide2', 'PyQt5'):
+ if qt.BINDING == "PyQt5":
self.guiConfig.BkgComBox.activated[str].connect(self.bkgEvent)
self.guiConfig.FunComBox.activated[str].connect(self.funEvent)
else: # Qt6
@@ -263,21 +274,21 @@ class FitWidget(qt.QWidget):
# 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)
+ 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)
+ self.associateConfigDialog(
+ bgtheory, configdialog, theory_is_background=True
+ )
# associate silx.gui.fit.BackgroundWidget with Strip and Snip
- bgdialog = getBgDialog(parent=self,
- default=self.fitconfig)
+ 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)
+ self.associateConfigDialog(
+ bgtheory, bgdialog, theory_is_background=True
+ )
def _populateFunctions(self):
"""Fill combo-boxes with fit theories and background theories
@@ -287,16 +298,18 @@ class FitWidget(qt.QWidget):
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)
+ 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)
+ 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)
@@ -320,10 +333,6 @@ class FitWidget(qt.QWidget):
configuration.update(self.configure())
- @deprecated(replacement='setData', since_version='0.3.0')
- def setdata(self, x, y, sigmay=None, xmin=None, xmax=None):
- self.setData(x, y, sigmay, xmin, xmax)
-
def setData(self, x=None, y=None, sigmay=None, xmin=None, xmax=None):
"""Set data to be fitted.
@@ -346,14 +355,14 @@ class FitWidget(qt.QWidget):
else:
self.guibuttons.EstimateButton.setEnabled(True)
self.guibuttons.StartFitButton.setEnabled(True)
- self.fitmanager.setdata(x=x, y=y, sigmay=sigmay,
- xmin=xmin, xmax=xmax)
+ 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):
+ 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.
@@ -373,23 +382,30 @@ class FitWidget(qt.QWidget):
methods (*show*, *exec*, *result*, *setDefault*) or the mandatory
attribute (*output*).
"""
- theories = self.fitmanager.bgtheories if theory_is_background else\
- self.fitmanager.theories
+ 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:
- if (not hasattr(config_widget, "exec") and
- not hasattr(config_widget, "exec_")):
+ if not hasattr(config_widget, "exec") and not hasattr(
+ config_widget, "exec_"
+ ):
raise AttributeError(
- "Custom configuration widget must define exec or exec_")
+ "Custom configuration widget must define exec or exec_"
+ )
for mandatory_attr in ["show", "result", "output"]:
if not hasattr(config_widget, mandatory_attr):
raise AttributeError(
- "Custom configuration widget must define " +
- "attribute or method " + mandatory_attr)
+ "Custom configuration widget must define "
+ + "attribute or method "
+ + mandatory_attr
+ )
if theory_is_background:
self.bgconfigdialogs[theory_name] = config_widget
@@ -428,25 +444,23 @@ class FitWidget(qt.QWidget):
configuration.update(self.configure(**newconfiguration))
# set fit function theory
try:
- i = 1 + \
- list(self.fitmanager.theories.keys()).index(
- self.fitmanager.selectedtheory)
+ 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)
+ _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)
+ 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)
+ _logger.error("Background not in list %s", self.fitmanager.selectedbg)
self.bkgEvent(list(self.fitmanager.bgtheories.keys())[0])
# update the Gui
@@ -510,8 +524,7 @@ class FitWidget(qt.QWidget):
theory_name = self.fitmanager.selectedtheory
estimation_function = self.fitmanager.theories[theory_name].estimate
if estimation_function is not None:
- ddict = {'event': 'EstimateStarted',
- 'data': None}
+ ddict = {"event": "EstimateStarted", "data": None}
self._emitSignal(ddict)
self.fitmanager.estimate(callback=self.fitStatus)
else:
@@ -521,34 +534,25 @@ class FitWidget(qt.QWidget):
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.setWindowTitle("FitWidget Message")
msg.exec()
return
- except Exception as e: # noqa (we want to catch and report all errors)
- _logger.warning('Estimate error: %s', traceback.format_exc())
+ except Exception as e: # noqa (we want to catch and report all errors)
+ _logger.warning("Estimate error: %s", traceback.format_exc())
msg = qt.QMessageBox(self)
msg.setIcon(qt.QMessageBox.Critical)
msg.setWindowTitle("Estimate Error")
msg.setText("Error on estimate: %s" % e)
msg.exec()
- ddict = {
- 'event': 'EstimateFailed',
- 'data': None}
+ 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.guiParameters.fillFromFit(self.fitmanager.fit_results, view="Fit")
+ self.guiParameters.removeAllViews(keep="Fit")
+ ddict = {"event": "EstimateFinished", "data": self.fitmanager.fit_results}
self._emitSignal(ddict)
- @deprecated(replacement='startFit', since_version='0.3.0')
- def startfit(self):
- self.startFit()
-
def startFit(self):
"""Run fit, then emit :attr:`sigFitWidgetSignal` with a dictionary
containing a status message and a list of fit
@@ -564,31 +568,23 @@ class FitWidget(qt.QWidget):
"""
self.fitmanager.fit_results = self.guiParameters.getFitResults()
try:
- ddict = {'event': 'FitStarted',
- 'data': None}
+ ddict = {"event": "FitStarted", "data": None}
self._emitSignal(ddict)
self.fitmanager.runfit(callback=self.fitStatus)
except Exception as e: # noqa (we want to catch and report all errors)
- _logger.warning('Estimate error: %s', traceback.format_exc())
+ _logger.warning("Estimate error: %s", traceback.format_exc())
msg = qt.QMessageBox(self)
msg.setIcon(qt.QMessageBox.Critical)
msg.setWindowTitle("Fit Error")
msg.setText("Error on Fit: %s" % e)
msg.exec()
- ddict = {
- 'event': 'FitFailed',
- 'data': None
- }
+ 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.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
@@ -599,15 +595,17 @@ class FitWidget(qt.QWidget):
self.fitmanager.setbackground(bgtheory)
else:
functionsfile = qt.QFileDialog.getOpenFileName(
- self, "Select python module with your function(s)", "",
- "Python Files (*.py);;All Files (*)")
+ 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")
+ qt.QMessageBox.critical(self, "ERROR", "Function not imported")
return
else:
# empty the ComboBox
@@ -617,9 +615,9 @@ class FitWidget(qt.QWidget):
for key in self.fitmanager.bgtheories:
self.guiConfig.BkgComBox.addItem(str(key))
- i = 1 + \
- list(self.fitmanager.bgtheories.keys()).index(
- self.fitmanager.selectedbg)
+ i = 1 + list(self.fitmanager.bgtheories.keys()).index(
+ self.fitmanager.selectedbg
+ )
self.guiConfig.BkgComBox.setCurrentIndex(i)
self.__initialParameters()
@@ -638,15 +636,17 @@ class FitWidget(qt.QWidget):
else:
# open a load file dialog
functionsfile = qt.QFileDialog.getOpenFileName(
- self, "Select python module with your function(s)", "",
- "Python Files (*.py);;All Files (*)")
+ 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")
+ qt.QMessageBox.critical(self, "ERROR", "Function not imported")
return
else:
# empty the ComboBox
@@ -656,9 +656,9 @@ class FitWidget(qt.QWidget):
for key in self.fitmanager.theories:
self.guiConfig.FunComBox.addItem(str(key))
- i = 1 + \
- list(self.fitmanager.theories.keys()).index(
- self.fitmanager.selectedtheory)
+ i = 1 + list(self.fitmanager.theories.keys()).index(
+ self.fitmanager.selectedtheory
+ )
self.guiConfig.FunComBox.setCurrentIndex(i)
self.__initialParameters()
@@ -683,45 +683,52 @@ class FitWidget(qt.QWidget):
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})
+ 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')
+ 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:
+ if "chisq" in data:
+ if data["chisq"] is None:
self.guistatus.ChisqLine.setText(" ")
else:
- chisq = data['chisq']
+ chisq = data["chisq"]
self.guistatus.ChisqLine.setText("%6.2f" % chisq)
- if 'status' in data:
- status = data['status']
+ if "status" in data:
+ status = data["status"]
self.guistatus.StatusLine.setText(str(status))
def dismiss(self):
@@ -735,13 +742,29 @@ if __name__ == "__main__":
x = numpy.arange(1500).astype(numpy.float64)
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.]
+ p = [
+ 1000,
+ 100.0,
+ 30.0,
+ 500,
+ 300.0,
+ 25.0,
+ 1700,
+ 500.0,
+ 35.0,
+ 750,
+ 700.0,
+ 30.0,
+ 1234,
+ 900.0,
+ 29.5,
+ 302,
+ 1100.0,
+ 30.5,
+ 75,
+ 1300.0,
+ 21.0,
+ ]
y = functions.sum_gauss(x, *p) + constant_bg
a = qt.QApplication(sys.argv)
diff --git a/src/silx/gui/fit/FitWidgets.py b/src/silx/gui/fit/FitWidgets.py
index 0fcc6b7..b7aef07 100644
--- a/src/silx/gui/fit/FitWidgets.py
+++ b/src/silx/gui/fit/FitWidgets.py
@@ -1,6 +1,5 @@
-# coding: utf-8
# /*##########################################################################
-# Copyright (C) 2004-2021 European Synchrotron Radiation Facility
+# Copyright (C) 2004-2023 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -24,8 +23,6 @@
"""Collection of widgets used to build
:class:`silx.gui.fit.FitWidget.FitWidget`"""
-from collections import OrderedDict
-
from silx.gui import qt
from silx.gui.fit.Parameters import Parameters
@@ -70,17 +67,17 @@ class FitActionsButtons(qt.QWidget):
self.EstimateButton = qt.QPushButton(self)
self.EstimateButton.setText("Estimate")
layout.addWidget(self.EstimateButton)
- spacer = qt.QSpacerItem(20, 20,
- qt.QSizePolicy.Expanding,
- qt.QSizePolicy.Minimum)
+ spacer = qt.QSpacerItem(
+ 20, 20, qt.QSizePolicy.Expanding, qt.QSizePolicy.Minimum
+ )
layout.addItem(spacer)
self.StartFitButton = qt.QPushButton(self)
self.StartFitButton.setText("Start Fit")
layout.addWidget(self.StartFitButton)
- spacer_2 = qt.QSpacerItem(20, 20,
- qt.QSizePolicy.Expanding,
- qt.QSizePolicy.Minimum)
+ spacer_2 = qt.QSpacerItem(
+ 20, 20, qt.QSizePolicy.Expanding, qt.QSizePolicy.Minimum
+ )
layout.addItem(spacer_2)
self.DismissButton = qt.QPushButton(self)
@@ -149,6 +146,7 @@ class FitConfigWidget(qt.QWidget):
- open a dialog for modifying advanced parameters through
:attr:`FunConfigureButton`
"""
+
def __init__(self, parent=None):
qt.QWidget.__init__(self, parent)
@@ -164,9 +162,11 @@ class FitConfigWidget(qt.QWidget):
self.FunComBox = qt.QComboBox(self)
self.FunComBox.addItem("Add Function(s)")
- self.FunComBox.setItemData(self.FunComBox.findText("Add Function(s)"),
- "Load fit theories from a file",
- qt.Qt.ToolTipRole)
+ self.FunComBox.setItemData(
+ self.FunComBox.findText("Add Function(s)"),
+ "Load fit theories from a file",
+ qt.Qt.ToolTipRole,
+ )
layout.addWidget(self.FunComBox, 0, 1)
self.BkgLabel = qt.QLabel(self)
@@ -175,28 +175,33 @@ class FitConfigWidget(qt.QWidget):
self.BkgComBox = qt.QComboBox(self)
self.BkgComBox.addItem("Add Background(s)")
- self.BkgComBox.setItemData(self.BkgComBox.findText("Add Background(s)"),
- "Load background theories from a file",
- qt.Qt.ToolTipRole)
+ self.BkgComBox.setItemData(
+ self.BkgComBox.findText("Add Background(s)"),
+ "Load background theories from a file",
+ qt.Qt.ToolTipRole,
+ )
layout.addWidget(self.BkgComBox, 1, 1)
self.FunConfigureButton = qt.QPushButton(self)
self.FunConfigureButton.setText("Configure")
self.FunConfigureButton.setToolTip(
- "Open a configuration dialog for the selected function")
+ "Open a configuration dialog for the selected function"
+ )
layout.addWidget(self.FunConfigureButton, 0, 2)
self.BgConfigureButton = qt.QPushButton(self)
self.BgConfigureButton.setText("Configure")
self.BgConfigureButton.setToolTip(
- "Open a configuration dialog for the selected background")
+ "Open a configuration dialog for the selected background"
+ )
layout.addWidget(self.BgConfigureButton, 1, 2)
self.WeightCheckBox = qt.QCheckBox(self)
self.WeightCheckBox.setText("Weighted fit")
self.WeightCheckBox.setToolTip(
- "Enable usage of weights in the least-square problem.\n Use" +
- " the uncertainties (sigma) if provided, else use sqrt(y).")
+ "Enable usage of weights in the least-square problem.\n Use"
+ + " the uncertainties (sigma) if provided, else use sqrt(y)."
+ )
layout.addWidget(self.WeightCheckBox, 0, 3, 2, 1)
@@ -282,7 +287,7 @@ class ParametersTab(qt.QTabWidget):
self.setWindowTitle(name)
self.setContentsMargins(0, 0, 0, 0)
- self.views = OrderedDict()
+ self.views = {}
"""Dictionary of views. Keys are view names,
items are :class:`Parameters` widgets"""
@@ -311,8 +316,8 @@ class ParametersTab(qt.QTabWidget):
view = self.latest_view
else:
raise KeyError(
- "No view available. You must specify a view" +
- " name the first time you call this method."
+ "No view available. You must specify a view"
+ + " name the first time you call this method."
)
if view in self.tables.keys():
@@ -404,7 +409,7 @@ class ParametersTab(qt.QTabWidget):
text += "<tr>"
ncols = table.columnCount()
for l in range(ncols):
- text += ('<td align="left" bgcolor="%s"><b>' % hcolor)
+ text += '<td align="left" bgcolor="%s"><b>' % hcolor
text += str(table.horizontalHeaderItem(l).text())
text += "</b></td>"
text += "</tr>"
@@ -438,11 +443,9 @@ class ParametersTab(qt.QTabWidget):
else:
finalcolor = "white"
if c < 2:
- text += ('<td align="left" bgcolor="%s">%s' %
- (finalcolor, b))
+ text += '<td align="left" bgcolor="%s">%s' % (finalcolor, b)
else:
- text += ('<td align="right" bgcolor="%s">%s' %
- (finalcolor, b))
+ text += '<td align="right" bgcolor="%s">%s' % (finalcolor, b)
text += newtext
if len(b):
text += "</td>"
@@ -506,14 +509,18 @@ def test():
fit = fitmanager.FitManager(x=x, y=y1)
fitfuns = fittheories.FitTheories()
- fit.addtheory(name="Gaussian",
- function=functions.sum_gauss,
- parameters=("height", "peak center", "fwhm"),
- estimate=fitfuns.estimate_height_position_fwhm)
- fit.settheory('Gaussian')
- fit.configure(PositiveFwhmFlag=True,
- PositiveHeightAreaFlag=True,
- AutoFwhm=True,)
+ fit.addtheory(
+ name="Gaussian",
+ function=functions.sum_gauss,
+ parameters=("height", "peak center", "fwhm"),
+ estimate=fitfuns.estimate_height_position_fwhm,
+ )
+ fit.settheory("Gaussian")
+ fit.configure(
+ PositiveFwhmFlag=True,
+ PositiveHeightAreaFlag=True,
+ AutoFwhm=True,
+ )
# Fit
fit.estimate()
@@ -521,26 +528,27 @@ def test():
w = ParametersTab()
w.show()
- w.fillFromFit(fit.fit_results, view='Gaussians')
+ w.fillFromFit(fit.fit_results, view="Gaussians")
- y2 = functions.sum_splitgauss(x,
- 100, 400, 100, 40,
- 10, 600, 50, 500,
- 80, 850, 10, 50)
+ y2 = functions.sum_splitgauss(
+ x, 100, 400, 100, 40, 10, 600, 50, 500, 80, 850, 10, 50
+ )
fit.setdata(x=x, y=y2)
# Define new theory
- fit.addtheory(name="Asymetric gaussian",
- function=functions.sum_splitgauss,
- parameters=("height", "peak center", "left fwhm", "right fwhm"),
- estimate=fitfuns.estimate_splitgauss)
- fit.settheory('Asymetric gaussian')
+ fit.addtheory(
+ name="Asymetric gaussian",
+ function=functions.sum_splitgauss,
+ parameters=("height", "peak center", "left fwhm", "right fwhm"),
+ estimate=fitfuns.estimate_splitgauss,
+ )
+ fit.settheory("Asymetric gaussian")
# Fit
fit.estimate()
fit.runfit()
- w.fillFromFit(fit.fit_results, view='Asymetric gaussians')
+ w.fillFromFit(fit.fit_results, view="Asymetric gaussians")
# Plot
pw = PlotWindow(control=True)
diff --git a/src/silx/gui/fit/Parameters.py b/src/silx/gui/fit/Parameters.py
index daa72f3..bd2605e 100644
--- a/src/silx/gui/fit/Parameters.py
+++ b/src/silx/gui/fit/Parameters.py
@@ -1,6 +1,5 @@
-# coding: utf-8
# /*##########################################################################
-# Copyright (C) 2004-2021 European Synchrotron Radiation Facility
+# Copyright (C) 2004-2023 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -28,7 +27,6 @@ __license__ = "MIT"
__date__ = "25/11/2016"
import sys
-from collections import OrderedDict
from silx.gui import qt
from silx.gui.widgets.TableWidget import TableWidget
@@ -56,6 +54,7 @@ class QComboTableItem(qt.QComboBox):
:param row: Row number of the table cell containing this widget
:param col: Column number of the table cell containing this widget"""
+
sigCellChanged = qt.Signal(int, int)
"""Signal emitted when this ``QComboBox`` is activated.
A ``(row, column)`` tuple is passed."""
@@ -79,6 +78,7 @@ class QCheckBoxItem(qt.QCheckBox):
:param row: Row number of the table cell containing this widget
:param col: Column number of the table cell containing this widget"""
+
sigCellChanged = qt.Signal(int, int)
"""Signal emitted when this ``QCheckBox`` is clicked.
A ``(row, column)`` tuple is passed."""
@@ -107,22 +107,39 @@ class Parameters(TableWidget):
peak.
:type paramlist: list[str] or None
"""
+
def __init__(self, parent=None, paramlist=None):
TableWidget.__init__(self, parent)
self.setContentsMargins(0, 0, 0, 0)
- labels = ['Parameter', 'Estimation', 'Fit Value', 'Sigma',
- 'Constraints', 'Min/Parame', 'Max/Factor/Delta']
- tooltips = ["Fit parameter name",
- "Estimated value for fit parameter. You can edit this column.",
- "Actual value for parameter, after fit",
- "Uncertainty (same unit as the parameter)",
- "Constraint to be applied to the parameter for fit",
- "First parameter for constraint (name of another param or min value)",
- "Second parameter for constraint (max value, or factor/delta)"]
-
- self.columnKeys = ['name', 'estimation', 'fitresult',
- 'sigma', 'code', 'val1', 'val2']
+ labels = [
+ "Parameter",
+ "Estimation",
+ "Fit Value",
+ "Sigma",
+ "Constraints",
+ "Min/Parame",
+ "Max/Factor/Delta",
+ ]
+ tooltips = [
+ "Fit parameter name",
+ "Estimated value for fit parameter. You can edit this column.",
+ "Actual value for parameter, after fit",
+ "Uncertainty (same unit as the parameter)",
+ "Constraint to be applied to the parameter for fit",
+ "First parameter for constraint (name of another param or min value)",
+ "Second parameter for constraint (max value, or factor/delta)",
+ ]
+
+ self.columnKeys = [
+ "name",
+ "estimation",
+ "fitresult",
+ "sigma",
+ "code",
+ "val1",
+ "val2",
+ ]
"""This list assigns shorter keys to refer to columns than the
displayed labels."""
@@ -134,8 +151,7 @@ class Parameters(TableWidget):
for i, label in enumerate(labels):
item = self.horizontalHeaderItem(i)
if item is None:
- item = qt.QTableWidgetItem(label,
- qt.QTableWidgetItem.Type)
+ item = qt.QTableWidgetItem(label, qt.QTableWidgetItem.Type)
self.setHorizontalHeaderItem(i, item)
item.setText(label)
@@ -149,7 +165,7 @@ class Parameters(TableWidget):
# Initialize the table with one line per supplied parameter
paramlist = paramlist if paramlist is not None else []
- self.parameters = OrderedDict()
+ self.parameters = {}
"""This attribute stores all the data in an ordered dictionary.
New data can be added using :meth:`newParameterLine`.
Existing data can be modified using :meth:`configureLine`
@@ -185,8 +201,17 @@ class Parameters(TableWidget):
for line, param in enumerate(paramlist):
self.newParameterLine(param, line)
- self.code_options = ["FREE", "POSITIVE", "QUOTED", "FIXED",
- "FACTOR", "DELTA", "SUM", "IGNORE", "ADD"]
+ self.code_options = [
+ "FREE",
+ "POSITIVE",
+ "QUOTED",
+ "FIXED",
+ "FACTOR",
+ "DELTA",
+ "SUM",
+ "IGNORE",
+ "ADD",
+ ]
"""Possible values in the combo boxes in the 'Constraints' column.
"""
@@ -211,43 +236,46 @@ class Parameters(TableWidget):
self.setRowCount(line + 1)
# default configuration for fit parameters
- self.parameters[param] = OrderedDict((('line', line),
- ('estimation', '0'),
- ('fitresult', ''),
- ('sigma', ''),
- ('code', 'FREE'),
- ('val1', ''),
- ('val2', ''),
- ('cons1', 0),
- ('cons2', 0),
- ('vmin', '0'),
- ('vmax', '1'),
- ('relatedto', ''),
- ('factor', '1.0'),
- ('delta', '0.0'),
- ('sum', '0.0'),
- ('group', ''),
- ('name', param),
- ('xmin', None),
- ('xmax', None)))
- self.setReadWrite(param, 'estimation')
- self.setReadOnly(param, ['name', 'fitresult', 'sigma', 'val1', 'val2'])
+ self.parameters[param] = dict(
+ (
+ ("line", line),
+ ("estimation", "0"),
+ ("fitresult", ""),
+ ("sigma", ""),
+ ("code", "FREE"),
+ ("val1", ""),
+ ("val2", ""),
+ ("cons1", 0),
+ ("cons2", 0),
+ ("vmin", "0"),
+ ("vmax", "1"),
+ ("relatedto", ""),
+ ("factor", "1.0"),
+ ("delta", "0.0"),
+ ("sum", "0.0"),
+ ("group", ""),
+ ("name", param),
+ ("xmin", None),
+ ("xmax", None),
+ )
+ )
+ self.setReadWrite(param, "estimation")
+ self.setReadOnly(param, ["name", "fitresult", "sigma", "val1", "val2"])
# Constraint codes
a = []
for option in self.code_options:
a.append(option)
- code_column_index = self.columnIndexByField('code')
+ code_column_index = self.columnIndexByField("code")
cellWidget = self.cellWidget(line, code_column_index)
if cellWidget is None:
- cellWidget = QComboTableItem(self, row=line,
- col=code_column_index)
+ cellWidget = QComboTableItem(self, row=line, col=code_column_index)
cellWidget.addItems(a)
self.setCellWidget(line, code_column_index, cellWidget)
cellWidget.sigCellChanged[int, int].connect(self.onCellChanged)
- self.parameters[param]['code_item'] = cellWidget
- self.parameters[param]['relatedto_item'] = None
+ self.parameters[param]["code_item"] = cellWidget
+ self.parameters[param]["relatedto_item"] = None
self.__configuring = False
def columnIndexByField(self, field):
@@ -269,44 +297,48 @@ class Parameters(TableWidget):
self.setRowCount(len(fitresults))
# Reinitialize and fill self.parameters
- self.parameters = OrderedDict()
- for (line, param) in enumerate(fitresults):
- self.newParameterLine(param['name'], line)
+ self.parameters = {}
+ for line, param in enumerate(fitresults):
+ self.newParameterLine(param["name"], line)
for param in fitresults:
- name = param['name']
- code = str(param['code'])
+ name = param["name"]
+ code = str(param["code"])
if code not in self.code_options:
# convert code from int to descriptive string
code = self.code_options[int(code)]
- val1 = param['cons1']
- val2 = param['cons2']
- estimation = param['estimation']
- group = param['group']
- sigma = param['sigma']
- fitresult = param['fitresult']
-
- xmin = param.get('xmin')
- xmax = param.get('xmax')
-
- self.configureLine(name=name,
- code=code,
- val1=val1, val2=val2,
- estimation=estimation,
- fitresult=fitresult,
- sigma=sigma,
- group=group,
- xmin=xmin, xmax=xmax)
+ val1 = param["cons1"]
+ val2 = param["cons2"]
+ estimation = param["estimation"]
+ group = param["group"]
+ sigma = param["sigma"]
+ fitresult = param["fitresult"]
+
+ xmin = param.get("xmin")
+ xmax = param.get("xmax")
+
+ self.configureLine(
+ name=name,
+ code=code,
+ val1=val1,
+ val2=val2,
+ estimation=estimation,
+ fitresult=fitresult,
+ sigma=sigma,
+ group=group,
+ xmin=xmin,
+ xmax=xmax,
+ )
def getConfiguration(self):
"""Return ``FitManager.paramlist`` dictionary
encapsulated in another dictionary"""
- return {'parameters': self.getFitResults()}
+ return {"parameters": self.getFitResults()}
def setConfiguration(self, ddict):
"""Fill table with values from a ``FitManager.paramlist`` dictionary
encapsulated in another dictionary"""
- self.fillFromFit(ddict['parameters'])
+ self.fillFromFit(ddict["parameters"])
def getFitResults(self):
"""Return fit parameters as a list of dictionaries in the format used
@@ -317,33 +349,33 @@ class Parameters(TableWidget):
fitparam = {}
name = param
estimation, [code, cons1, cons2] = self.getEstimationConstraints(name)
- buf = str(self.parameters[param]['fitresult'])
- xmin = self.parameters[param]['xmin']
- xmax = self.parameters[param]['xmax']
+ buf = str(self.parameters[param]["fitresult"])
+ xmin = self.parameters[param]["xmin"]
+ xmax = self.parameters[param]["xmax"]
if len(buf):
fitresult = float(buf)
else:
fitresult = 0.0
- buf = str(self.parameters[param]['sigma'])
+ buf = str(self.parameters[param]["sigma"])
if len(buf):
sigma = float(buf)
else:
sigma = 0.0
- buf = str(self.parameters[param]['group'])
+ buf = str(self.parameters[param]["group"])
if len(buf):
group = float(buf)
else:
group = 0
- fitparam['name'] = name
- fitparam['estimation'] = estimation
- fitparam['fitresult'] = fitresult
- fitparam['sigma'] = sigma
- fitparam['group'] = group
- fitparam['code'] = code
- fitparam['cons1'] = cons1
- fitparam['cons2'] = cons2
- fitparam['xmin'] = xmin
- fitparam['xmax'] = xmax
+ fitparam["name"] = name
+ fitparam["estimation"] = estimation
+ fitparam["fitresult"] = fitresult
+ fitparam["sigma"] = sigma
+ fitparam["group"] = group
+ fitparam["code"] = code
+ fitparam["cons1"] = cons1
+ fitparam["cons2"] = cons2
+ fitparam["xmin"] = xmin
+ fitparam["xmax"] = xmax
fitparameterslist.append(fitparam)
return fitparameterslist
@@ -371,7 +403,7 @@ class Parameters(TableWidget):
if item is not None:
newvalue = item.text()
else:
- newvalue = ''
+ newvalue = ""
else:
# this is the combobox
widget = self.cellWidget(row, col)
@@ -380,12 +412,12 @@ class Parameters(TableWidget):
paramdict = {"name": param, field: newvalue}
self.configureLine(**paramdict)
else:
- if field == 'code':
+ if field == "code":
# New code not valid, try restoring the old one
index = self.code_options.index(oldvalue)
self.__configuring = True
try:
- self.parameters[param]['code_item'].setCurrentIndex(index)
+ self.parameters[param]["code_item"].setCurrentIndex(index)
finally:
self.__configuring = False
else:
@@ -401,10 +433,14 @@ class Parameters(TableWidget):
:param newvalue: New value to be validated
:return: True if new cell value is valid, else False
"""
- if field == 'code':
+ if field == "code":
return self.setCodeValue(param, oldvalue, newvalue)
# FIXME: validate() shouldn't have side effects. Move this bit to configureLine()?
- if field == 'val1' and str(self.parameters[param]['code']) in ['DELTA', 'FACTOR', 'SUM']:
+ if field == "val1" and str(self.parameters[param]["code"]) in [
+ "DELTA",
+ "FACTOR",
+ "SUM",
+ ]:
_, candidates = self.getRelatedCandidates(param)
# We expect val1 to be a fit parameter name
if str(newvalue) in candidates:
@@ -430,52 +466,48 @@ class Parameters(TableWidget):
:return: ``True`` if code was successfully updated
"""
- if str(newvalue) in ['FREE', 'POSITIVE', 'QUOTED', 'FIXED']:
- self.configureLine(name=param,
- code=newvalue)
- if str(oldvalue) == 'IGNORE':
+ if str(newvalue) in ["FREE", "POSITIVE", "QUOTED", "FIXED"]:
+ self.configureLine(name=param, code=newvalue)
+ if str(oldvalue) == "IGNORE":
self.freeRestOfGroup(param)
return True
- elif str(newvalue) in ['FACTOR', 'DELTA', 'SUM']:
+ elif str(newvalue) in ["FACTOR", "DELTA", "SUM"]:
# I should check here that some parameter is set
best, candidates = self.getRelatedCandidates(param)
if len(candidates) == 0:
return False
- self.configureLine(name=param,
- code=newvalue,
- relatedto=best)
- if str(oldvalue) == 'IGNORE':
+ self.configureLine(name=param, code=newvalue, relatedto=best)
+ if str(oldvalue) == "IGNORE":
self.freeRestOfGroup(param)
return True
- elif str(newvalue) == 'IGNORE':
+ elif str(newvalue) == "IGNORE":
# I should check if the group can be ignored
# for the time being I just fix all of them to ignore
- group = int(float(str(self.parameters[param]['group'])))
+ group = int(float(str(self.parameters[param]["group"])))
candidates = []
for param in self.parameters.keys():
- if group == int(float(str(self.parameters[param]['group']))):
+ if group == int(float(str(self.parameters[param]["group"]))):
candidates.append(param)
# print candidates
# I should check here if there is any relation to them
for param in candidates:
- self.configureLine(name=param,
- code=newvalue)
+ self.configureLine(name=param, code=newvalue)
return True
- elif str(newvalue) == 'ADD':
- group = int(float(str(self.parameters[param]['group'])))
+ elif str(newvalue) == "ADD":
+ group = int(float(str(self.parameters[param]["group"])))
if group == 0:
# One cannot add a background group
return False
i = 0
for param in self.parameters:
- if i <= int(float(str(self.parameters[param]['group']))):
+ if i <= int(float(str(self.parameters[param]["group"]))):
i += 1
- if (group == 0) and (i == 1): # FIXME: why +1?
+ if (group == 0) and (i == 1): # FIXME: why +1?
i += 1
self.addGroup(i, group)
return False
- elif str(newvalue) == 'SHOW':
+ elif str(newvalue) == "SHOW":
print(self.getEstimationConstraints(param))
return False
@@ -493,14 +525,14 @@ class Parameters(TableWidget):
newparam = []
# loop through parameters until we encounter group number `gtype`
for param in list(self.parameters):
- paramgroup = int(float(str(self.parameters[param]['group'])))
+ paramgroup = int(float(str(self.parameters[param]["group"])))
# copy parameter names in group number `gtype`
if paramgroup == gtype:
# but replace `gtype` with `newg`
newparam.append(param.rstrip("0123456789") + "%d" % newg)
- xmin = self.parameters[param]['xmin']
- xmax = self.parameters[param]['xmax']
+ xmin = self.parameters[param]["xmin"]
+ xmax = self.parameters[param]["xmax"]
# Add new parameters (one table line per parameter) and configureLine each
# one by updating xmin and xmax to the same values as group `gtype`
@@ -520,16 +552,14 @@ class Parameters(TableWidget):
:param workparam: Fit parameter name
"""
if workparam in self.parameters.keys():
- group = int(float(str(self.parameters[workparam]['group'])))
+ group = int(float(str(self.parameters[workparam]["group"])))
for param in self.parameters:
- if param != workparam and\
- group == int(float(str(self.parameters[param]['group']))):
- self.configureLine(name=param,
- code='FREE',
- cons1=0,
- cons2=0,
- val1='',
- val2='')
+ if param != workparam and group == int(
+ float(str(self.parameters[param]["group"]))
+ ):
+ self.configureLine(
+ name=param, code="FREE", cons1=0, cons2=0, val1="", val2=""
+ )
def getRelatedCandidates(self, workparam):
"""If fit parameter ``workparam`` has a constraint that involves other
@@ -544,12 +574,16 @@ class Parameters(TableWidget):
for param_name in self.parameters:
if param_name != workparam:
# ignore parameters that are fixed by a constraint
- if str(self.parameters[param_name]['code']) not in\
- ['IGNORE', 'FACTOR', 'DELTA', 'SUM']:
+ if str(self.parameters[param_name]["code"]) not in [
+ "IGNORE",
+ "FACTOR",
+ "DELTA",
+ "SUM",
+ ]:
candidates.append(param_name)
# take the previous one (before code cell changed) if possible
- if str(self.parameters[workparam]['relatedto']) in candidates:
- best = str(self.parameters[workparam]['relatedto'])
+ if str(self.parameters[workparam]["relatedto"]) in candidates:
+ best = str(self.parameters[workparam]["relatedto"])
return best, candidates
# take the first with same base name (after removing numbers)
for param_name in candidates:
@@ -585,9 +619,7 @@ class Parameters(TableWidget):
:param fields: Field names identifying the columns
:type fields: str or list[str]
"""
- editflags = qt.Qt.ItemIsSelectable |\
- qt.Qt.ItemIsEnabled |\
- qt.Qt.ItemIsEditable
+ editflags = qt.Qt.ItemIsSelectable | qt.Qt.ItemIsEnabled | qt.Qt.ItemIsEditable
self.setField(parameter, fields, editflags)
def setField(self, parameter, fields, edit_flags):
@@ -602,13 +634,11 @@ class Parameters(TableWidget):
qt.Qt.ItemIsSelectable | qt.Qt.ItemIsEnabled |
qt.Qt.ItemIsEditable
"""
- if isinstance(parameter, list) or \
- isinstance(parameter, tuple):
+ if isinstance(parameter, list) or isinstance(parameter, tuple):
paramlist = parameter
else:
paramlist = [parameter]
- if isinstance(fields, list) or \
- isinstance(fields, tuple):
+ if isinstance(fields, list) or isinstance(fields, tuple):
fieldlist = fields
else:
fieldlist = [fields]
@@ -624,7 +654,7 @@ class Parameters(TableWidget):
row = list(self.parameters.keys()).index(param)
for field in fieldlist:
col = self.columnIndexByField(field)
- if field != 'code':
+ if field != "code":
key = field + "_item"
item = self.item(row, col)
if item is None:
@@ -639,10 +669,22 @@ class Parameters(TableWidget):
# Restore previous _configuring flag
self.__configuring = _oldvalue
- def configureLine(self, name, code=None, val1=None, val2=None,
- sigma=None, estimation=None, fitresult=None,
- group=None, xmin=None, xmax=None, relatedto=None,
- cons1=None, cons2=None):
+ def configureLine(
+ self,
+ name,
+ code=None,
+ val1=None,
+ val2=None,
+ sigma=None,
+ estimation=None,
+ fitresult=None,
+ group=None,
+ xmin=None,
+ xmax=None,
+ relatedto=None,
+ cons1=None,
+ cons2=None,
+ ):
"""This function updates values in a line of the table
:param name: Name of the parameter (serves as unique identifier for
@@ -676,73 +718,88 @@ class Parameters(TableWidget):
# update code first, if specified
if code is not None:
code = str(code)
- self.parameters[name]['code'] = code
+ self.parameters[name]["code"] = code
# update combobox
- index = self.parameters[name]['code_item'].findText(code)
- self.parameters[name]['code_item'].setCurrentIndex(index)
+ index = self.parameters[name]["code_item"].findText(code)
+ self.parameters[name]["code_item"].setCurrentIndex(index)
else:
# set code to previous value, used later for setting val1 val2
- code = self.parameters[name]['code']
+ code = self.parameters[name]["code"]
# val1 and sigma have special formats
if val1 is not None:
- fmt = None if self.parameters[name]['code'] in\
- ['DELTA', 'FACTOR', 'SUM'] else "%8g"
+ fmt = (
+ None
+ if self.parameters[name]["code"] in ["DELTA", "FACTOR", "SUM"]
+ else "%8g"
+ )
self._updateField(name, "val1", val1, fmat=fmt)
if sigma is not None:
self._updateField(name, "sigma", sigma, fmat="%6.3g")
# other fields are formatted as "%8g"
- keys_params = (("val2", val2), ("estimation", estimation),
- ("fitresult", fitresult))
+ keys_params = (
+ ("val2", val2),
+ ("estimation", estimation),
+ ("fitresult", fitresult),
+ )
for key, value in keys_params:
if value is not None:
self._updateField(name, key, value, fmat="%8g")
# the rest of the parameters are treated as strings and don't need
# validation
- keys_params = (("group", group), ("xmin", xmin),
- ("xmax", xmax), ("relatedto", relatedto),
- ("cons1", cons1), ("cons2", cons2))
+ keys_params = (
+ ("group", group),
+ ("xmin", xmin),
+ ("xmax", xmax),
+ ("relatedto", relatedto),
+ ("cons1", cons1),
+ ("cons2", cons2),
+ )
for key, value in keys_params:
if value is not None:
self.parameters[name][key] = str(value)
# val1 and val2 have different meanings depending on the code
- if code == 'QUOTED':
+ if code == "QUOTED":
if val1 is not None:
- self.parameters[name]['vmin'] = self.parameters[name]['val1']
+ self.parameters[name]["vmin"] = self.parameters[name]["val1"]
else:
- self.parameters[name]['val1'] = self.parameters[name]['vmin']
+ self.parameters[name]["val1"] = self.parameters[name]["vmin"]
if val2 is not None:
- self.parameters[name]['vmax'] = self.parameters[name]['val2']
+ self.parameters[name]["vmax"] = self.parameters[name]["val2"]
else:
- self.parameters[name]['val2'] = self.parameters[name]['vmax']
+ self.parameters[name]["val2"] = self.parameters[name]["vmax"]
# cons1 and cons2 are scalar representations of val1 and val2
- self.parameters[name]['cons1'] =\
- float_else_zero(self.parameters[name]['val1'])
- self.parameters[name]['cons2'] =\
- float_else_zero(self.parameters[name]['val2'])
+ self.parameters[name]["cons1"] = float_else_zero(
+ self.parameters[name]["val1"]
+ )
+ self.parameters[name]["cons2"] = float_else_zero(
+ self.parameters[name]["val2"]
+ )
# cons1, cons2 = min(val1, val2), max(val1, val2)
- if self.parameters[name]['cons1'] > self.parameters[name]['cons2']:
- self.parameters[name]['cons1'], self.parameters[name]['cons2'] =\
- self.parameters[name]['cons2'], self.parameters[name]['cons1']
+ if self.parameters[name]["cons1"] > self.parameters[name]["cons2"]:
+ self.parameters[name]["cons1"], self.parameters[name]["cons2"] = (
+ self.parameters[name]["cons2"],
+ self.parameters[name]["cons1"],
+ )
- elif code in ['DELTA', 'SUM', 'FACTOR']:
+ elif code in ["DELTA", "SUM", "FACTOR"]:
# For these codes, val1 is the fit parameter name on which the
# constraint depends
if val1 is not None and val1 in paramlist:
- self.parameters[name]['relatedto'] = self.parameters[name]["val1"]
+ self.parameters[name]["relatedto"] = self.parameters[name]["val1"]
elif val1 is not None:
# val1 could be the index of the fit parameter
try:
- self.parameters[name]['relatedto'] = paramlist[int(val1)]
+ self.parameters[name]["relatedto"] = paramlist[int(val1)]
except ValueError:
- self.parameters[name]['relatedto'] = self.parameters[name]["val1"]
+ self.parameters[name]["relatedto"] = self.parameters[name]["val1"]
elif relatedto is not None:
# code changed, val1 not specified but relatedto specified:
@@ -754,25 +811,27 @@ class Parameters(TableWidget):
self.parameters[name][key] = self.parameters[name]["val2"]
# FIXME: val1 is sometimes specified as an index rather than a param name
- self.parameters[name]['val1'] = self.parameters[name]['relatedto']
+ self.parameters[name]["val1"] = self.parameters[name]["relatedto"]
# cons1 is the index of the fit parameter in the ordered dictionary
- if self.parameters[name]['val1'] in paramlist:
- self.parameters[name]['cons1'] =\
- paramlist.index(self.parameters[name]['val1'])
+ if self.parameters[name]["val1"] in paramlist:
+ self.parameters[name]["cons1"] = paramlist.index(
+ self.parameters[name]["val1"]
+ )
# cons2 is the constraint value (factor, delta or sum)
try:
- self.parameters[name]['cons2'] =\
- float(str(self.parameters[name]['val2']))
+ self.parameters[name]["cons2"] = float(
+ str(self.parameters[name]["val2"])
+ )
except ValueError:
- self.parameters[name]['cons2'] = 1.0 if code == "FACTOR" else 0.0
+ self.parameters[name]["cons2"] = 1.0 if code == "FACTOR" else 0.0
- elif code in ['FREE', 'POSITIVE', 'IGNORE', 'FIXED']:
- self.parameters[name]['val1'] = ""
- self.parameters[name]['val2'] = ""
- self.parameters[name]['cons1'] = 0
- self.parameters[name]['cons2'] = 0
+ elif code in ["FREE", "POSITIVE", "IGNORE", "FIXED"]:
+ self.parameters[name]["val1"] = ""
+ self.parameters[name]["val2"] = ""
+ self.parameters[name]["cons1"] = 0
+ self.parameters[name]["cons2"] = 0
self._updateCellRWFlags(name, code)
@@ -794,9 +853,9 @@ class Parameters(TableWidget):
newvalue = fmat % float(value) if value != "" else ""
else:
newvalue = value
- self.parameters[name][field] = newvalue if\
- self.validate(name, field, oldvalue, newvalue) else\
- oldvalue
+ self.parameters[name][field] = (
+ newvalue if self.validate(name, field, oldvalue, newvalue) else oldvalue
+ )
def _updateCellRWFlags(self, name, code=None):
"""Set read-only or read-write flags in a row,
@@ -807,12 +866,12 @@ class Parameters(TableWidget):
`'FIXED', 'FACTOR', 'DELTA', 'SUM', 'ADD'`
:return:
"""
- if code in ['FREE', 'POSITIVE', 'IGNORE', 'FIXED']:
- self.setReadWrite(name, 'estimation')
- self.setReadOnly(name, ['fitresult', 'sigma', 'val1', 'val2'])
+ if code in ["FREE", "POSITIVE", "IGNORE", "FIXED"]:
+ self.setReadWrite(name, "estimation")
+ self.setReadOnly(name, ["fitresult", "sigma", "val1", "val2"])
else:
- self.setReadWrite(name, ['estimation', 'val1', 'val2'])
- self.setReadOnly(name, ['fitresult', 'sigma'])
+ self.setReadWrite(name, ["estimation", "val1", "val2"])
+ self.setReadOnly(name, ["fitresult", "sigma"])
def getEstimationConstraints(self, param):
"""
@@ -823,18 +882,17 @@ class Parameters(TableWidget):
estimation = None
constraints = None
if param in self.parameters.keys():
- buf = str(self.parameters[param]['estimation'])
+ buf = str(self.parameters[param]["estimation"])
if len(buf):
estimation = float(buf)
else:
estimation = 0
- if str(self.parameters[param]['code']) in self.code_options:
- code = self.code_options.index(
- str(self.parameters[param]['code']))
+ if str(self.parameters[param]["code"]) in self.code_options:
+ code = self.code_options.index(str(self.parameters[param]["code"]))
else:
- code = str(self.parameters[param]['code'])
- cons1 = self.parameters[param]['cons1']
- cons2 = self.parameters[param]['cons2']
+ code = str(self.parameters[param]["code"])
+ cons1 = self.parameters[param]["cons1"]
+ cons2 = self.parameters[param]["cons2"]
constraints = [code, cons1, cons2]
return estimation, constraints
@@ -842,21 +900,24 @@ class Parameters(TableWidget):
def main(args):
from silx.math.fit import fittheories
from silx.math.fit import fitmanager
+
try:
from PyMca5 import PyMcaDataDir
except ImportError:
raise ImportError("This demo requires PyMca data. Install PyMca5.")
import numpy
import os
+
app = qt.QApplication(args)
- tab = Parameters(paramlist=['Height', 'Position', 'FWHM'])
+ tab = Parameters(paramlist=["Height", "Position", "FWHM"])
tab.showGrid()
- tab.configureLine(name='Height', estimation='1234', group=0)
- tab.configureLine(name='Position', code='FIXED', group=1)
- tab.configureLine(name='FWHM', group=1)
+ tab.configureLine(name="Height", estimation="1234", group=0)
+ tab.configureLine(name="Position", code="FIXED", group=1)
+ tab.configureLine(name="FWHM", group=1)
- y = numpy.loadtxt(os.path.join(PyMcaDataDir.PYMCA_DATA_DIR,
- "XRFSpectrum.mca")) # FIXME
+ y = numpy.loadtxt(
+ os.path.join(PyMcaDataDir.PYMCA_DATA_DIR, "XRFSpectrum.mca")
+ ) # FIXME
x = numpy.arange(len(y)) * 0.0502883 - 0.492773
fit = fitmanager.FitManager()
@@ -864,19 +925,22 @@ def main(args):
fit.loadtheories(fittheories)
- fit.settheory('ahypermet')
- fit.configure(Yscaling=1.,
- PositiveFwhmFlag=True,
- PositiveHeightAreaFlag=True,
- FwhmPoints=16,
- QuotedPositionFlag=1,
- HypermetTails=1)
- fit.setbackground('Linear')
+ fit.settheory("ahypermet")
+ fit.configure(
+ Yscaling=1.0,
+ PositiveFwhmFlag=True,
+ PositiveHeightAreaFlag=True,
+ FwhmPoints=16,
+ QuotedPositionFlag=1,
+ HypermetTails=1,
+ )
+ fit.setbackground("Linear")
fit.estimate()
fit.runfit()
tab.fillFromFit(fit.fit_results)
tab.show()
app.exec()
+
if __name__ == "__main__":
main(sys.argv)
diff --git a/src/silx/gui/fit/__init__.py b/src/silx/gui/fit/__init__.py
index e4fd3ab..478ea22 100644
--- a/src/silx/gui/fit/__init__.py
+++ b/src/silx/gui/fit/__init__.py
@@ -1,4 +1,3 @@
-# coding: utf-8
# /*##########################################################################
# Copyright (C) 2016 European Synchrotron Radiation Facility
#
diff --git a/src/silx/gui/fit/setup.py b/src/silx/gui/fit/setup.py
deleted file mode 100644
index 6672363..0000000
--- a/src/silx/gui/fit/setup.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2016 European Synchrotron Radiation Facility
-#
-# 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.
-#
-# ###########################################################################*/
-__authors__ = ["P. Knobel"]
-__license__ = "MIT"
-__date__ = "21/07/2016"
-
-
-from numpy.distutils.misc_util import Configuration
-
-
-def configuration(parent_package='', top_path=None):
- config = Configuration('fit', parent_package, top_path)
- config.add_subpackage('test')
-
- return config
-
-
-if __name__ == "__main__":
- from numpy.distutils.core import setup
-
- setup(configuration=configuration)
diff --git a/src/silx/gui/fit/test/__init__.py b/src/silx/gui/fit/test/__init__.py
index 71128fb..b03339f 100644
--- a/src/silx/gui/fit/test/__init__.py
+++ b/src/silx/gui/fit/test/__init__.py
@@ -1,4 +1,3 @@
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016 European Synchrotron Radiation Facility
diff --git a/src/silx/gui/fit/test/testBackgroundWidget.py b/src/silx/gui/fit/test/testBackgroundWidget.py
index b8570f7..73e3fba 100644
--- a/src/silx/gui/fit/test/testBackgroundWidget.py
+++ b/src/silx/gui/fit/test/testBackgroundWidget.py
@@ -1,4 +1,3 @@
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016 European Synchrotron Radiation Facility
@@ -22,8 +21,6 @@
# THE SOFTWARE.
#
# ###########################################################################*/
-import unittest
-
from silx.gui.utils.testutils import TestCaseQt
from .. import BackgroundWidget
@@ -37,8 +34,7 @@ class TestBackgroundWidget(TestCaseQt):
def setUp(self):
super(TestBackgroundWidget, self).setUp()
self.bgdialog = BackgroundWidget.BackgroundDialog()
- self.bgdialog.setData(list([0, 1, 2, 3]),
- list([0, 1, 4, 8]))
+ self.bgdialog.setData(list([0, 1, 2, 3]), list([0, 1, 4, 8]))
self.qWaitForWindowExposed(self.bgdialog)
def tearDown(self):
@@ -61,9 +57,17 @@ class TestBackgroundWidget(TestCaseQt):
self.bgdialog.accept()
output = self.bgdialog.output
- for key in ["algorithm", "StripThreshold", "SnipWidth",
- "StripIterations", "StripWidth", "SmoothingFlag",
- "SmoothingWidth", "AnchorsFlag", "AnchorsList"]:
+ for key in [
+ "algorithm",
+ "StripThreshold",
+ "SnipWidth",
+ "StripIterations",
+ "StripWidth",
+ "SmoothingFlag",
+ "SmoothingWidth",
+ "AnchorsFlag",
+ "AnchorsList",
+ ]:
self.assertIn(key, output)
self.assertFalse(output["AnchorsFlag"])
diff --git a/src/silx/gui/fit/test/testFitConfig.py b/src/silx/gui/fit/test/testFitConfig.py
index 53da2dd..d59562c 100644
--- a/src/silx/gui/fit/test/testFitConfig.py
+++ b/src/silx/gui/fit/test/testFitConfig.py
@@ -1,4 +1,3 @@
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016 European Synchrotron Radiation Facility
@@ -28,8 +27,6 @@ __authors__ = ["P. Knobel"]
__license__ = "MIT"
__date__ = "05/12/2016"
-import unittest
-
from silx.gui.utils.testutils import TestCaseQt
from .. import FitConfig
@@ -62,22 +59,24 @@ class TestFitConfig(TestCaseQt):
self.fit_config.accept()
output = self.fit_config.output
- for key in ["AutoFwhm",
- "PositiveHeightAreaFlag",
- "QuotedPositionFlag",
- "PositiveFwhmFlag",
- "SameFwhmFlag",
- "QuotedEtaFlag",
- "NoConstraintsFlag",
- "FwhmPoints",
- "Sensitivity",
- "Yscaling",
- "ForcePeakPresence",
- "StripBackgroundFlag",
- "StripWidth",
- "StripIterations",
- "StripThreshold",
- "SmoothingFlag"]:
+ for key in [
+ "AutoFwhm",
+ "PositiveHeightAreaFlag",
+ "QuotedPositionFlag",
+ "PositiveFwhmFlag",
+ "SameFwhmFlag",
+ "QuotedEtaFlag",
+ "NoConstraintsFlag",
+ "FwhmPoints",
+ "Sensitivity",
+ "Yscaling",
+ "ForcePeakPresence",
+ "StripBackgroundFlag",
+ "StripWidth",
+ "StripIterations",
+ "StripThreshold",
+ "SmoothingFlag",
+ ]:
self.assertIn(key, output)
self.assertTrue(output["AutoFwhm"])
diff --git a/src/silx/gui/fit/test/testFitWidget.py b/src/silx/gui/fit/test/testFitWidget.py
index abe9d89..e59fa92 100644
--- a/src/silx/gui/fit/test/testFitWidget.py
+++ b/src/silx/gui/fit/test/testFitWidget.py
@@ -1,4 +1,3 @@
-# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016 European Synchrotron Radiation Facility
@@ -24,8 +23,6 @@
# ###########################################################################*/
"""Basic tests for :class:`FitWidget`"""
-import unittest
-
from silx.gui.utils.testutils import TestCaseQt
from ... import qt
@@ -83,13 +80,9 @@ class TestFitWidget(TestCaseQt):
y = [fitfun(x_, 2, 3) for x_ in x]
def conf(**kw):
- return {"spam": "eggs",
- "hello": "world!"}
+ return {"spam": "eggs", "hello": "world!"}
- theory = FitTheory(
- function=fitfun,
- parameters=["a", "b"],
- configure=conf)
+ theory = FitTheory(function=fitfun, parameters=["a", "b"], configure=conf)
fitmngr = FitManager()
fitmngr.setdata(x, y)
@@ -98,8 +91,9 @@ class TestFitWidget(TestCaseQt):
fitmngr.addbgtheory("spam", theory)
fw = FitWidget(fitmngr=fitmngr)
- fw.associateConfigDialog("spam", CustomConfigWidget(),
- theory_is_background=True)
+ fw.associateConfigDialog(
+ "spam", CustomConfigWidget(), theory_is_background=True
+ )
fw.associateConfigDialog("foo", CustomConfigWidget())
fw.show()
self.qWaitForWindowExposed(fw)
@@ -107,8 +101,7 @@ class TestFitWidget(TestCaseQt):
fw.bgconfigdialogs["spam"].accept()
self.assertTrue(fw.bgconfigdialogs["spam"].result())
- self.assertEqual(fw.bgconfigdialogs["spam"].output,
- {"hello": "world"})
+ self.assertEqual(fw.bgconfigdialogs["spam"].output, {"hello": "world"})
fw.bgconfigdialogs["spam"].reject()
self.assertFalse(fw.bgconfigdialogs["spam"].result())