File indexing completed on 2025-10-12 03:31:22

0001 /*
0002     File             : XYIntegrationCurveDock.cpp
0003     Project          : LabPlot
0004     Description      : widget for editing properties of integration curves
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2016-2021 Stefan Gerlach <stefan.gerlach@uni.kn>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "XYIntegrationCurveDock.h"
0012 #include "backend/worksheet/plots/cartesian/CartesianCoordinateSystem.h"
0013 #include "backend/worksheet/plots/cartesian/XYIntegrationCurve.h"
0014 #include "commonfrontend/widgets/TreeViewComboBox.h"
0015 
0016 #include <QMenu>
0017 #include <QWidgetAction>
0018 
0019 extern "C" {
0020 #include "backend/nsl/nsl_int.h"
0021 }
0022 
0023 /*!
0024   \class XYIntegrationCurveDock
0025  \brief  Provides a widget for editing the properties of the XYIntegrationCurves
0026         (2D-curves defined by a integration) currently selected in
0027         the project explorer.
0028 
0029   If more than one curves are set, the properties of the first column are shown.
0030   The changes of the properties are applied to all curves.
0031   The exclusions are the name, the comment and the datasets (columns) of
0032   the curves  - these properties can only be changed if there is only one single curve.
0033 
0034   \ingroup kdefrontend
0035 */
0036 
0037 XYIntegrationCurveDock::XYIntegrationCurveDock(QWidget* parent)
0038     : XYAnalysisCurveDock(parent) {
0039 }
0040 
0041 /*!
0042  *  // Tab "General"
0043  */
0044 void XYIntegrationCurveDock::setupGeneral() {
0045     auto* generalTab = new QWidget(ui.tabGeneral);
0046     uiGeneralTab.setupUi(generalTab);
0047     setPlotRangeCombobox(uiGeneralTab.cbPlotRanges);
0048     setBaseWidgets(uiGeneralTab.leName, uiGeneralTab.teComment, uiGeneralTab.pbRecalculate, uiGeneralTab.cbDataSourceType);
0049     setVisibilityWidgets(uiGeneralTab.chkVisible, uiGeneralTab.chkLegendVisible);
0050 
0051     auto* gridLayout = static_cast<QGridLayout*>(generalTab->layout());
0052     gridLayout->setContentsMargins(2, 2, 2, 2);
0053     gridLayout->setHorizontalSpacing(2);
0054     gridLayout->setVerticalSpacing(2);
0055 
0056     cbDataSourceCurve = new TreeViewComboBox(generalTab);
0057     gridLayout->addWidget(cbDataSourceCurve, 5, 2, 1, 3);
0058     cbXDataColumn = new TreeViewComboBox(generalTab);
0059     gridLayout->addWidget(cbXDataColumn, 6, 2, 1, 3);
0060     cbYDataColumn = new TreeViewComboBox(generalTab);
0061     gridLayout->addWidget(cbYDataColumn, 7, 2, 1, 3);
0062 
0063     for (int i = 0; i < NSL_INT_NETHOD_COUNT; i++)
0064         uiGeneralTab.cbMethod->addItem(i18n(nsl_int_method_name[i]));
0065 
0066     uiGeneralTab.leMin->setValidator(new QDoubleValidator(uiGeneralTab.leMin));
0067     uiGeneralTab.leMax->setValidator(new QDoubleValidator(uiGeneralTab.leMax));
0068 
0069     auto* layout = new QHBoxLayout(ui.tabGeneral);
0070     layout->setContentsMargins(0, 0, 0, 0);
0071     layout->addWidget(generalTab);
0072 
0073     // Slots
0074     connect(uiGeneralTab.cbDataSourceType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYIntegrationCurveDock::dataSourceTypeChanged);
0075     connect(uiGeneralTab.cbAutoRange, &QCheckBox::clicked, this, &XYIntegrationCurveDock::autoRangeChanged);
0076     connect(uiGeneralTab.leMin, &QLineEdit::textChanged, this, &XYIntegrationCurveDock::xRangeMinChanged);
0077     connect(uiGeneralTab.leMax, &QLineEdit::textChanged, this, &XYIntegrationCurveDock::xRangeMaxChanged);
0078     connect(uiGeneralTab.dateTimeEditMin, &UTCDateTimeEdit::mSecsSinceEpochUTCChanged, this, &XYIntegrationCurveDock::xRangeMinDateTimeChanged);
0079     connect(uiGeneralTab.dateTimeEditMax, &UTCDateTimeEdit::mSecsSinceEpochUTCChanged, this, &XYIntegrationCurveDock::xRangeMaxDateTimeChanged);
0080     connect(uiGeneralTab.cbMethod, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYIntegrationCurveDock::methodChanged);
0081     connect(uiGeneralTab.cbAbsolute, &QCheckBox::clicked, this, &XYIntegrationCurveDock::absoluteChanged);
0082     connect(uiGeneralTab.pbRecalculate, &QPushButton::clicked, this, &XYIntegrationCurveDock::recalculateClicked);
0083 
0084     connect(cbDataSourceCurve, &TreeViewComboBox::currentModelIndexChanged, this, &XYIntegrationCurveDock::dataSourceCurveChanged);
0085     connect(cbXDataColumn, &TreeViewComboBox::currentModelIndexChanged, this, &XYIntegrationCurveDock::xDataColumnChanged);
0086     connect(cbYDataColumn, &TreeViewComboBox::currentModelIndexChanged, this, &XYIntegrationCurveDock::yDataColumnChanged);
0087 }
0088 
0089 void XYIntegrationCurveDock::initGeneralTab() {
0090     // show the properties of the first curve
0091     // data source
0092     uiGeneralTab.cbDataSourceType->setCurrentIndex(static_cast<int>(m_integrationCurve->dataSourceType()));
0093     this->dataSourceTypeChanged(uiGeneralTab.cbDataSourceType->currentIndex());
0094     cbDataSourceCurve->setAspect(m_integrationCurve->dataSourceCurve());
0095     cbXDataColumn->setColumn(m_integrationCurve->xDataColumn(), m_integrationCurve->xDataColumnPath());
0096     cbYDataColumn->setColumn(m_integrationCurve->yDataColumn(), m_integrationCurve->yDataColumnPath());
0097 
0098     // range widgets
0099     const auto* plot = static_cast<const CartesianPlot*>(m_integrationCurve->parentAspect());
0100     const int xIndex = plot->coordinateSystem(m_curve->coordinateSystemIndex())->index(CartesianCoordinateSystem::Dimension::X);
0101     m_dateTimeRange = (plot->xRangeFormat(xIndex) != RangeT::Format::Numeric);
0102     if (!m_dateTimeRange) {
0103         const auto numberLocale = QLocale();
0104         uiGeneralTab.leMin->setText(numberLocale.toString(m_integrationData.xRange.first()));
0105         uiGeneralTab.leMax->setText(numberLocale.toString(m_integrationData.xRange.last()));
0106     } else {
0107         uiGeneralTab.dateTimeEditMin->setMSecsSinceEpochUTC(m_integrationData.xRange.first());
0108         uiGeneralTab.dateTimeEditMax->setMSecsSinceEpochUTC(m_integrationData.xRange.last());
0109     }
0110 
0111     uiGeneralTab.lMin->setVisible(!m_dateTimeRange);
0112     uiGeneralTab.leMin->setVisible(!m_dateTimeRange);
0113     uiGeneralTab.lMax->setVisible(!m_dateTimeRange);
0114     uiGeneralTab.leMax->setVisible(!m_dateTimeRange);
0115     uiGeneralTab.lMinDateTime->setVisible(m_dateTimeRange);
0116     uiGeneralTab.dateTimeEditMin->setVisible(m_dateTimeRange);
0117     uiGeneralTab.lMaxDateTime->setVisible(m_dateTimeRange);
0118     uiGeneralTab.dateTimeEditMax->setVisible(m_dateTimeRange);
0119 
0120     // auto range
0121     uiGeneralTab.cbAutoRange->setChecked(m_integrationData.autoRange);
0122     this->autoRangeChanged();
0123 
0124     // update list of selectable types
0125     xDataColumnChanged(cbXDataColumn->currentModelIndex());
0126 
0127     uiGeneralTab.cbMethod->setCurrentIndex(m_integrationData.method);
0128     this->methodChanged(m_integrationData.method);
0129     uiGeneralTab.cbAbsolute->setChecked(m_integrationData.absolute);
0130     this->absoluteChanged();
0131 
0132     this->showIntegrationResult();
0133 
0134     uiGeneralTab.chkLegendVisible->setChecked(m_curve->legendVisible());
0135     uiGeneralTab.chkVisible->setChecked(m_curve->isVisible());
0136 
0137     // Slots
0138     connect(m_integrationCurve, &XYIntegrationCurve::dataSourceTypeChanged, this, &XYIntegrationCurveDock::curveDataSourceTypeChanged);
0139     connect(m_integrationCurve, &XYIntegrationCurve::dataSourceCurveChanged, this, &XYIntegrationCurveDock::curveDataSourceCurveChanged);
0140     connect(m_integrationCurve, &XYIntegrationCurve::xDataColumnChanged, this, &XYIntegrationCurveDock::curveXDataColumnChanged);
0141     connect(m_integrationCurve, &XYIntegrationCurve::yDataColumnChanged, this, &XYIntegrationCurveDock::curveYDataColumnChanged);
0142     connect(m_integrationCurve, &XYIntegrationCurve::integrationDataChanged, this, &XYIntegrationCurveDock::curveIntegrationDataChanged);
0143     connect(m_integrationCurve, &XYIntegrationCurve::sourceDataChanged, this, &XYIntegrationCurveDock::enableRecalculate);
0144 }
0145 
0146 void XYIntegrationCurveDock::setModel() {
0147     auto list = defaultColumnTopLevelClasses();
0148     list.append(AspectType::XYFitCurve);
0149 
0150     XYAnalysisCurveDock::setModel(list);
0151 }
0152 
0153 /*!
0154   sets the curves. The properties of the curves in the list \c list can be edited in this widget.
0155 */
0156 void XYIntegrationCurveDock::setCurves(QList<XYCurve*> list) {
0157     CONDITIONAL_LOCK_RETURN;
0158     m_curvesList = list;
0159     m_curve = list.first();
0160     setAspects(list);
0161     setAnalysisCurves(list);
0162     m_integrationCurve = static_cast<XYIntegrationCurve*>(m_curve);
0163     this->setModel();
0164     m_integrationData = m_integrationCurve->integrationData();
0165 
0166     initGeneralTab();
0167     initTabs();
0168     setSymbols(list);
0169 
0170     updatePlotRangeList();
0171 }
0172 
0173 //*************************************************************
0174 //**** SLOTs for changes triggered in XYFitCurveDock *****
0175 //*************************************************************
0176 
0177 void XYIntegrationCurveDock::dataSourceTypeChanged(int index) {
0178     const auto type = (XYAnalysisCurve::DataSourceType)index;
0179     if (type == XYAnalysisCurve::DataSourceType::Spreadsheet) {
0180         uiGeneralTab.lDataSourceCurve->hide();
0181         cbDataSourceCurve->hide();
0182         uiGeneralTab.lXColumn->show();
0183         cbXDataColumn->show();
0184         uiGeneralTab.lYColumn->show();
0185         cbYDataColumn->show();
0186     } else {
0187         uiGeneralTab.lDataSourceCurve->show();
0188         cbDataSourceCurve->show();
0189         uiGeneralTab.lXColumn->hide();
0190         cbXDataColumn->hide();
0191         uiGeneralTab.lYColumn->hide();
0192         cbYDataColumn->hide();
0193     }
0194 
0195     CONDITIONAL_LOCK_RETURN;
0196 
0197     for (auto* curve : m_curvesList)
0198         static_cast<XYIntegrationCurve*>(curve)->setDataSourceType(type);
0199 
0200     enableRecalculate();
0201 }
0202 
0203 void XYIntegrationCurveDock::xDataColumnChanged(const QModelIndex& index) {
0204     CONDITIONAL_LOCK_RETURN;
0205 
0206     auto* column = static_cast<AbstractColumn*>(index.internalPointer());
0207 
0208     for (auto* curve : m_curvesList)
0209         static_cast<XYIntegrationCurve*>(curve)->setXDataColumn(column);
0210 
0211     if (column) {
0212         if (uiGeneralTab.cbAutoRange->isChecked()) {
0213             const auto numberLocale = QLocale();
0214             uiGeneralTab.leMin->setText(numberLocale.toString(column->minimum()));
0215             uiGeneralTab.leMax->setText(numberLocale.toString(column->maximum()));
0216         }
0217 
0218         updateSettings(column);
0219     }
0220 
0221     enableRecalculate();
0222 }
0223 
0224 /*!
0225  * disable integration methods that need more data points
0226  */
0227 void XYIntegrationCurveDock::updateSettings(const AbstractColumn* column) {
0228     if (!column)
0229         return;
0230 
0231     // TODO
0232     //  size_t n = 0;
0233     //  for (int row = 0; row < column->rowCount(); row++)
0234     //      if (!std::isnan(column->valueAt(row)) && !column->isMasked(row))
0235     //          n++;
0236 }
0237 
0238 void XYIntegrationCurveDock::autoRangeChanged() {
0239     bool autoRange = uiGeneralTab.cbAutoRange->isChecked();
0240     m_integrationData.autoRange = autoRange;
0241 
0242     uiGeneralTab.lMin->setEnabled(!autoRange);
0243     uiGeneralTab.leMin->setEnabled(!autoRange);
0244     uiGeneralTab.lMax->setEnabled(!autoRange);
0245     uiGeneralTab.leMax->setEnabled(!autoRange);
0246     uiGeneralTab.lMinDateTime->setEnabled(!autoRange);
0247     uiGeneralTab.dateTimeEditMin->setEnabled(!autoRange);
0248     uiGeneralTab.lMaxDateTime->setEnabled(!autoRange);
0249     uiGeneralTab.dateTimeEditMax->setEnabled(!autoRange);
0250 
0251     if (autoRange) {
0252         const AbstractColumn* xDataColumn = nullptr;
0253         if (m_integrationCurve->dataSourceType() == XYAnalysisCurve::DataSourceType::Spreadsheet)
0254             xDataColumn = m_integrationCurve->xDataColumn();
0255         else {
0256             if (m_integrationCurve->dataSourceCurve())
0257                 xDataColumn = m_integrationCurve->dataSourceCurve()->xColumn();
0258         }
0259 
0260         if (xDataColumn) {
0261             if (!m_dateTimeRange) {
0262                 const auto numberLocale = QLocale();
0263                 uiGeneralTab.leMin->setText(numberLocale.toString(xDataColumn->minimum()));
0264                 uiGeneralTab.leMax->setText(numberLocale.toString(xDataColumn->maximum()));
0265             } else {
0266                 uiGeneralTab.dateTimeEditMin->setMSecsSinceEpochUTC(xDataColumn->minimum());
0267                 uiGeneralTab.dateTimeEditMax->setMSecsSinceEpochUTC(xDataColumn->maximum());
0268             }
0269         }
0270     }
0271 }
0272 
0273 void XYIntegrationCurveDock::xRangeMinChanged() {
0274     SET_DOUBLE_FROM_LE_REC(m_integrationData.xRange.first(), uiGeneralTab.leMin);
0275 }
0276 
0277 void XYIntegrationCurveDock::xRangeMaxChanged() {
0278     SET_DOUBLE_FROM_LE_REC(m_integrationData.xRange.last(), uiGeneralTab.leMax);
0279 }
0280 
0281 void XYIntegrationCurveDock::xRangeMinDateTimeChanged(qint64 value) {
0282     CONDITIONAL_LOCK_RETURN;
0283 
0284     m_integrationData.xRange.first() = value;
0285     enableRecalculate();
0286 }
0287 
0288 void XYIntegrationCurveDock::xRangeMaxDateTimeChanged(qint64 value) {
0289     CONDITIONAL_LOCK_RETURN;
0290 
0291     m_integrationData.xRange.last() = value;
0292     enableRecalculate();
0293 }
0294 
0295 void XYIntegrationCurveDock::methodChanged(int index) {
0296     const auto method = (nsl_int_method_type)index;
0297     m_integrationData.method = method;
0298 
0299     // update absolute option
0300     switch (method) {
0301     case nsl_int_method_rectangle:
0302     case nsl_int_method_trapezoid:
0303         uiGeneralTab.cbAbsolute->setEnabled(true);
0304         break;
0305     case nsl_int_method_simpson:
0306     case nsl_int_method_simpson_3_8:
0307         uiGeneralTab.cbAbsolute->setChecked(false);
0308         uiGeneralTab.cbAbsolute->setEnabled(false);
0309     }
0310 
0311     enableRecalculate();
0312 }
0313 
0314 void XYIntegrationCurveDock::absoluteChanged() {
0315     bool absolute = uiGeneralTab.cbAbsolute->isChecked();
0316     m_integrationData.absolute = absolute;
0317 
0318     enableRecalculate();
0319 }
0320 
0321 void XYIntegrationCurveDock::recalculateClicked() {
0322     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
0323 
0324     for (auto* curve : m_curvesList)
0325         static_cast<XYIntegrationCurve*>(curve)->setIntegrationData(m_integrationData);
0326 
0327     uiGeneralTab.pbRecalculate->setEnabled(false);
0328     Q_EMIT info(i18n("Integration status: %1", m_integrationCurve->integrationResult().status));
0329     QApplication::restoreOverrideCursor();
0330 }
0331 
0332 /*!
0333  * show the result and details of the integration
0334  */
0335 void XYIntegrationCurveDock::showIntegrationResult() {
0336     showResult(m_integrationCurve, uiGeneralTab.teResult);
0337 }
0338 
0339 QString XYIntegrationCurveDock::customText() const {
0340     const auto numberLocale = QLocale();
0341     return i18n("value: %1", numberLocale.toString(m_integrationCurve->integrationResult().value)) + QStringLiteral("<br>");
0342 }
0343 
0344 //*************************************************************
0345 //*********** SLOTs for changes triggered in XYCurve **********
0346 //*************************************************************
0347 // General-Tab
0348 void XYIntegrationCurveDock::curveIntegrationDataChanged(const XYIntegrationCurve::IntegrationData& integrationData) {
0349     CONDITIONAL_LOCK_RETURN;
0350     m_integrationData = integrationData;
0351     uiGeneralTab.cbMethod->setCurrentIndex(m_integrationData.method);
0352     this->methodChanged(m_integrationData.method);
0353     uiGeneralTab.cbAbsolute->setChecked(m_integrationData.absolute);
0354     this->absoluteChanged();
0355 
0356     this->showIntegrationResult();
0357 }