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 }