File indexing completed on 2024-05-12 15:28:08
0001 /*************************************************************************** 0002 File : XYFourierTransformCurveDock.cpp 0003 Project : LabPlot 0004 -------------------------------------------------------------------- 0005 Copyright : (C) 2016 Stefan Gerlach (stefan.gerlach@uni.kn) 0006 Description : widget for editing properties of Fourier transform curves 0007 0008 ***************************************************************************/ 0009 0010 /*************************************************************************** 0011 * * 0012 * This program is free software; you can redistribute it and/or modify * 0013 * it under the terms of the GNU General Public License as published by * 0014 * the Free Software Foundation; either version 2 of the License, or * 0015 * (at your option) any later version. * 0016 * * 0017 * This program is distributed in the hope that it will be useful, * 0018 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0019 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0020 * GNU General Public License for more details. * 0021 * * 0022 * You should have received a copy of the GNU General Public License * 0023 * along with this program; if not, write to the Free Software * 0024 * Foundation, Inc., 51 Franklin Street, Fifth Floor, * 0025 * Boston, MA 02110-1301 USA * 0026 * * 0027 ***************************************************************************/ 0028 0029 #include "XYFourierTransformCurveDock.h" 0030 #include "backend/core/AspectTreeModel.h" 0031 #include "backend/core/Project.h" 0032 #include "backend/worksheet/plots/cartesian/XYFourierTransformCurve.h" 0033 #include "commonfrontend/widgets/TreeViewComboBox.h" 0034 0035 #include <KMessageBox> 0036 0037 #include <QMenu> 0038 #include <QWidgetAction> 0039 0040 /*! 0041 \class XYFourierTransformCurveDock 0042 \brief Provides a widget for editing the properties of the XYFourierTransformCurves 0043 (2D-curves defined by a Fourier transform) currently selected in 0044 the project explorer. 0045 0046 If more then one curves are set, the properties of the first column are shown. 0047 The changes of the properties are applied to all curves. 0048 The exclusions are the name, the comment and the datasets (columns) of 0049 the curves - these properties can only be changed if there is only one single curve. 0050 0051 \ingroup kdefrontend 0052 */ 0053 0054 XYFourierTransformCurveDock::XYFourierTransformCurveDock(QWidget *parent) : XYCurveDock(parent) { 0055 } 0056 0057 /*! 0058 * // Tab "General" 0059 */ 0060 void XYFourierTransformCurveDock::setupGeneral() { 0061 QWidget* generalTab = new QWidget(ui.tabGeneral); 0062 uiGeneralTab.setupUi(generalTab); 0063 m_leName = uiGeneralTab.leName; 0064 m_leComment = uiGeneralTab.leComment; 0065 0066 auto* gridLayout = static_cast<QGridLayout*>(generalTab->layout()); 0067 gridLayout->setContentsMargins(2,2,2,2); 0068 gridLayout->setHorizontalSpacing(2); 0069 gridLayout->setVerticalSpacing(2); 0070 0071 cbXDataColumn = new TreeViewComboBox(generalTab); 0072 gridLayout->addWidget(cbXDataColumn, 5, 2, 1, 2); 0073 cbYDataColumn = new TreeViewComboBox(generalTab); 0074 gridLayout->addWidget(cbYDataColumn, 6, 2, 1, 2); 0075 0076 for (int i = 0; i < NSL_SF_WINDOW_TYPE_COUNT; i++) 0077 uiGeneralTab.cbWindowType->addItem(i18n(nsl_sf_window_type_name[i])); 0078 for (int i = 0; i < NSL_DFT_RESULT_TYPE_COUNT; i++) 0079 uiGeneralTab.cbType->addItem(i18n(nsl_dft_result_type_name[i])); 0080 for (int i = 0; i < NSL_DFT_XSCALE_COUNT; i++) 0081 uiGeneralTab.cbXScale->addItem(i18n(nsl_dft_xscale_name[i])); 0082 0083 //TODO: use line edits 0084 uiGeneralTab.sbMin->setRange(-std::numeric_limits<double>::max(), std::numeric_limits<double>::max()); 0085 uiGeneralTab.sbMax->setRange(-std::numeric_limits<double>::max(), std::numeric_limits<double>::max()); 0086 0087 auto* layout = new QHBoxLayout(ui.tabGeneral); 0088 layout->setMargin(0); 0089 layout->addWidget(generalTab); 0090 0091 //Slots 0092 connect( uiGeneralTab.leName, &QLineEdit::textChanged, this, &XYFourierTransformCurveDock::nameChanged ); 0093 connect( uiGeneralTab.leComment, &QLineEdit::textChanged, this, &XYFourierTransformCurveDock::commentChanged ); 0094 connect( uiGeneralTab.chkVisible, &QCheckBox::clicked, this, &XYFourierTransformCurveDock::visibilityChanged); 0095 connect( uiGeneralTab.cbAutoRange, &QCheckBox::clicked, this, &XYFourierTransformCurveDock::autoRangeChanged); 0096 connect( uiGeneralTab.sbMin, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &XYFourierTransformCurveDock::xRangeMinChanged); 0097 connect( uiGeneralTab.sbMax, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &XYFourierTransformCurveDock::xRangeMaxChanged); 0098 connect( uiGeneralTab.cbWindowType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYFourierTransformCurveDock::windowTypeChanged); 0099 connect( uiGeneralTab.cbType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYFourierTransformCurveDock::typeChanged); 0100 connect( uiGeneralTab.cbTwoSided, &QCheckBox::stateChanged, this, &XYFourierTransformCurveDock::twoSidedChanged); 0101 connect( uiGeneralTab.cbShifted, &QCheckBox::stateChanged, this, &XYFourierTransformCurveDock::shiftedChanged); 0102 connect( uiGeneralTab.cbXScale, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYFourierTransformCurveDock::xScaleChanged); 0103 connect( uiGeneralTab.pbRecalculate, &QPushButton::clicked, this, &XYFourierTransformCurveDock::recalculateClicked); 0104 0105 connect(cbXDataColumn, &TreeViewComboBox::currentModelIndexChanged, this, &XYFourierTransformCurveDock::xDataColumnChanged); 0106 connect(cbYDataColumn, &TreeViewComboBox::currentModelIndexChanged, this, &XYFourierTransformCurveDock::yDataColumnChanged); 0107 } 0108 0109 void XYFourierTransformCurveDock::initGeneralTab() { 0110 //if there are more then one curve in the list, disable the tab "general" 0111 if (m_curvesList.size() == 1) { 0112 uiGeneralTab.lName->setEnabled(true); 0113 uiGeneralTab.leName->setEnabled(true); 0114 uiGeneralTab.lComment->setEnabled(true); 0115 uiGeneralTab.leComment->setEnabled(true); 0116 0117 uiGeneralTab.leName->setText(m_curve->name()); 0118 uiGeneralTab.leComment->setText(m_curve->comment()); 0119 } else { 0120 uiGeneralTab.lName->setEnabled(false); 0121 uiGeneralTab.leName->setEnabled(false); 0122 uiGeneralTab.lComment->setEnabled(false); 0123 uiGeneralTab.leComment->setEnabled(false); 0124 0125 uiGeneralTab.leName->setText(QString()); 0126 uiGeneralTab.leComment->setText(QString()); 0127 } 0128 0129 auto* analysisCurve = dynamic_cast<XYAnalysisCurve*>(m_curve); 0130 checkColumnAvailability(cbXDataColumn, analysisCurve->xDataColumn(), analysisCurve->xDataColumnPath()); 0131 checkColumnAvailability(cbYDataColumn, analysisCurve->yDataColumn(), analysisCurve->yDataColumnPath()); 0132 0133 //show the properties of the first curve 0134 m_transformCurve = dynamic_cast<XYFourierTransformCurve*>(m_curve); 0135 0136 XYCurveDock::setModelIndexFromAspect(cbXDataColumn, m_transformCurve->xDataColumn()); 0137 XYCurveDock::setModelIndexFromAspect(cbYDataColumn, m_transformCurve->yDataColumn()); 0138 uiGeneralTab.cbAutoRange->setChecked(m_transformData.autoRange); 0139 uiGeneralTab.sbMin->setValue(m_transformData.xRange.first()); 0140 uiGeneralTab.sbMax->setValue(m_transformData.xRange.last()); 0141 this->autoRangeChanged(); 0142 0143 uiGeneralTab.cbWindowType->setCurrentIndex(m_transformData.windowType); 0144 this->windowTypeChanged(); 0145 uiGeneralTab.cbType->setCurrentIndex(m_transformData.type); 0146 this->typeChanged(); 0147 uiGeneralTab.cbTwoSided->setChecked(m_transformData.twoSided); 0148 this->twoSidedChanged(); // show/hide shifted check box 0149 uiGeneralTab.cbShifted->setChecked(m_transformData.shifted); 0150 this->shiftedChanged(); 0151 uiGeneralTab.cbXScale->setCurrentIndex(m_transformData.xScale); 0152 this->xScaleChanged(); 0153 this->showTransformResult(); 0154 0155 //enable the "recalculate"-button if the source data was changed since the last transform 0156 uiGeneralTab.pbRecalculate->setEnabled(m_transformCurve->isSourceDataChangedSinceLastRecalc()); 0157 0158 uiGeneralTab.chkVisible->setChecked( m_curve->isVisible() ); 0159 0160 //Slots 0161 connect(m_transformCurve, &XYFourierTransformCurve::aspectDescriptionChanged, this, &XYFourierTransformCurveDock::curveDescriptionChanged); 0162 connect(m_transformCurve, &XYFourierTransformCurve::xDataColumnChanged, this, &XYFourierTransformCurveDock::curveXDataColumnChanged); 0163 connect(m_transformCurve, &XYFourierTransformCurve::yDataColumnChanged, this, &XYFourierTransformCurveDock::curveYDataColumnChanged); 0164 connect(m_transformCurve, &XYFourierTransformCurve::transformDataChanged, this, &XYFourierTransformCurveDock::curveTransformDataChanged); 0165 connect(m_transformCurve, &XYFourierTransformCurve::sourceDataChanged, this, &XYFourierTransformCurveDock::enableRecalculate); 0166 connect(m_transformCurve, QOverload<bool>::of(&XYCurve::visibilityChanged), this, &XYFourierTransformCurveDock::curveVisibilityChanged); 0167 } 0168 0169 void XYFourierTransformCurveDock::setModel() { 0170 QList<AspectType> list{AspectType::Folder, AspectType::Workbook, AspectType::Datapicker, 0171 AspectType::DatapickerCurve, AspectType::Spreadsheet, AspectType::LiveDataSource, 0172 AspectType::Column, AspectType::Worksheet, AspectType::CartesianPlot, 0173 AspectType::XYFitCurve, AspectType::CantorWorksheet}; 0174 cbXDataColumn->setTopLevelClasses(list); 0175 cbYDataColumn->setTopLevelClasses(list); 0176 0177 cbXDataColumn->setModel(m_aspectTreeModel); 0178 cbYDataColumn->setModel(m_aspectTreeModel); 0179 0180 XYCurveDock::setModel(); 0181 } 0182 0183 /*! 0184 sets the curves. The properties of the curves in the list \c list can be edited in this widget. 0185 */ 0186 void XYFourierTransformCurveDock::setCurves(QList<XYCurve*> list) { 0187 m_initializing = true; 0188 m_curvesList = list; 0189 m_curve = list.first(); 0190 m_aspect = m_curve; 0191 m_transformCurve = dynamic_cast<XYFourierTransformCurve*>(m_curve); 0192 m_aspectTreeModel = new AspectTreeModel(m_curve->project()); 0193 this->setModel(); 0194 m_transformData = m_transformCurve->transformData(); 0195 0196 SET_NUMBER_LOCALE 0197 uiGeneralTab.sbMin->setLocale(numberLocale); 0198 uiGeneralTab.sbMax->setLocale(numberLocale); 0199 0200 initGeneralTab(); 0201 initTabs(); 0202 m_initializing = false; 0203 } 0204 0205 //************************************************************* 0206 //**** SLOTs for changes triggered in XYFitCurveDock ***** 0207 //************************************************************* 0208 void XYFourierTransformCurveDock::xDataColumnChanged(const QModelIndex& index) { 0209 if (m_initializing) 0210 return; 0211 0212 auto* aspect = static_cast<AbstractAspect*>(index.internalPointer()); 0213 auto* column = dynamic_cast<AbstractColumn*>(aspect); 0214 0215 for (auto* curve : m_curvesList) 0216 dynamic_cast<XYFourierTransformCurve*>(curve)->setXDataColumn(column); 0217 0218 if (column != nullptr) { 0219 if (uiGeneralTab.cbAutoRange->isChecked()) { 0220 uiGeneralTab.sbMin->setValue(column->minimum()); 0221 uiGeneralTab.sbMax->setValue(column->maximum()); 0222 } 0223 } 0224 0225 cbXDataColumn->useCurrentIndexText(true); 0226 cbXDataColumn->setInvalid(false); 0227 } 0228 0229 void XYFourierTransformCurveDock::yDataColumnChanged(const QModelIndex& index) { 0230 if (m_initializing) 0231 return; 0232 0233 auto* aspect = static_cast<AbstractAspect*>(index.internalPointer()); 0234 auto* column = dynamic_cast<AbstractColumn*>(aspect); 0235 0236 for (auto* curve : m_curvesList) 0237 dynamic_cast<XYFourierTransformCurve*>(curve)->setYDataColumn(column); 0238 0239 cbYDataColumn->useCurrentIndexText(true); 0240 cbYDataColumn->setInvalid(false); 0241 } 0242 0243 void XYFourierTransformCurveDock::autoRangeChanged() { 0244 bool autoRange = uiGeneralTab.cbAutoRange->isChecked(); 0245 m_transformData.autoRange = autoRange; 0246 0247 if (autoRange) { 0248 uiGeneralTab.lMin->setEnabled(false); 0249 uiGeneralTab.sbMin->setEnabled(false); 0250 uiGeneralTab.lMax->setEnabled(false); 0251 uiGeneralTab.sbMax->setEnabled(false); 0252 m_transformCurve = dynamic_cast<XYFourierTransformCurve*>(m_curve); 0253 if (m_transformCurve->xDataColumn()) { 0254 uiGeneralTab.sbMin->setValue(m_transformCurve->xDataColumn()->minimum()); 0255 uiGeneralTab.sbMax->setValue(m_transformCurve->xDataColumn()->maximum()); 0256 } 0257 } else { 0258 uiGeneralTab.lMin->setEnabled(true); 0259 uiGeneralTab.sbMin->setEnabled(true); 0260 uiGeneralTab.lMax->setEnabled(true); 0261 uiGeneralTab.sbMax->setEnabled(true); 0262 } 0263 0264 } 0265 void XYFourierTransformCurveDock::xRangeMinChanged() { 0266 double xMin = uiGeneralTab.sbMin->value(); 0267 0268 m_transformData.xRange.first() = xMin; 0269 uiGeneralTab.pbRecalculate->setEnabled(true); 0270 } 0271 0272 void XYFourierTransformCurveDock::xRangeMaxChanged() { 0273 double xMax = uiGeneralTab.sbMax->value(); 0274 0275 m_transformData.xRange.last() = xMax; 0276 uiGeneralTab.pbRecalculate->setEnabled(true); 0277 } 0278 0279 void XYFourierTransformCurveDock::windowTypeChanged() { 0280 auto windowType = (nsl_sf_window_type)uiGeneralTab.cbWindowType->currentIndex(); 0281 m_transformData.windowType = windowType; 0282 0283 enableRecalculate(); 0284 } 0285 0286 void XYFourierTransformCurveDock::typeChanged() { 0287 auto type = (nsl_dft_result_type)uiGeneralTab.cbType->currentIndex(); 0288 m_transformData.type = type; 0289 0290 enableRecalculate(); 0291 } 0292 0293 void XYFourierTransformCurveDock::twoSidedChanged() { 0294 bool twoSided = uiGeneralTab.cbTwoSided->isChecked(); 0295 m_transformData.twoSided = twoSided; 0296 0297 if (twoSided) 0298 uiGeneralTab.cbShifted->setEnabled(true); 0299 else { 0300 uiGeneralTab.cbShifted->setEnabled(false); 0301 uiGeneralTab.cbShifted->setChecked(false); 0302 } 0303 0304 enableRecalculate(); 0305 } 0306 0307 void XYFourierTransformCurveDock::shiftedChanged() { 0308 bool shifted = uiGeneralTab.cbShifted->isChecked(); 0309 m_transformData.shifted = shifted; 0310 0311 enableRecalculate(); 0312 } 0313 0314 void XYFourierTransformCurveDock::xScaleChanged() { 0315 auto xScale = (nsl_dft_xscale)uiGeneralTab.cbXScale->currentIndex(); 0316 m_transformData.xScale = xScale; 0317 0318 enableRecalculate(); 0319 } 0320 0321 void XYFourierTransformCurveDock::recalculateClicked() { 0322 0323 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); 0324 for (auto* curve : m_curvesList) 0325 dynamic_cast<XYFourierTransformCurve*>(curve)->setTransformData(m_transformData); 0326 0327 uiGeneralTab.pbRecalculate->setEnabled(false); 0328 emit info(i18n("Fourier transformation status: %1", m_transformCurve->transformResult().status)); 0329 QApplication::restoreOverrideCursor(); 0330 } 0331 0332 void XYFourierTransformCurveDock::enableRecalculate() const { 0333 if (m_initializing) 0334 return; 0335 0336 //no transforming possible without the x- and y-data 0337 AbstractAspect* aspectX = static_cast<AbstractAspect*>(cbXDataColumn->currentModelIndex().internalPointer()); 0338 AbstractAspect* aspectY = static_cast<AbstractAspect*>(cbYDataColumn->currentModelIndex().internalPointer()); 0339 bool data = (aspectX != nullptr && aspectY != nullptr); 0340 if (aspectX) { 0341 cbXDataColumn->useCurrentIndexText(true); 0342 cbXDataColumn->setInvalid(false); 0343 } 0344 if (aspectY) { 0345 cbYDataColumn->useCurrentIndexText(true); 0346 cbYDataColumn->setInvalid(false); 0347 } 0348 0349 uiGeneralTab.pbRecalculate->setEnabled(data); 0350 } 0351 0352 /*! 0353 * show the result and details of the transform 0354 */ 0355 void XYFourierTransformCurveDock::showTransformResult() { 0356 const XYFourierTransformCurve::TransformResult& transformResult = m_transformCurve->transformResult(); 0357 if (!transformResult.available) { 0358 uiGeneralTab.teResult->clear(); 0359 return; 0360 } 0361 0362 QString str = i18n("status: %1", transformResult.status) + "<br>"; 0363 0364 if (!transformResult.valid) { 0365 uiGeneralTab.teResult->setText(str); 0366 return; //result is not valid, there was an error which is shown in the status-string, nothing to show more. 0367 } 0368 0369 SET_NUMBER_LOCALE 0370 if (transformResult.elapsedTime > 1000) 0371 str += i18n("calculation time: %1 s", numberLocale.toString(transformResult.elapsedTime/1000)) + "<br>"; 0372 else 0373 str += i18n("calculation time: %1 ms", numberLocale.toString(transformResult.elapsedTime)) + "<br>"; 0374 0375 str += "<br><br>"; 0376 0377 uiGeneralTab.teResult->setText(str); 0378 } 0379 0380 //************************************************************* 0381 //*********** SLOTs for changes triggered in XYCurve ********** 0382 //************************************************************* 0383 //General-Tab 0384 void XYFourierTransformCurveDock::curveDescriptionChanged(const AbstractAspect* aspect) { 0385 if (m_curve != aspect) 0386 return; 0387 0388 m_initializing = true; 0389 if (aspect->name() != uiGeneralTab.leName->text()) 0390 uiGeneralTab.leName->setText(aspect->name()); 0391 else if (aspect->comment() != uiGeneralTab.leComment->text()) 0392 uiGeneralTab.leComment->setText(aspect->comment()); 0393 m_initializing = false; 0394 } 0395 0396 void XYFourierTransformCurveDock::curveXDataColumnChanged(const AbstractColumn* column) { 0397 m_initializing = true; 0398 XYCurveDock::setModelIndexFromAspect(cbXDataColumn, column); 0399 m_initializing = false; 0400 } 0401 0402 void XYFourierTransformCurveDock::curveYDataColumnChanged(const AbstractColumn* column) { 0403 m_initializing = true; 0404 XYCurveDock::setModelIndexFromAspect(cbYDataColumn, column); 0405 m_initializing = false; 0406 } 0407 0408 void XYFourierTransformCurveDock::curveTransformDataChanged(const XYFourierTransformCurve::TransformData& transformData) { 0409 m_initializing = true; 0410 m_transformData = transformData; 0411 uiGeneralTab.cbType->setCurrentIndex(m_transformData.type); 0412 this->typeChanged(); 0413 0414 this->showTransformResult(); 0415 m_initializing = false; 0416 } 0417 0418 void XYFourierTransformCurveDock::dataChanged() { 0419 this->enableRecalculate(); 0420 } 0421 0422 void XYFourierTransformCurveDock::curveVisibilityChanged(bool on) { 0423 m_initializing = true; 0424 uiGeneralTab.chkVisible->setChecked(on); 0425 m_initializing = false; 0426 }