From bfa4dba15485b4192f8bbe13345e9658c97ecf76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Picca=20Fr=C3=A9d=C3=A9ric-Emmanuel?= Date: Sat, 7 Oct 2017 07:59:01 +0200 Subject: New upstream version 0.6.0+dfsg --- silx/gui/plot/actions/fit.py | 189 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 silx/gui/plot/actions/fit.py (limited to 'silx/gui/plot/actions/fit.py') diff --git a/silx/gui/plot/actions/fit.py b/silx/gui/plot/actions/fit.py new file mode 100644 index 0000000..d7256ab --- /dev/null +++ b/silx/gui/plot/actions/fit.py @@ -0,0 +1,189 @@ +# 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. +# +# ###########################################################################*/ +""" +: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__ = "28/06/2017" + +from . import PlotAction +import logging +from silx.gui import qt +from silx.gui.plot.ItemsSelectionDialog import ItemsSelectionDialog +from silx.gui.plot.items import Curve, Histogram + +_logger = logging.getLogger(__name__) + + +def _getUniqueCurve(plt): + """Get a single curve from the plot. + Get the active curve if any, else if a single curve is plotted + get it, else return None. + + :param plt: :class:`.PlotWidget` instance on which to operate + + :return: return value of plt.getActiveCurve(), or plt.getAllCurves()[0], + or None + """ + curve = plt.getActiveCurve() + if curve is not None: + return curve + + curves = plt.getAllCurves() + if len(curves) == 0: + return None + + if len(curves) == 1 and len(plt._getItems(kind='histogram')) == 0: + return curves[0] + + return None + + +def _getUniqueHistogram(plt): + """Return the histogram if there is a single histogram and no curve in + the plot. In all other cases, return None. + + :param plt: :class:`.PlotWidget` instance on which to operate + :return: histogram or None + """ + histograms = plt._getItems(kind='histogram') + if len(histograms) != 1: + return None + if plt.getAllCurves(just_legend=True): + return None + return histograms[0] + + +class FitAction(PlotAction): + """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): + super(FitAction, self).__init__( + plot, icon='math-fit', text='Fit curve', + tooltip='Open a fit dialog', + triggered=self._getFitWindow, + checkable=False, parent=parent) + self.fit_window = None + + def _getFitWindow(self): + self.xlabel = self.plot.getXAxis().getLabel() + self.ylabel = self.plot.getYAxis().getLabel() + self.xmin, self.xmax = self.plot.getXAxis().getLimits() + + histo = _getUniqueHistogram(self.plot) + curve = _getUniqueCurve(self.plot) + + if histo is None and curve is None: + # ambiguous case, we need to ask which plot item to fit + isd = ItemsSelectionDialog(plot=self.plot) + isd.setWindowTitle("Select item to be fitted") + isd.setItemsSelectionMode(qt.QTableWidget.SingleSelection) + isd.setAvailableKinds(["curve", "histogram"]) + isd.selectAllKinds() + + result = isd.exec_() + if result and len(isd.getSelectedItems()) == 1: + item = isd.getSelectedItems()[0] + else: + return + elif histo is not None: + # presence of a unique histo and no curve + item = histo + elif curve is not None: + # presence of a unique or active curve + item = curve + + self.legend = item.getLegend() + + if isinstance(item, 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, Curve): + self.x = item.getXData(copy=False) + self.y = item.getYData(copy=False) + + # open a window with a FitWidget + if self.fit_window is None: + self.fit_window = qt.QMainWindow() + # import done here rather than at module level to avoid circular import + # FitWidget -> BackgroundWidget -> PlotWindow -> actions -> fit -> FitWidget + from ...fit.FitWidget import FitWidget + self.fit_widget = FitWidget(parent=self.fit_window) + self.fit_window.setCentralWidget( + self.fit_widget) + self.fit_widget.guibuttons.DismissButton.clicked.connect( + self.fit_window.close) + self.fit_widget.sigFitWidgetSignal.connect( + self.handle_signal) + self.fit_window.show() + else: + if self.fit_window.isHidden(): + self.fit_window.show() + self.fit_widget.show() + self.fit_window.raise_() + + self.fit_widget.setData(self.x, self.y, + xmin=self.xmin, xmax=self.xmax) + self.fit_window.setWindowTitle( + "Fitting " + self.legend + + " on x range %f-%f" % (self.xmin, self.xmax)) + + def handle_signal(self, ddict): + x_fit = self.x[self.xmin <= self.x] + x_fit = x_fit[x_fit <= self.xmax] + fit_legend = "Fit <%s>" % self.legend + fit_curve = self.plot.getCurve(fit_legend) + + if ddict["event"] == "FitFinished": + y_fit = self.fit_widget.fitmanager.gendata() + if fit_curve is None: + self.plot.addCurve(x_fit, y_fit, + fit_legend, + xlabel=self.xlabel, ylabel=self.ylabel, + resetzoom=False) + else: + fit_curve.setData(x_fit, y_fit) + fit_curve.setVisible(True) + + if ddict["event"] in ["FitStarted", "FitFailed"]: + if fit_curve is not None: + fit_curve.setVisible(False) -- cgit v1.2.3