diff options
Diffstat (limited to 'silx/gui/plot/actions/fit.py')
-rw-r--r-- | silx/gui/plot/actions/fit.py | 403 |
1 files changed, 0 insertions, 403 deletions
diff --git a/silx/gui/plot/actions/fit.py b/silx/gui/plot/actions/fit.py deleted file mode 100644 index f3c9e1c..0000000 --- a/silx/gui/plot/actions/fit.py +++ /dev/null @@ -1,403 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2004-2020 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. -# -# ###########################################################################*/ -""" -:mod:`silx.gui.plot.actions.fit` module provides actions relative to fit. - -The following QAction are available: - -- :class:`.FitAction` - -.. autoclass:`.FitAction` -""" - -from __future__ import division - -__authors__ = ["V.A. Sole", "T. Vincent", "P. Knobel"] -__license__ = "MIT" -__date__ = "10/10/2018" - -import logging - -import numpy - -from .PlotToolAction import PlotToolAction -from .. import items -from ....utils.deprecation import deprecated -from silx.gui import qt -from silx.gui.plot.ItemsSelectionDialog import ItemsSelectionDialog - -_logger = logging.getLogger(__name__) - - -def _getUniqueCurveOrHistogram(plot): - """Returns unique :class:`Curve` or :class:`Histogram` in a `PlotWidget`. - - If there is an active curve, returns it, else return curve or histogram - only if alone in the plot. - - :param PlotWidget plot: - :rtype: Union[None,~silx.gui.plot.items.Curve,~silx.gui.plot.items.Histogram] - """ - curve = plot.getActiveCurve() - if curve is not None: - return curve - - histograms = [item for item in plot.getItems() - if isinstance(item, items.Histogram) and item.isVisible()] - curves = [item for item in plot.getItems() - if isinstance(item, items.Curve) and item.isVisible()] - - if len(histograms) == 1 and len(curves) == 0: - return histograms[0] - elif len(curves) == 1 and len(histograms) == 0: - return curves[0] - else: - return None - - -class FitAction(PlotToolAction): - """QAction to open a :class:`FitWidget` and set its data to the - active curve if any, or to the first curve. - - :param plot: :class:`.PlotWidget` instance on which to operate - :param parent: See :class:`QAction` - """ - - def __init__(self, plot, parent=None): - self.__item = None - self.__activeCurveSynchroEnabled = False - self.__range = 0, 1 - self.__rangeAutoUpdate = False - self.__x, self.__y = None, None # Data to fit - self.__curveParams = {} # Store curve parameters to use for fit result - self.__legend = None - - super(FitAction, self).__init__( - plot, icon='math-fit', text='Fit curve', - tooltip='Open a fit dialog', - parent=parent) - - @property - @deprecated(replacement='getXRange()[0]', since_version='0.13.0') - def xmin(self): - return self.getXRange()[0] - - @property - @deprecated(replacement='getXRange()[1]', since_version='0.13.0') - def xmax(self): - return self.getXRange()[1] - - @property - @deprecated(replacement='getXData()', since_version='0.13.0') - def x(self): - return self.getXData() - - @property - @deprecated(replacement='getYData()', since_version='0.13.0') - def y(self): - return self.getYData() - - @property - @deprecated(since_version='0.13.0') - def xlabel(self): - return self.__curveParams.get('xlabel', None) - - @property - @deprecated(since_version='0.13.0') - def ylabel(self): - return self.__curveParams.get('ylabel', None) - - @property - @deprecated(since_version='0.13.0') - def legend(self): - return self.__legend - - def _createToolWindow(self): - # import done here rather than at module level to avoid circular import - # FitWidget -> BackgroundWidget -> PlotWindow -> actions -> fit -> FitWidget - from ...fit.FitWidget import FitWidget - - window = FitWidget(parent=self.plot) - window.setWindowFlags(qt.Qt.Dialog) - window.sigFitWidgetSignal.connect(self.handle_signal) - return window - - def _connectPlot(self, window): - if self.isXRangeUpdatedOnZoom(): - self.__setAutoXRangeEnabled(True) - else: - plot = self.plot - if plot is None: - _logger.error("No associated PlotWidget") - return - self._setXRange(*plot.getXAxis().getLimits()) - - if self.isFittedItemUpdatedFromActiveCurve(): - self.__setFittedItemAutoUpdateEnabled(True) - else: - # Wait for the next iteration, else the plot is not yet initialized - # No curve available - qt.QTimer.singleShot(10, self._initFit) - - def _disconnectPlot(self, window): - if self.isXRangeUpdatedOnZoom(): - self.__setAutoXRangeEnabled(False) - - if self.isFittedItemUpdatedFromActiveCurve(): - self.__setFittedItemAutoUpdateEnabled(False) - - def _initFit(self): - plot = self.plot - if plot is None: - _logger.error("No associated PlotWidget") - return - - item = _getUniqueCurveOrHistogram(plot) - if item is None: - # ambiguous case, we need to ask which plot item to fit - isd = ItemsSelectionDialog(parent=plot, plot=plot) - isd.setWindowTitle("Select item to be fitted") - isd.setItemsSelectionMode(qt.QTableWidget.SingleSelection) - isd.setAvailableKinds(["curve", "histogram"]) - isd.selectAllKinds() - - if not isd.exec_(): # Cancel - self._getToolWindow().setVisible(False) - else: - selectedItems = isd.getSelectedItems() - item = selectedItems[0] if len(selectedItems) == 1 else None - - self._setXRange(*plot.getXAxis().getLimits()) - self._setFittedItem(item) - - def __updateFitWidget(self): - """Update the data/range used by the FitWidget""" - fitWidget = self._getToolWindow() - - item = self._getFittedItem() - xdata = self.getXData(copy=False) - ydata = self.getYData(copy=False) - if item is None or xdata is None or ydata is None: - fitWidget.setData(y=None) - fitWidget.setWindowTitle("No curve selected") - - else: - xmin, xmax = self.getXRange() - fitWidget.setData( - xdata, ydata, xmin=xmin, xmax=xmax) - fitWidget.setWindowTitle( - "Fitting " + item.getName() + - " on x range %f-%f" % (xmin, xmax)) - - # X Range management - - def getXRange(self): - """Returns the range on the X axis on which to perform the fit.""" - return self.__range - - def _setXRange(self, xmin, xmax): - """Set the range on which the fit is done. - - :param float xmin: - :param float xmax: - """ - range_ = float(xmin), float(xmax) - if self.__range != range_: - self.__range = range_ - self.__updateFitWidget() - - def __setAutoXRangeEnabled(self, enabled): - """Implement the change of update mode of the X range. - - :param bool enabled: - """ - plot = self.plot - if plot is None: - _logger.error("No associated PlotWidget") - return - - if enabled: - self._setXRange(*plot.getXAxis().getLimits()) - plot.getXAxis().sigLimitsChanged.connect(self._setXRange) - else: - plot.getXAxis().sigLimitsChanged.disconnect(self._setXRange) - - def setXRangeUpdatedOnZoom(self, enabled): - """Set whether or not to update the X range on zoom change. - - :param bool enabled: - """ - if enabled != self.__rangeAutoUpdate: - self.__rangeAutoUpdate = enabled - if self._getToolWindow().isVisible(): - self.__setAutoXRangeEnabled(enabled) - - def isXRangeUpdatedOnZoom(self): - """Returns the current mode of fitted data X range update. - - :rtype: bool - """ - return self.__rangeAutoUpdate - - # Fitted item update - - def getXData(self, copy=True): - """Returns the X data used for the fit or None if undefined. - - :param bool copy: - True to get a copy of the data, False to get the internal data. - :rtype: Union[numpy.ndarray,None] - """ - return None if self.__x is None else numpy.array(self.__x, copy=copy) - - def getYData(self, copy=True): - """Returns the Y data used for the fit or None if undefined. - - :param bool copy: - True to get a copy of the data, False to get the internal data. - :rtype: Union[numpy.ndarray,None] - """ - return None if self.__y is None else numpy.array(self.__y, copy=copy) - - def _getFittedItem(self): - """Returns the current item used for the fit - - :rtype: Union[~silx.gui.plot.items.Curve,~silx.gui.plot.items.Histogram,None] - """ - return self.__item - - def _setFittedItem(self, item): - """Set the curve to use for fitting. - - :param Union[~silx.gui.plot.items.Curve,~silx.gui.plot.items.Histogram,None] item: - """ - plot = self.plot - if plot is None: - _logger.error("No associated PlotWidget") - - if plot is None or item is None: - self.__item = None - self.__curveParams = {} - self.__updateFitWidget() - return - - axis = item.getYAxis() if isinstance(item, items.YAxisMixIn) else 'left' - self.__curveParams = { - 'yaxis': axis, - 'xlabel': plot.getXAxis().getLabel(), - 'ylabel': plot.getYAxis(axis).getLabel(), - } - self.__legend = item.getName() - - if isinstance(item, items.Histogram): - bin_edges = item.getBinEdgesData(copy=False) - # take the middle coordinate between adjacent bin edges - self.__x = (bin_edges[1:] + bin_edges[:-1]) / 2 - self.__y = item.getValueData(copy=False) - # else take the active curve, or else the unique curve - elif isinstance(item, items.Curve): - self.__x = item.getXData(copy=False) - self.__y = item.getYData(copy=False) - - self.__item = item - self.__updateFitWidget() - - def __activeCurveChanged(self, previous, current): - """Handle change of active curve in the PlotWidget - """ - if current is None: - self._setFittedItem(None) - else: - item = self.plot.getCurve(current) - self._setFittedItem(item) - - def __setFittedItemAutoUpdateEnabled(self, enabled): - """Implement the change of fitted item update mode - - :param bool enabled: - """ - plot = self.plot - if plot is None: - _logger.error("No associated PlotWidget") - return - - if enabled: - self._setFittedItem(plot.getActiveCurve()) - plot.sigActiveCurveChanged.connect(self.__activeCurveChanged) - - else: - plot.sigActiveCurveChanged.disconnect( - self.__activeCurveChanged) - - def setFittedItemUpdatedFromActiveCurve(self, enabled): - """Toggle fitted data synchronization with plot active curve. - - :param bool enabled: - """ - enabled = bool(enabled) - if enabled != self.__activeCurveSynchroEnabled: - self.__activeCurveSynchroEnabled = enabled - if self._getToolWindow().isVisible(): - self.__setFittedItemAutoUpdateEnabled(enabled) - - def isFittedItemUpdatedFromActiveCurve(self): - """Returns True if fitted data is synchronized with plot. - - :rtype: bool - """ - return self.__activeCurveSynchroEnabled - - # Handle fit completed - - def handle_signal(self, ddict): - xdata = self.getXData(copy=False) - if xdata is None: - _logger.error("No reference data to display fit result for") - return - - xmin, xmax = self.getXRange() - x_fit = xdata[xmin <= xdata] - x_fit = x_fit[x_fit <= xmax] - fit_legend = "Fit <%s>" % self.__legend - fit_curve = self.plot.getCurve(fit_legend) - - if ddict["event"] == "FitFinished": - fit_widget = self._getToolWindow() - if fit_widget is None: - return - y_fit = fit_widget.fitmanager.gendata() - if fit_curve is None: - self.plot.addCurve(x_fit, y_fit, - fit_legend, - resetzoom=False, - **self.__curveParams) - else: - fit_curve.setData(x_fit, y_fit) - fit_curve.setVisible(True) - fit_curve.setYAxis(self.__curveParams.get('yaxis', 'left')) - - if ddict["event"] in ["FitStarted", "FitFailed"]: - if fit_curve is not None: - fit_curve.setVisible(False) |