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 }