diff options
Diffstat (limited to 'silx/gui/fit/Parameters.py')
-rw-r--r-- | silx/gui/fit/Parameters.py | 882 |
1 files changed, 0 insertions, 882 deletions
diff --git a/silx/gui/fit/Parameters.py b/silx/gui/fit/Parameters.py deleted file mode 100644 index 62e3278..0000000 --- a/silx/gui/fit/Parameters.py +++ /dev/null @@ -1,882 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# Copyright (C) 2004-2017 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. -# -# ######################################################################### */ -"""This module defines a table widget that is specialized in displaying fit -parameter results and associated constraints.""" -__authors__ = ["V.A. Sole", "P. Knobel"] -__license__ = "MIT" -__date__ = "25/11/2016" - -import sys -from collections import OrderedDict - -from silx.gui import qt -from silx.gui.widgets.TableWidget import TableWidget - - -def float_else_zero(sstring): - """Return converted string to float. If conversion fail, return zero. - - :param sstring: String to be converted - :return: ``float(sstrinq)`` if ``sstring`` can be converted to float - (e.g. ``"3.14"``), else ``0`` - """ - try: - return float(sstring) - except ValueError: - return 0 - - -class QComboTableItem(qt.QComboBox): - """:class:`qt.QComboBox` augmented with a ``sigCellChanged`` signal - to emit a tuple of ``(row, column)`` coordinates when the value is - changed. - - This signal can be used to locate the modified combo box in a table. - - :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.""" - - def __init__(self, parent=None, row=None, col=None): - self._row = row - self._col = col - qt.QComboBox.__init__(self, parent) - self.activated[int].connect(self._cellChanged) - - def _cellChanged(self, idx): # noqa - self.sigCellChanged.emit(self._row, self._col) - - -class QCheckBoxItem(qt.QCheckBox): - """:class:`qt.QCheckBox` augmented with a ``sigCellChanged`` signal - to emit a tuple of ``(row, column)`` coordinates when the check box has - been clicked on. - - This signal can be used to locate the modified check box in a table. - - :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.""" - - def __init__(self, parent=None, row=None, col=None): - self._row = row - self._col = col - qt.QCheckBox.__init__(self, parent) - self.clicked.connect(self._cellChanged) - - def _cellChanged(self): - self.sigCellChanged.emit(self._row, self._col) - - -class Parameters(TableWidget): - """:class:`TableWidget` customized to display fit results - and to interact with :class:`FitManager` objects. - - Data and references to cell widgets are kept in a dictionary - attribute :attr:`parameters`. - - :param parent: Parent widget - :param labels: Column headers. If ``None``, default headers will be used. - :type labels: List of strings or None - :param paramlist: List of fit parameters to be displayed for each fitted - 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'] - """This list assigns shorter keys to refer to columns than the - displayed labels.""" - - self.__configuring = False - - # column headers and associated tooltips - self.setColumnCount(len(labels)) - - for i, label in enumerate(labels): - item = self.horizontalHeaderItem(i) - if item is None: - item = qt.QTableWidgetItem(label, - qt.QTableWidgetItem.Type) - self.setHorizontalHeaderItem(i, item) - - item.setText(label) - if tooltips is not None: - item.setToolTip(tooltips[i]) - - # resize columns - for col_key in ["name", "estimation", "sigma", "val1", "val2"]: - col_idx = self.columnIndexByField(col_key) - self.resizeColumnToContents(col_idx) - - # Initialize the table with one line per supplied parameter - paramlist = paramlist if paramlist is not None else [] - self.parameters = OrderedDict() - """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` - - Keys of the dictionary are: - - - 'name': parameter name - - 'line': line index for the parameter in the table - - 'estimation' - - 'fitresult' - - 'sigma' - - 'code': constraint code (one of the elements of - :attr:`code_options`) - - 'val1': first parameter related to constraint, formatted - as a string, as typed in the table - - 'val2': second parameter related to constraint, formatted - as a string, as typed in the table - - 'cons1': scalar representation of 'val1' - (e.g. when val1 is the name of a fit parameter, cons1 - will be the line index of this parameter) - - 'cons2': scalar representation of 'val2' - - 'vmin': equal to 'val1' when 'code' is "QUOTED" - - 'vmax': equal to 'val2' when 'code' is "QUOTED" - - 'relatedto': name of related parameter when this parameter - is constrained to another parameter (same as 'val1') - - 'factor': same as 'val2' when 'code' is 'FACTOR' - - 'delta': same as 'val2' when 'code' is 'DELTA' - - 'sum': same as 'val2' when 'code' is 'SUM' - - 'group': group index for the parameter - - 'xmin': data range minimum - - 'xmax': data range maximum - """ - for line, param in enumerate(paramlist): - self.newParameterLine(param, line) - - self.code_options = ["FREE", "POSITIVE", "QUOTED", "FIXED", - "FACTOR", "DELTA", "SUM", "IGNORE", "ADD"] - """Possible values in the combo boxes in the 'Constraints' column. - """ - - # connect signal - self.cellChanged[int, int].connect(self.onCellChanged) - - def newParameterLine(self, param, line): - """Add a line to the :class:`QTableWidget`. - - Each line represents one of the fit parameters for one of - the fitted peaks. - - :param param: Name of the fit parameter - :type param: str - :param line: 0-based line index - :type line: int - """ - # get current number of lines - nlines = self.rowCount() - self.__configuring = True - if line >= nlines: - 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']) - - # Constraint codes - a = [] - for option in self.code_options: - a.append(option) - - 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.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.__configuring = False - - def columnIndexByField(self, field): - """ - - :param field: Field name (column key) - :return: Index of the column with this field name - """ - return self.columnKeys.index(field) - - def fillFromFit(self, fitresults): - """Fill table with values from a list of dictionaries - (see :attr:`silx.math.fit.fitmanager.FitManager.fit_results`) - - :param fitresults: List of parameters as recorded - in the ``paramlist`` attribute of a :class:`FitManager` object - :type fitresults: list[dict] - """ - self.setRowCount(len(fitresults)) - - # Reinitialize and fill self.parameters - self.parameters = OrderedDict() - for (line, param) in enumerate(fitresults): - self.newParameterLine(param['name'], line) - - for param in fitresults: - 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) - - def getConfiguration(self): - """Return ``FitManager.paramlist`` dictionary - encapsulated in another dictionary""" - 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']) - - def getFitResults(self): - """Return fit parameters as a list of dictionaries in the format used - by :class:`FitManager` (attribute ``paramlist``). - """ - fitparameterslist = [] - for param in self.parameters: - 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'] - if len(buf): - fitresult = float(buf) - else: - fitresult = 0.0 - buf = str(self.parameters[param]['sigma']) - if len(buf): - sigma = float(buf) - else: - sigma = 0.0 - 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 - fitparameterslist.append(fitparam) - return fitparameterslist - - def onCellChanged(self, row, col): - """Slot called when ``cellChanged`` signal is emitted. - Checks the validity of the new text in the cell, then calls - :meth:`configureLine` to update the internal ``self.parameters`` - dictionary. - - :param row: Row number of the changed cell (0-based index) - :param col: Column number of the changed cell (0-based index) - """ - if (col != self.columnIndexByField("code")) and (col != -1): - if row != self.currentRow(): - return - if col != self.currentColumn(): - return - if self.__configuring: - return - param = list(self.parameters)[row] - field = self.columnKeys[col] - oldvalue = self.parameters[param][field] - if col != 4: - item = self.item(row, col) - if item is not None: - newvalue = item.text() - else: - newvalue = '' - else: - # this is the combobox - widget = self.cellWidget(row, col) - newvalue = widget.currentText() - if self.validate(param, field, oldvalue, newvalue): - paramdict = {"name": param, field: newvalue} - self.configureLine(**paramdict) - else: - 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) - finally: - self.__configuring = False - else: - paramdict = {"name": param, field: oldvalue} - self.configureLine(**paramdict) - - def validate(self, param, field, oldvalue, newvalue): - """Check validity of ``newvalue`` when a cell's value is modified. - - :param param: Fit parameter name - :param field: Column name - :param oldvalue: Cell value before change attempt - :param newvalue: New value to be validated - :return: True if new cell value is valid, else False - """ - 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']: - _, candidates = self.getRelatedCandidates(param) - # We expect val1 to be a fit parameter name - if str(newvalue) in candidates: - return True - else: - return False - # except for code, val1 and name (which is read-only and does not need - # validation), all fields must always be convertible to float - else: - try: - float(str(newvalue)) - except ValueError: - return False - return True - - def setCodeValue(self, param, oldvalue, newvalue): - """Update 'code' and 'relatedto' fields when code cell is - changed. - - :param param: Fit parameter name - :param oldvalue: Cell value before change attempt - :param newvalue: New value to be validated - :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': - self.freeRestOfGroup(param) - return True - 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.freeRestOfGroup(param) - return True - - 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']))) - candidates = [] - for param in self.parameters.keys(): - 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) - return True - 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']))): - i += 1 - if (group == 0) and (i == 1): # FIXME: why +1? - i += 1 - self.addGroup(i, group) - return False - elif str(newvalue) == 'SHOW': - print(self.getEstimationConstraints(param)) - return False - - def addGroup(self, newg, gtype): - """Add a fit parameter group with the same fit parameters as an - existing group. - - This function is called when the user selects "ADD" in the - "constraints" combobox. - - :param int newg: New group number - :param int gtype: Group number whose parameters we want to copy - - """ - newparam = [] - # loop through parameters until we encounter group number `gtype` - for param in list(self.parameters): - 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'] - - # Add new parameters (one table line per parameter) and configureLine each - # one by updating xmin and xmax to the same values as group `gtype` - line = len(list(self.parameters)) - for param in newparam: - self.newParameterLine(param, line) - line += 1 - for param in newparam: - self.configureLine(name=param, group=newg, xmin=xmin, xmax=xmax) - - def freeRestOfGroup(self, workparam): - """Set ``code`` to ``"FREE"`` for all fit parameters belonging to - the same group as ``workparam``. This is done when the entire group - of parameters was previously ignored and one of them has his code - set to something different than ``"IGNORE"``. - - :param workparam: Fit parameter name - """ - if workparam in self.parameters.keys(): - 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='') - - def getRelatedCandidates(self, workparam): - """If fit parameter ``workparam`` has a constraint that involves other - fit parameters, find possible candidates and try to guess which one - is the most likely. - - :param workparam: Fit parameter name - :return: (best_candidate, possible_candidates) tuple - :rtype: (str, list[str]) - """ - candidates = [] - 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']: - 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']) - return best, candidates - # take the first with same base name (after removing numbers) - for param_name in candidates: - basename = param_name.rstrip("0123456789") - try: - pos = workparam.index(basename) - if pos == 0: - best = param_name - return best, candidates - except ValueError: - pass - # take the first - return candidates[0], candidates - - def setReadOnly(self, parameter, fields): - """Make table cells read-only by setting it's flags and omitting - flag ``qt.Qt.ItemIsEditable`` - - :param parameter: Fit parameter names identifying the rows - :type parameter: str or list[str] - :param fields: Field names identifying the columns - :type fields: str or list[str] - """ - editflags = qt.Qt.ItemIsSelectable | qt.Qt.ItemIsEnabled - self.setField(parameter, fields, editflags) - - def setReadWrite(self, parameter, fields): - """Make table cells read-write by setting it's flags including - flag ``qt.Qt.ItemIsEditable`` - - :param parameter: Fit parameter names identifying the rows - :type parameter: str or list[str] - :param fields: Field names identifying the columns - :type fields: str or list[str] - """ - editflags = qt.Qt.ItemIsSelectable |\ - qt.Qt.ItemIsEnabled |\ - qt.Qt.ItemIsEditable - self.setField(parameter, fields, editflags) - - def setField(self, parameter, fields, edit_flags): - """Set text and flags in a table cell. - - :param parameter: Fit parameter names identifying the rows - :type parameter: str or list[str] - :param fields: Field names identifying the columns - :type fields: str or list[str] - :param edit_flags: Flag combination, e.g:: - - qt.Qt.ItemIsSelectable | qt.Qt.ItemIsEnabled | - qt.Qt.ItemIsEditable - """ - if isinstance(parameter, list) or \ - isinstance(parameter, tuple): - paramlist = parameter - else: - paramlist = [parameter] - if isinstance(fields, list) or \ - isinstance(fields, tuple): - fieldlist = fields - else: - fieldlist = [fields] - - # Set _configuring flag to ignore cellChanged signals in - # self.onCellChanged - _oldvalue = self.__configuring - self.__configuring = True - - # 2D loop through parameter list and field list - # to update their cells - for param in paramlist: - row = list(self.parameters.keys()).index(param) - for field in fieldlist: - col = self.columnIndexByField(field) - if field != 'code': - key = field + "_item" - item = self.item(row, col) - if item is None: - item = qt.QTableWidgetItem() - item.setText(self.parameters[param][field]) - self.setItem(row, col, item) - else: - item.setText(self.parameters[param][field]) - self.parameters[param][key] = item - item.setFlags(edit_flags) - - # 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): - """This function updates values in a line of the table - - :param name: Name of the parameter (serves as unique identifier for - a line). - :param code: Constraint code *FREE, FIXED, POSITIVE, DELTA, FACTOR, - SUM, QUOTED, IGNORE* - :param val1: Constraint 1 (can be the index or name of another - parameter for code *DELTA, FACTOR, SUM*, or a min value - for code *QUOTED*) - :param val2: Constraint 2 - :param sigma: Standard deviation for a fit parameter - :param estimation: Estimated initial value for a fit parameter (used - as input to iterative fit) - :param fitresult: Final result of fit - :param group: Group number of a fit parameter (peak number when doing - multi-peak fitting, as each peak corresponds to a group - of several consecutive parameters) - :param xmin: - :param xmax: - :param relatedto: Index or name of another fit parameter - to which this parameter is related to (constraints) - :param cons1: similar meaning to ``val1``, but is always a number - :param cons2: similar meaning to ``val2``, but is always a number - :return: - """ - paramlist = list(self.parameters.keys()) - - if name not in self.parameters: - raise KeyError("'%s' is not in the parameter list" % name) - - # update code first, if specified - if code is not None: - code = str(code) - self.parameters[name]['code'] = code - # update combobox - 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'] - - # val1 and sigma have special formats - if val1 is not None: - 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)) - 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)) - 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 val1 is not None: - self.parameters[name]['vmin'] = self.parameters[name]['val1'] - else: - self.parameters[name]['val1'] = self.parameters[name]['vmin'] - if val2 is not None: - self.parameters[name]['vmax'] = self.parameters[name]['val2'] - else: - 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']) - - # 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'] - - 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"] - - elif val1 is not None: - # val1 could be the index of the fit parameter - try: - self.parameters[name]['relatedto'] = paramlist[int(val1)] - except ValueError: - self.parameters[name]['relatedto'] = self.parameters[name]["val1"] - - elif relatedto is not None: - # code changed, val1 not specified but relatedto specified: - # set val1 to relatedto (pre-fill best guess) - self.parameters[name]["val1"] = relatedto - - # update fields "delta", "sum" or "factor" - key = code.lower() - 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'] - - # 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']) - - # cons2 is the constraint value (factor, delta or sum) - try: - self.parameters[name]['cons2'] =\ - float(str(self.parameters[name]['val2'])) - except ValueError: - 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 - - self._updateCellRWFlags(name, code) - - def _updateField(self, name, field, value, fmat=None): - """Update field in ``self.parameters`` dictionary, if the new value - is valid. - - :param name: Fit parameter name - :param field: Field name - :param value: New value to assign - :type value: String - :param fmat: Format string (e.g. "%8g") to be applied if value represents - a scalar. If ``None``, format is not modified. If ``value`` is an - empty string, ``fmat`` is ignored. - """ - if value is not None: - oldvalue = self.parameters[name][field] - if fmat is not None: - newvalue = fmat % float(value) if value != "" else "" - else: - newvalue = value - 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, - depending on the constraint code - - :param name: Fit parameter name identifying the row - :param code: Constraint code, in `'FREE', 'POSITIVE', 'IGNORE',` - `'FIXED', 'FACTOR', 'DELTA', 'SUM', 'ADD'` - :return: - """ - 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']) - - def getEstimationConstraints(self, param): - """ - Return tuple ``(estimation, constraints)`` where ``estimation`` is the - value in the ``estimate`` field and ``constraints`` are the relevant - constraints according to the active code - """ - estimation = None - constraints = None - if param in self.parameters.keys(): - 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'])) - else: - code = str(self.parameters[param]['code']) - cons1 = self.parameters[param]['cons1'] - cons2 = self.parameters[param]['cons2'] - constraints = [code, cons1, cons2] - return estimation, constraints - - -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.showGrid() - 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 - - x = numpy.arange(len(y)) * 0.0502883 - 0.492773 - fit = fitmanager.FitManager() - fit.setdata(x=x, y=y, xmin=20, xmax=150) - - fit.loadtheories(fittheories) - - fit.settheory('ahypermet') - fit.configure(Yscaling=1., - 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) |