File indexing completed on 2025-11-02 03:43:45

0001 /*
0002     File                 : FitOptionsWidget.cpp
0003     Project              : LabPlot
0004     Description          : widget for editing advanced fit options
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2014-2020 Alexander Semke <alexander.semke@web.de>
0007     SPDX-FileCopyrightText: 2017-2022 Stefan Gerlach <stefan.gerlach@uni.kn>
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "FitOptionsWidget.h"
0012 #include "backend/core/AbstractColumn.h"
0013 #include "backend/worksheet/plots/cartesian/CartesianCoordinateSystem.h"
0014 #include "backend/worksheet/plots/cartesian/CartesianPlot.h"
0015 #include "backend/worksheet/plots/cartesian/Histogram.h"
0016 
0017 /*!
0018     \class FitOptionsWidget
0019     \brief Widget for editing advanced fit options.
0020 
0021     \ingroup kdefrontend
0022  */
0023 FitOptionsWidget::FitOptionsWidget(QWidget* parent, XYFitCurve::FitData* fitData, XYFitCurve* fitCurve)
0024     : QWidget(parent)
0025     , m_fitData(fitData)
0026     , m_fitCurve(fitCurve) {
0027     ui.setupUi(this);
0028     ui.pbApply->setIcon(QIcon::fromTheme(QStringLiteral("dialog-ok-apply")));
0029     ui.pbCancel->setIcon(QIcon::fromTheme(QStringLiteral("dialog-cancel")));
0030 
0031     // TODO: show "robust" option when robust fitting is possible
0032     //  ui.cbRobust->addItem(i18n("on"));
0033     //  ui.cbRobust->addItem(i18n("off"));
0034     ui.lRobust->setVisible(false);
0035     ui.cbRobust->setVisible(false);
0036 
0037     ui.leMaxIterations->setValidator(new QIntValidator(ui.leMaxIterations));
0038     ui.leEps->setValidator(new QDoubleValidator(ui.leEps));
0039     ui.leEvaluatedPoints->setValidator(new QIntValidator(ui.leEvaluatedPoints));
0040 
0041     const auto numberLocale = QLocale();
0042     ui.leMaxIterations->setText(numberLocale.toString(m_fitData->maxIterations));
0043     ui.leEps->setText(numberLocale.toString(m_fitData->eps));
0044     ui.leEvaluatedPoints->setText(numberLocale.toString(static_cast<qulonglong>(m_fitData->evaluatedPoints)));
0045     ui.sbConfidenceInterval->setLocale(numberLocale);
0046 
0047     // range widgets
0048     const auto* plot = static_cast<const CartesianPlot*>(fitCurve->parentAspect());
0049     const int xIndex = plot->coordinateSystem(m_fitCurve->coordinateSystemIndex())->index(CartesianCoordinateSystem::Dimension::X);
0050     m_dateTimeRange = (plot->xRangeFormat(xIndex) != RangeT::Format::Numeric);
0051     if (!m_dateTimeRange) {
0052         ui.leMin->setText(numberLocale.toString(m_fitData->fitRange.start()));
0053         ui.leMax->setText(numberLocale.toString(m_fitData->fitRange.end()));
0054         ui.leEvalMin->setText(numberLocale.toString(m_fitData->evalRange.start()));
0055         ui.leEvalMax->setText(numberLocale.toString(m_fitData->evalRange.end()));
0056     } else {
0057         ui.dateTimeEditMin->setMSecsSinceEpochUTC(m_fitData->fitRange.start());
0058         ui.dateTimeEditMax->setMSecsSinceEpochUTC(m_fitData->fitRange.end());
0059         ui.dateTimeEditEvalMin->setMSecsSinceEpochUTC(m_fitData->evalRange.start());
0060         ui.dateTimeEditEvalMax->setMSecsSinceEpochUTC(m_fitData->evalRange.end());
0061     }
0062     // changing data range not supported by ML
0063     if (fitData->algorithm == nsl_fit_algorithm_ml)
0064         ui.cbAutoRange->setEnabled(false);
0065 
0066     ui.leMin->setVisible(!m_dateTimeRange);
0067     ui.leMax->setVisible(!m_dateTimeRange);
0068     ui.lXRange->setVisible(!m_dateTimeRange);
0069     ui.leEvalMin->setVisible(!m_dateTimeRange);
0070     ui.leEvalMax->setVisible(!m_dateTimeRange);
0071     ui.lEvalRange->setVisible(!m_dateTimeRange);
0072     ui.dateTimeEditMin->setVisible(m_dateTimeRange);
0073     ui.dateTimeEditMax->setVisible(m_dateTimeRange);
0074     ui.lXRangeDateTime->setVisible(m_dateTimeRange);
0075     ui.dateTimeEditEvalMin->setVisible(m_dateTimeRange);
0076     ui.dateTimeEditEvalMax->setVisible(m_dateTimeRange);
0077     ui.lEvalRangeDateTime->setVisible(m_dateTimeRange);
0078 
0079     // auto range
0080     ui.cbAutoRange->setChecked(m_fitData->autoRange);
0081     ui.cbAutoEvalRange->setChecked(m_fitData->autoEvalRange);
0082     this->autoRangeChanged();
0083     this->autoEvalRangeChanged();
0084 
0085     ui.cbUseDataErrors->setChecked(m_fitData->useDataErrors);
0086     ui.cbUseResults->setChecked(m_fitData->useResults);
0087     ui.cbPreview->setChecked(m_fitData->previewEnabled);
0088     ui.sbConfidenceInterval->setValue(m_fitData->confidenceInterval);
0089 
0090     // SLOTS
0091     connect(ui.leEps, &QLineEdit::textChanged, this, &FitOptionsWidget::changed);
0092     connect(ui.leMaxIterations, &QLineEdit::textChanged, this, &FitOptionsWidget::changed);
0093     connect(ui.leEvaluatedPoints, &QLineEdit::textChanged, this, &FitOptionsWidget::changed);
0094     connect(ui.cbUseDataErrors, &QCheckBox::clicked, this, &FitOptionsWidget::changed);
0095     connect(ui.cbUseResults, &QCheckBox::clicked, this, &FitOptionsWidget::changed);
0096     connect(ui.cbPreview, &QCheckBox::clicked, this, &FitOptionsWidget::changed);
0097     connect(ui.sbConfidenceInterval, QOverload<double>::of(&NumberSpinBox::valueChanged), this, &FitOptionsWidget::changed);
0098     connect(ui.pbApply, &QPushButton::clicked, this, &FitOptionsWidget::applyClicked);
0099     connect(ui.pbCancel, &QPushButton::clicked, this, &FitOptionsWidget::finished);
0100     connect(ui.cbAutoRange, &QCheckBox::clicked, this, &FitOptionsWidget::autoRangeChanged);
0101     connect(ui.cbAutoEvalRange, &QCheckBox::clicked, this, &FitOptionsWidget::autoEvalRangeChanged);
0102     connect(ui.leMin, &QLineEdit::textChanged, this, &FitOptionsWidget::fitRangeMinChanged);
0103     connect(ui.leMax, &QLineEdit::textChanged, this, &FitOptionsWidget::fitRangeMaxChanged);
0104     connect(ui.dateTimeEditMin, &UTCDateTimeEdit::mSecsSinceEpochUTCChanged, this, &FitOptionsWidget::fitRangeMinDateTimeChanged);
0105     connect(ui.dateTimeEditMax, &UTCDateTimeEdit::mSecsSinceEpochUTCChanged, this, &FitOptionsWidget::fitRangeMaxDateTimeChanged);
0106     connect(ui.leEvalMin, &QLineEdit::textChanged, this, &FitOptionsWidget::evalRangeMinChanged);
0107     connect(ui.leEvalMax, &QLineEdit::textChanged, this, &FitOptionsWidget::evalRangeMaxChanged);
0108     connect(ui.dateTimeEditEvalMin, &UTCDateTimeEdit::mSecsSinceEpochUTCChanged, this, &FitOptionsWidget::evalRangeMinDateTimeChanged);
0109     connect(ui.dateTimeEditEvalMax, &UTCDateTimeEdit::mSecsSinceEpochUTCChanged, this, &FitOptionsWidget::evalRangeMaxDateTimeChanged);
0110 }
0111 
0112 void FitOptionsWidget::autoRangeChanged() {
0113     const bool autoRange = ui.cbAutoRange->isChecked();
0114     m_fitData->autoRange = autoRange;
0115 
0116     ui.leMin->setEnabled(!autoRange);
0117     ui.lXRange->setEnabled(!autoRange);
0118     ui.leMax->setEnabled(!autoRange);
0119     ui.dateTimeEditMin->setEnabled(!autoRange);
0120     ui.lXRange->setEnabled(!autoRange);
0121     ui.dateTimeEditMax->setEnabled(!autoRange);
0122 
0123     if (autoRange) {
0124         const AbstractColumn* xDataColumn = nullptr;
0125         if (m_fitCurve->dataSourceType() == XYAnalysisCurve::DataSourceType::Spreadsheet)
0126             xDataColumn = m_fitCurve->xDataColumn();
0127         else {
0128             if (m_fitCurve->dataSourceCurve())
0129                 xDataColumn = m_fitCurve->dataSourceCurve()->xColumn();
0130         }
0131 
0132         if (xDataColumn) {
0133             const double xMin = xDataColumn->minimum();
0134             const double xMax = xDataColumn->maximum();
0135             m_fitData->fitRange.setRange(xMin, xMax);
0136 
0137             const auto numberLocale = QLocale();
0138             if (!m_dateTimeRange) {
0139                 ui.leMin->setText(numberLocale.toString(xMin));
0140                 ui.leMax->setText(numberLocale.toString(xMax));
0141             } else {
0142                 ui.dateTimeEditMin->setMSecsSinceEpochUTC(xMin);
0143                 ui.dateTimeEditMax->setMSecsSinceEpochUTC(xMax);
0144             }
0145         }
0146     }
0147 }
0148 
0149 void FitOptionsWidget::autoEvalRangeChanged() {
0150     const bool autoRange = ui.cbAutoEvalRange->isChecked();
0151     m_fitData->autoEvalRange = autoRange;
0152 
0153     ui.leEvalMin->setEnabled(!autoRange);
0154     ui.lEvalRange->setEnabled(!autoRange);
0155     ui.leEvalMax->setEnabled(!autoRange);
0156     ui.dateTimeEditEvalMin->setEnabled(!autoRange);
0157     ui.lEvalRange->setEnabled(!autoRange);
0158     ui.dateTimeEditEvalMax->setEnabled(!autoRange);
0159 
0160     if (autoRange) {
0161         const AbstractColumn* xDataColumn = nullptr;
0162         switch (m_fitCurve->dataSourceType()) {
0163         case XYAnalysisCurve::DataSourceType::Spreadsheet:
0164             xDataColumn = m_fitCurve->xDataColumn();
0165             break;
0166         case XYAnalysisCurve::DataSourceType::Curve:
0167             if (m_fitCurve->dataSourceCurve())
0168                 xDataColumn = m_fitCurve->dataSourceCurve()->xColumn();
0169             break;
0170         case XYAnalysisCurve::DataSourceType::Histogram:
0171             if (m_fitCurve->dataSourceHistogram())
0172                 xDataColumn = m_fitCurve->dataSourceHistogram()->bins();
0173         }
0174 
0175         if (xDataColumn) {
0176             const double xMin = xDataColumn->minimum();
0177             const double xMax = xDataColumn->maximum();
0178             m_fitData->evalRange.setRange(xMin, xMax);
0179 
0180             const auto numberLocale = QLocale();
0181             if (!m_dateTimeRange) {
0182                 ui.leEvalMin->setText(numberLocale.toString(xMin));
0183                 ui.leEvalMax->setText(numberLocale.toString(xMax));
0184             } else {
0185                 ui.dateTimeEditEvalMin->setMSecsSinceEpochUTC(xMin);
0186                 ui.dateTimeEditEvalMax->setMSecsSinceEpochUTC(xMax);
0187             }
0188         }
0189     }
0190 }
0191 
0192 void FitOptionsWidget::fitRangeMinChanged() {
0193     SET_DOUBLE_FROM_LE(m_fitData->fitRange.start(), ui.leMin);
0194     changed();
0195 }
0196 void FitOptionsWidget::fitRangeMaxChanged() {
0197     SET_DOUBLE_FROM_LE(m_fitData->fitRange.end(), ui.leMax);
0198     changed();
0199 }
0200 
0201 void FitOptionsWidget::fitRangeMinDateTimeChanged(qint64 value) {
0202     m_fitData->fitRange.setStart(value);
0203     changed();
0204 }
0205 
0206 void FitOptionsWidget::fitRangeMaxDateTimeChanged(qint64 value) {
0207     m_fitData->fitRange.setEnd(value);
0208     changed();
0209 }
0210 
0211 void FitOptionsWidget::evalRangeMinChanged() {
0212     SET_DOUBLE_FROM_LE(m_fitData->evalRange.start(), ui.leEvalMin);
0213     changed();
0214 }
0215 void FitOptionsWidget::evalRangeMaxChanged() {
0216     SET_DOUBLE_FROM_LE(m_fitData->evalRange.end(), ui.leEvalMax);
0217     changed();
0218 }
0219 
0220 void FitOptionsWidget::evalRangeMinDateTimeChanged(qint64 value) {
0221     m_fitData->evalRange.setStart(value);
0222     changed();
0223 }
0224 
0225 void FitOptionsWidget::evalRangeMaxDateTimeChanged(qint64 value) {
0226     m_fitData->evalRange.setEnd(value);
0227     changed();
0228 }
0229 
0230 void FitOptionsWidget::applyClicked() {
0231     SET_INT_FROM_LE(m_fitData->maxIterations, ui.leMaxIterations);
0232     SET_DOUBLE_FROM_LE(m_fitData->eps, ui.leEps);
0233     SET_INT_FROM_LE(m_fitData->evaluatedPoints, ui.leEvaluatedPoints);
0234 
0235     m_fitData->useDataErrors = ui.cbUseDataErrors->isChecked();
0236     m_fitData->useResults = ui.cbUseResults->isChecked();
0237     m_fitData->previewEnabled = ui.cbPreview->isChecked();
0238     m_fitData->confidenceInterval = ui.sbConfidenceInterval->value();
0239 
0240     if (m_changed)
0241         Q_EMIT optionsChanged();
0242 
0243     Q_EMIT finished();
0244 }
0245 
0246 void FitOptionsWidget::changed() {
0247     m_changed = true;
0248 }