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 }