File indexing completed on 2025-07-13 03:32:48

0001 /*
0002     File             : XYDifferentiationCurveDock.cpp
0003     Project          : LabPlot
0004     Description      : widget for editing properties of differentiation curves
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2016-2021 Stefan Gerlach <stefan.gerlach@uni.kn>
0007     SPDX-FileCopyrightText: 2017-2023 Alexander Semke <alexander.semke@web.de>
0008 
0009     SPDX-License-Identifier: GPL-2.0-or-later
0010 */
0011 
0012 #include "XYDifferentiationCurveDock.h"
0013 #include "backend/core/column/Column.h"
0014 #include "backend/worksheet/plots/cartesian/CartesianCoordinateSystem.h"
0015 #include "backend/worksheet/plots/cartesian/XYDifferentiationCurve.h"
0016 #include "commonfrontend/widgets/TreeViewComboBox.h"
0017 
0018 #include <QStandardItemModel>
0019 
0020 extern "C" {
0021 #include "backend/nsl/nsl_diff.h"
0022 }
0023 
0024 /*!
0025   \class XYDifferentiationCurveDock
0026  \brief  Provides a widget for editing the properties of the XYDifferentiationCurves
0027         (2D-curves defined by a differentiation) currently selected in
0028         the project explorer.
0029 
0030   If more than one curves are set, the properties of the first column are shown.
0031   The changes of the properties are applied to all curves.
0032   The exclusions are the name, the comment and the datasets (columns) of
0033   the curves  - these properties can only be changed if there is only one single curve.
0034 
0035   \ingroup kdefrontend
0036 */
0037 
0038 XYDifferentiationCurveDock::XYDifferentiationCurveDock(QWidget* parent)
0039     : XYAnalysisCurveDock(parent) {
0040 }
0041 
0042 /*!
0043  *  // Tab "General"
0044  */
0045 void XYDifferentiationCurveDock::setupGeneral() {
0046     auto* generalTab = new QWidget(ui.tabGeneral);
0047     uiGeneralTab.setupUi(generalTab);
0048     setPlotRangeCombobox(uiGeneralTab.cbPlotRanges);
0049     setBaseWidgets(uiGeneralTab.leName, uiGeneralTab.teComment, uiGeneralTab.pbRecalculate, uiGeneralTab.cbDataSourceType);
0050     setVisibilityWidgets(uiGeneralTab.chkVisible, uiGeneralTab.chkLegendVisible);
0051 
0052     auto* gridLayout = static_cast<QGridLayout*>(generalTab->layout());
0053     gridLayout->setContentsMargins(2, 2, 2, 2);
0054     gridLayout->setHorizontalSpacing(2);
0055     gridLayout->setVerticalSpacing(2);
0056 
0057     cbDataSourceCurve = new TreeViewComboBox(generalTab);
0058     gridLayout->addWidget(cbDataSourceCurve, 5, 2, 1, 3);
0059     cbXDataColumn = new TreeViewComboBox(generalTab);
0060     gridLayout->addWidget(cbXDataColumn, 6, 2, 1, 3);
0061     cbYDataColumn = new TreeViewComboBox(generalTab);
0062     gridLayout->addWidget(cbYDataColumn, 7, 2, 1, 3);
0063 
0064     for (int i = 0; i < NSL_DIFF_DERIV_ORDER_COUNT; ++i)
0065         uiGeneralTab.cbDerivOrder->addItem(i18n(nsl_diff_deriv_order_name[i]));
0066 
0067     uiGeneralTab.leMin->setValidator(new QDoubleValidator(uiGeneralTab.leMin));
0068     uiGeneralTab.leMax->setValidator(new QDoubleValidator(uiGeneralTab.leMax));
0069 
0070     auto* layout = new QHBoxLayout(ui.tabGeneral);
0071     layout->setContentsMargins(0, 0, 0, 0);
0072     layout->addWidget(generalTab);
0073 
0074     // Slots
0075     connect(uiGeneralTab.cbDataSourceType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYDifferentiationCurveDock::dataSourceTypeChanged);
0076     connect(uiGeneralTab.cbAutoRange, &QCheckBox::clicked, this, &XYDifferentiationCurveDock::autoRangeChanged);
0077     connect(uiGeneralTab.leMin, &QLineEdit::textChanged, this, &XYDifferentiationCurveDock::xRangeMinChanged);
0078     connect(uiGeneralTab.leMax, &QLineEdit::textChanged, this, &XYDifferentiationCurveDock::xRangeMaxChanged);
0079     connect(uiGeneralTab.dateTimeEditMin, &UTCDateTimeEdit::mSecsSinceEpochUTCChanged, this, &XYDifferentiationCurveDock::xRangeMinDateTimeChanged);
0080     connect(uiGeneralTab.dateTimeEditMax, &UTCDateTimeEdit::mSecsSinceEpochUTCChanged, this, &XYDifferentiationCurveDock::xRangeMaxDateTimeChanged);
0081     connect(uiGeneralTab.cbDerivOrder, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYDifferentiationCurveDock::derivOrderChanged);
0082     connect(uiGeneralTab.sbAccOrder, QOverload<int>::of(&QSpinBox::valueChanged), this, &XYDifferentiationCurveDock::accOrderChanged);
0083     connect(uiGeneralTab.pbRecalculate, &QPushButton::clicked, this, &XYDifferentiationCurveDock::recalculateClicked);
0084 
0085     connect(cbDataSourceCurve, &TreeViewComboBox::currentModelIndexChanged, this, &XYDifferentiationCurveDock::dataSourceCurveChanged);
0086     connect(cbXDataColumn, &TreeViewComboBox::currentModelIndexChanged, this, &XYDifferentiationCurveDock::xDataColumnChanged);
0087     connect(cbYDataColumn, &TreeViewComboBox::currentModelIndexChanged, this, &XYDifferentiationCurveDock::yDataColumnChanged);
0088 }
0089 
0090 void XYDifferentiationCurveDock::initGeneralTab() {
0091     // show the properties of the first curve
0092     // data source
0093     uiGeneralTab.cbDataSourceType->setCurrentIndex(static_cast<int>(m_differentiationCurve->dataSourceType()));
0094     this->dataSourceTypeChanged(uiGeneralTab.cbDataSourceType->currentIndex());
0095     cbDataSourceCurve->setAspect(m_differentiationCurve->dataSourceCurve());
0096     cbXDataColumn->setColumn(m_differentiationCurve->xDataColumn(), m_differentiationCurve->xDataColumnPath());
0097     cbYDataColumn->setColumn(m_differentiationCurve->yDataColumn(), m_differentiationCurve->yDataColumnPath());
0098 
0099     // range widgets
0100     const auto* plot = static_cast<const CartesianPlot*>(m_differentiationCurve->parentAspect());
0101     const int xIndex = plot->coordinateSystem(m_curve->coordinateSystemIndex())->index(CartesianCoordinateSystem::Dimension::X);
0102     m_dateTimeRange = (plot->xRangeFormat(xIndex) != RangeT::Format::Numeric);
0103     if (!m_dateTimeRange) {
0104         const auto numberLocale = QLocale();
0105         uiGeneralTab.leMin->setText(numberLocale.toString(m_differentiationData.xRange.first()));
0106         uiGeneralTab.leMax->setText(numberLocale.toString(m_differentiationData.xRange.last()));
0107     } else {
0108         uiGeneralTab.dateTimeEditMin->setMSecsSinceEpochUTC(m_differentiationData.xRange.first());
0109         uiGeneralTab.dateTimeEditMax->setMSecsSinceEpochUTC(m_differentiationData.xRange.last());
0110     }
0111 
0112     uiGeneralTab.lMin->setVisible(!m_dateTimeRange);
0113     uiGeneralTab.leMin->setVisible(!m_dateTimeRange);
0114     uiGeneralTab.lMax->setVisible(!m_dateTimeRange);
0115     uiGeneralTab.leMax->setVisible(!m_dateTimeRange);
0116     uiGeneralTab.lMinDateTime->setVisible(m_dateTimeRange);
0117     uiGeneralTab.dateTimeEditMin->setVisible(m_dateTimeRange);
0118     uiGeneralTab.lMaxDateTime->setVisible(m_dateTimeRange);
0119     uiGeneralTab.dateTimeEditMax->setVisible(m_dateTimeRange);
0120 
0121     // auto range
0122     uiGeneralTab.cbAutoRange->setChecked(m_differentiationData.autoRange);
0123     this->autoRangeChanged();
0124 
0125     // update list of selectable types
0126     xDataColumnChanged(cbXDataColumn->currentModelIndex());
0127 
0128     uiGeneralTab.cbDerivOrder->setCurrentIndex(m_differentiationData.derivOrder);
0129     this->derivOrderChanged(m_differentiationData.derivOrder);
0130 
0131     uiGeneralTab.sbAccOrder->setValue(m_differentiationData.accOrder);
0132     this->accOrderChanged(m_differentiationData.accOrder);
0133 
0134     this->showDifferentiationResult();
0135 
0136     uiGeneralTab.chkLegendVisible->setChecked(m_curve->legendVisible());
0137     uiGeneralTab.chkVisible->setChecked(m_curve->isVisible());
0138 
0139     // Slots
0140     connect(m_differentiationCurve, &XYDifferentiationCurve::dataSourceTypeChanged, this, &XYDifferentiationCurveDock::curveDataSourceTypeChanged);
0141     connect(m_differentiationCurve, &XYDifferentiationCurve::dataSourceCurveChanged, this, &XYDifferentiationCurveDock::curveDataSourceCurveChanged);
0142     connect(m_differentiationCurve, &XYDifferentiationCurve::xDataColumnChanged, this, &XYDifferentiationCurveDock::curveXDataColumnChanged);
0143     connect(m_differentiationCurve, &XYDifferentiationCurve::yDataColumnChanged, this, &XYDifferentiationCurveDock::curveYDataColumnChanged);
0144     connect(m_differentiationCurve, &XYDifferentiationCurve::differentiationDataChanged, this, &XYDifferentiationCurveDock::curveDifferentiationDataChanged);
0145     connect(m_differentiationCurve, &XYDifferentiationCurve::sourceDataChanged, this, &XYDifferentiationCurveDock::enableRecalculate);
0146 }
0147 
0148 void XYDifferentiationCurveDock::setModel() {
0149     auto list = defaultColumnTopLevelClasses();
0150     list.append(AspectType::XYFitCurve);
0151 
0152     XYAnalysisCurveDock::setModel(list);
0153 }
0154 
0155 /*!
0156   sets the curves. The properties of the curves in the list \c list can be edited in this widget.
0157 */
0158 void XYDifferentiationCurveDock::setCurves(QList<XYCurve*> list) {
0159     CONDITIONAL_LOCK_RETURN;
0160     m_curvesList = list;
0161     m_curve = list.first();
0162     setAspects(list);
0163     setAnalysisCurves(list);
0164     m_differentiationCurve = static_cast<XYDifferentiationCurve*>(m_curve);
0165     this->setModel();
0166     m_differentiationData = m_differentiationCurve->differentiationData();
0167 
0168     initGeneralTab();
0169     initTabs();
0170     setSymbols(list);
0171 
0172     updatePlotRangeList();
0173 }
0174 
0175 //*************************************************************
0176 //**** SLOTs for changes triggered in XYFitCurveDock *****
0177 //*************************************************************
0178 void XYDifferentiationCurveDock::dataSourceTypeChanged(int index) {
0179     const auto type = (XYAnalysisCurve::DataSourceType)index;
0180     if (type == XYAnalysisCurve::DataSourceType::Spreadsheet) {
0181         uiGeneralTab.lDataSourceCurve->hide();
0182         cbDataSourceCurve->hide();
0183         uiGeneralTab.lXColumn->show();
0184         cbXDataColumn->show();
0185         uiGeneralTab.lYColumn->show();
0186         cbYDataColumn->show();
0187     } else {
0188         uiGeneralTab.lDataSourceCurve->show();
0189         cbDataSourceCurve->show();
0190         uiGeneralTab.lXColumn->hide();
0191         cbXDataColumn->hide();
0192         uiGeneralTab.lYColumn->hide();
0193         cbYDataColumn->hide();
0194     }
0195 
0196     CONDITIONAL_LOCK_RETURN;
0197 
0198     for (auto* curve : m_curvesList)
0199         static_cast<XYDifferentiationCurve*>(curve)->setDataSourceType(type);
0200 
0201     enableRecalculate();
0202 }
0203 
0204 /*!
0205  * disable deriv orders and accuracies that need more data points
0206  */
0207 void XYDifferentiationCurveDock::updateSettings(const AbstractColumn* column) {
0208     if (!column)
0209         return;
0210 
0211     const auto& statistics = static_cast<const Column*>(column)->statistics();
0212 
0213     if (uiGeneralTab.cbAutoRange->isChecked()) {
0214         const auto numberLocale = QLocale();
0215         uiGeneralTab.leMin->setText(numberLocale.toString(statistics.minimum));
0216         uiGeneralTab.leMax->setText(numberLocale.toString(statistics.maximum));
0217     }
0218 
0219     const int n = statistics.size;
0220 
0221     const auto* model = qobject_cast<const QStandardItemModel*>(uiGeneralTab.cbDerivOrder->model());
0222     auto* item = model->item(nsl_diff_deriv_order_first);
0223     if (n < 3)
0224         item->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled));
0225     else {
0226         item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
0227         if (n < 5)
0228             uiGeneralTab.sbAccOrder->setMinimum(2);
0229     }
0230 
0231     item = model->item(nsl_diff_deriv_order_second);
0232     if (n < 3) {
0233         item->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled));
0234         if (uiGeneralTab.cbDerivOrder->currentIndex() == nsl_diff_deriv_order_second)
0235             uiGeneralTab.cbDerivOrder->setCurrentIndex(nsl_diff_deriv_order_first);
0236     } else {
0237         item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
0238         if (n < 4)
0239             uiGeneralTab.sbAccOrder->setMinimum(1);
0240         else if (n < 5)
0241             uiGeneralTab.sbAccOrder->setMinimum(2);
0242     }
0243 
0244     item = model->item(nsl_diff_deriv_order_third);
0245     if (n < 5) {
0246         item->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled));
0247         if (uiGeneralTab.cbDerivOrder->currentIndex() == nsl_diff_deriv_order_third)
0248             uiGeneralTab.cbDerivOrder->setCurrentIndex(nsl_diff_deriv_order_first);
0249     } else
0250         item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
0251 
0252     item = model->item(nsl_diff_deriv_order_fourth);
0253     if (n < 5) {
0254         item->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled));
0255         if (uiGeneralTab.cbDerivOrder->currentIndex() == nsl_diff_deriv_order_fourth)
0256             uiGeneralTab.cbDerivOrder->setCurrentIndex(nsl_diff_deriv_order_first);
0257     } else {
0258         item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
0259         if (n < 7)
0260             uiGeneralTab.sbAccOrder->setMinimum(1);
0261     }
0262 
0263     item = model->item(nsl_diff_deriv_order_fifth);
0264     if (n < 7) {
0265         item->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled));
0266         if (uiGeneralTab.cbDerivOrder->currentIndex() == nsl_diff_deriv_order_fifth)
0267             uiGeneralTab.cbDerivOrder->setCurrentIndex(nsl_diff_deriv_order_first);
0268     } else
0269         item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
0270 
0271     item = model->item(nsl_diff_deriv_order_sixth);
0272     if (n < 7) {
0273         item->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled));
0274         if (uiGeneralTab.cbDerivOrder->currentIndex() == nsl_diff_deriv_order_sixth)
0275             uiGeneralTab.cbDerivOrder->setCurrentIndex(nsl_diff_deriv_order_first);
0276     } else
0277         item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
0278 }
0279 
0280 void XYDifferentiationCurveDock::autoRangeChanged() {
0281     bool autoRange = uiGeneralTab.cbAutoRange->isChecked();
0282     m_differentiationData.autoRange = autoRange;
0283 
0284     uiGeneralTab.lMin->setEnabled(!autoRange);
0285     uiGeneralTab.leMin->setEnabled(!autoRange);
0286     uiGeneralTab.lMax->setEnabled(!autoRange);
0287     uiGeneralTab.leMax->setEnabled(!autoRange);
0288     uiGeneralTab.lMinDateTime->setEnabled(!autoRange);
0289     uiGeneralTab.dateTimeEditMin->setEnabled(!autoRange);
0290     uiGeneralTab.lMaxDateTime->setEnabled(!autoRange);
0291     uiGeneralTab.dateTimeEditMax->setEnabled(!autoRange);
0292 
0293     if (autoRange) {
0294         const AbstractColumn* xDataColumn = nullptr;
0295         if (m_differentiationCurve->dataSourceType() == XYAnalysisCurve::DataSourceType::Spreadsheet)
0296             xDataColumn = m_differentiationCurve->xDataColumn();
0297         else {
0298             if (m_differentiationCurve->dataSourceCurve())
0299                 xDataColumn = m_differentiationCurve->dataSourceCurve()->xColumn();
0300         }
0301 
0302         if (xDataColumn) {
0303             if (!m_dateTimeRange) {
0304                 const auto numberLocale = QLocale();
0305                 uiGeneralTab.leMin->setText(numberLocale.toString(xDataColumn->minimum()));
0306                 uiGeneralTab.leMax->setText(numberLocale.toString(xDataColumn->maximum()));
0307             } else {
0308                 uiGeneralTab.dateTimeEditMin->setMSecsSinceEpochUTC(xDataColumn->minimum());
0309                 uiGeneralTab.dateTimeEditMax->setMSecsSinceEpochUTC(xDataColumn->maximum());
0310             }
0311         }
0312     }
0313 }
0314 
0315 void XYDifferentiationCurveDock::xRangeMinChanged() {
0316     SET_DOUBLE_FROM_LE_REC(m_differentiationData.xRange.first(), uiGeneralTab.leMin);
0317 }
0318 
0319 void XYDifferentiationCurveDock::xRangeMaxChanged() {
0320     SET_DOUBLE_FROM_LE_REC(m_differentiationData.xRange.last(), uiGeneralTab.leMax);
0321 }
0322 
0323 void XYDifferentiationCurveDock::xRangeMinDateTimeChanged(qint64 value) {
0324     CONDITIONAL_LOCK_RETURN;
0325 
0326     m_differentiationData.xRange.first() = value;
0327     enableRecalculate();
0328 }
0329 
0330 void XYDifferentiationCurveDock::xRangeMaxDateTimeChanged(qint64 value) {
0331     CONDITIONAL_LOCK_RETURN;
0332 
0333     m_differentiationData.xRange.last() = value;
0334     enableRecalculate();
0335 }
0336 
0337 void XYDifferentiationCurveDock::derivOrderChanged(int index) {
0338     const auto derivOrder = (nsl_diff_deriv_order_type)index;
0339     m_differentiationData.derivOrder = derivOrder;
0340 
0341     // update avail. accuracies
0342     switch (derivOrder) {
0343     case nsl_diff_deriv_order_first:
0344         uiGeneralTab.sbAccOrder->setMinimum(2);
0345         uiGeneralTab.sbAccOrder->setMaximum(4);
0346         uiGeneralTab.sbAccOrder->setSingleStep(2);
0347         uiGeneralTab.sbAccOrder->setValue(4);
0348         break;
0349     case nsl_diff_deriv_order_second:
0350         uiGeneralTab.sbAccOrder->setMinimum(1);
0351         uiGeneralTab.sbAccOrder->setMaximum(3);
0352         uiGeneralTab.sbAccOrder->setSingleStep(1);
0353         uiGeneralTab.sbAccOrder->setValue(3);
0354         break;
0355     case nsl_diff_deriv_order_third:
0356         uiGeneralTab.sbAccOrder->setMinimum(2);
0357         uiGeneralTab.sbAccOrder->setMaximum(2);
0358         break;
0359     case nsl_diff_deriv_order_fourth:
0360         uiGeneralTab.sbAccOrder->setMinimum(1);
0361         uiGeneralTab.sbAccOrder->setMaximum(3);
0362         uiGeneralTab.sbAccOrder->setSingleStep(2);
0363         uiGeneralTab.sbAccOrder->setValue(3);
0364         break;
0365     case nsl_diff_deriv_order_fifth:
0366         uiGeneralTab.sbAccOrder->setMinimum(2);
0367         uiGeneralTab.sbAccOrder->setMaximum(2);
0368         break;
0369     case nsl_diff_deriv_order_sixth:
0370         uiGeneralTab.sbAccOrder->setMinimum(1);
0371         uiGeneralTab.sbAccOrder->setMaximum(1);
0372         break;
0373     }
0374 
0375     enableRecalculate();
0376 }
0377 
0378 void XYDifferentiationCurveDock::accOrderChanged(int value) {
0379     m_differentiationData.accOrder = value;
0380     enableRecalculate();
0381 }
0382 
0383 void XYDifferentiationCurveDock::recalculateClicked() {
0384     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
0385 
0386     for (auto* curve : m_curvesList)
0387         static_cast<XYDifferentiationCurve*>(curve)->setDifferentiationData(m_differentiationData);
0388 
0389     uiGeneralTab.pbRecalculate->setEnabled(false);
0390     Q_EMIT info(i18n("Differentiation status: %1", m_differentiationCurve->differentiationResult().status));
0391     QApplication::restoreOverrideCursor();
0392 }
0393 
0394 /*!
0395  * show the result and details of the differentiation
0396  */
0397 void XYDifferentiationCurveDock::showDifferentiationResult() {
0398     showResult(m_differentiationCurve, uiGeneralTab.teResult);
0399 }
0400 
0401 //*************************************************************
0402 //*** SLOTs for changes triggered in XYDifferentiationCurve ***
0403 //*************************************************************
0404 // General-Tab
0405 void XYDifferentiationCurveDock::curveDifferentiationDataChanged(const XYDifferentiationCurve::DifferentiationData& differentiationData) {
0406     CONDITIONAL_LOCK_RETURN;
0407     m_differentiationData = differentiationData;
0408     uiGeneralTab.cbDerivOrder->setCurrentIndex(m_differentiationData.derivOrder);
0409     this->derivOrderChanged(m_differentiationData.derivOrder);
0410     uiGeneralTab.sbAccOrder->setValue(m_differentiationData.accOrder);
0411     this->accOrderChanged(m_differentiationData.accOrder);
0412 
0413     this->showDifferentiationResult();
0414 }