File indexing completed on 2024-05-12 15:28:08
0001 /*************************************************************************** 0002 File : XYFourierFilterCurveDock.cpp 0003 Project : LabPlot 0004 -------------------------------------------------------------------- 0005 Copyright : (C) 2016 Stefan Gerlach (stefan.gerlach@uni.kn) 0006 Description : widget for editing properties of Fourier filter 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 "XYFourierFilterCurveDock.h" 0030 #include "backend/core/AspectTreeModel.h" 0031 #include "backend/core/Project.h" 0032 #include "backend/worksheet/plots/cartesian/XYFourierFilterCurve.h" 0033 #include "commonfrontend/widgets/TreeViewComboBox.h" 0034 0035 #include <KMessageBox> 0036 0037 #include <QMenu> 0038 #include <QWidgetAction> 0039 0040 /*! 0041 \class XYFourierFilterCurveDock 0042 \brief Provides a widget for editing the properties of the XYFourierFilterCurves 0043 (2D-curves defined by a Fourier filter) 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 XYFourierFilterCurveDock::XYFourierFilterCurveDock(QWidget* parent) : XYCurveDock(parent) { 0055 } 0056 0057 /*! 0058 * // Tab "General" 0059 */ 0060 void XYFourierFilterCurveDock::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 uiGeneralTab.cbDataSourceType->addItem(i18n("Spreadsheet")); 0072 uiGeneralTab.cbDataSourceType->addItem(i18n("XY-Curve")); 0073 0074 cbDataSourceCurve = new TreeViewComboBox(generalTab); 0075 gridLayout->addWidget(cbDataSourceCurve, 5, 2, 1, 3); 0076 cbXDataColumn = new TreeViewComboBox(generalTab); 0077 gridLayout->addWidget(cbXDataColumn, 6, 2, 1, 2); 0078 cbYDataColumn = new TreeViewComboBox(generalTab); 0079 gridLayout->addWidget(cbYDataColumn, 7, 2, 1, 2); 0080 0081 for (int i = 0; i < NSL_FILTER_TYPE_COUNT; i++) 0082 uiGeneralTab.cbType->addItem(i18n(nsl_filter_type_name[i])); 0083 0084 for (int i = 0; i < NSL_FILTER_FORM_COUNT; i++) 0085 uiGeneralTab.cbForm->addItem(i18n(nsl_filter_form_name[i])); 0086 0087 for (int i = 0; i < NSL_FILTER_CUTOFF_UNIT_COUNT; i++) { 0088 uiGeneralTab.cbUnit->addItem(i18n(nsl_filter_cutoff_unit_name[i])); 0089 uiGeneralTab.cbUnit2->addItem(i18n(nsl_filter_cutoff_unit_name[i])); 0090 } 0091 0092 //TODO: use line edits 0093 uiGeneralTab.sbMin->setRange(-std::numeric_limits<double>::max(), std::numeric_limits<double>::max()); 0094 uiGeneralTab.sbMax->setRange(-std::numeric_limits<double>::max(), std::numeric_limits<double>::max()); 0095 0096 uiGeneralTab.pbRecalculate->setIcon(QIcon::fromTheme("run-build")); 0097 0098 auto* layout = new QHBoxLayout(ui.tabGeneral); 0099 layout->setMargin(0); 0100 layout->addWidget(generalTab); 0101 0102 //Slots 0103 connect(uiGeneralTab.leName, &QLineEdit::textChanged, this, &XYFourierFilterCurveDock::nameChanged ); 0104 connect(uiGeneralTab.leComment, &QLineEdit::textChanged, this, &XYFourierFilterCurveDock::commentChanged ); 0105 connect(uiGeneralTab.chkVisible, &QCheckBox::clicked, this, &XYFourierFilterCurveDock::visibilityChanged); 0106 connect(uiGeneralTab.cbDataSourceType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYFourierFilterCurveDock::dataSourceTypeChanged); 0107 connect(uiGeneralTab.cbAutoRange, &QCheckBox::clicked, this, &XYFourierFilterCurveDock::autoRangeChanged); 0108 connect(uiGeneralTab.sbMin, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &XYFourierFilterCurveDock::xRangeMinChanged); 0109 connect(uiGeneralTab.sbMax, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &XYFourierFilterCurveDock::xRangeMaxChanged); 0110 0111 connect(uiGeneralTab.cbType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYFourierFilterCurveDock::typeChanged); 0112 connect(uiGeneralTab.cbForm, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYFourierFilterCurveDock::formChanged); 0113 connect(uiGeneralTab.sbOrder, QOverload<int>::of(&QSpinBox::valueChanged), this, &XYFourierFilterCurveDock::orderChanged); 0114 connect(uiGeneralTab.sbCutoff, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &XYFourierFilterCurveDock::enableRecalculate); 0115 connect(uiGeneralTab.sbCutoff2, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &XYFourierFilterCurveDock::enableRecalculate); 0116 connect(uiGeneralTab.cbUnit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYFourierFilterCurveDock::unitChanged); 0117 connect(uiGeneralTab.cbUnit2, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYFourierFilterCurveDock::unit2Changed); 0118 connect(uiGeneralTab.pbRecalculate, &QPushButton::clicked, this, &XYFourierFilterCurveDock::recalculateClicked); 0119 0120 connect(cbDataSourceCurve, &TreeViewComboBox::currentModelIndexChanged, this, &XYFourierFilterCurveDock::dataSourceCurveChanged); 0121 connect(cbXDataColumn, &TreeViewComboBox::currentModelIndexChanged, this, &XYFourierFilterCurveDock::xDataColumnChanged); 0122 connect(cbYDataColumn, &TreeViewComboBox::currentModelIndexChanged, this, &XYFourierFilterCurveDock::yDataColumnChanged); 0123 } 0124 0125 void XYFourierFilterCurveDock::initGeneralTab() { 0126 //if there are more then one curve in the list, disable the tab "general" 0127 if (m_curvesList.size() == 1) { 0128 uiGeneralTab.lName->setEnabled(true); 0129 uiGeneralTab.leName->setEnabled(true); 0130 uiGeneralTab.lComment->setEnabled(true); 0131 uiGeneralTab.leComment->setEnabled(true); 0132 0133 uiGeneralTab.leName->setText(m_curve->name()); 0134 uiGeneralTab.leComment->setText(m_curve->comment()); 0135 } else { 0136 uiGeneralTab.lName->setEnabled(false); 0137 uiGeneralTab.leName->setEnabled(false); 0138 uiGeneralTab.lComment->setEnabled(false); 0139 uiGeneralTab.leComment->setEnabled(false); 0140 0141 uiGeneralTab.leName->setText(QString()); 0142 uiGeneralTab.leComment->setText(QString()); 0143 } 0144 0145 auto* analysisCurve = dynamic_cast<XYAnalysisCurve*>(m_curve); 0146 checkColumnAvailability(cbXDataColumn, analysisCurve->xDataColumn(), analysisCurve->xDataColumnPath()); 0147 checkColumnAvailability(cbYDataColumn, analysisCurve->yDataColumn(), analysisCurve->yDataColumnPath()); 0148 0149 //show the properties of the first curve 0150 m_filterCurve = dynamic_cast<XYFourierFilterCurve*>(m_curve); 0151 0152 uiGeneralTab.cbDataSourceType->setCurrentIndex(static_cast<int>(m_filterCurve->dataSourceType())); 0153 this->dataSourceTypeChanged(uiGeneralTab.cbDataSourceType->currentIndex()); 0154 XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, m_filterCurve->dataSourceCurve()); 0155 XYCurveDock::setModelIndexFromAspect(cbXDataColumn, m_filterCurve->xDataColumn()); 0156 XYCurveDock::setModelIndexFromAspect(cbYDataColumn, m_filterCurve->yDataColumn()); 0157 uiGeneralTab.cbAutoRange->setChecked(m_filterData.autoRange); 0158 uiGeneralTab.sbMin->setValue(m_filterData.xRange.first()); 0159 uiGeneralTab.sbMax->setValue(m_filterData.xRange.last()); 0160 this->autoRangeChanged(); 0161 0162 uiGeneralTab.cbType->setCurrentIndex(m_filterData.type); 0163 this->typeChanged(); 0164 uiGeneralTab.cbForm->setCurrentIndex(m_filterData.form); 0165 this->formChanged(); 0166 uiGeneralTab.sbOrder->setValue((int)m_filterData.order); 0167 uiGeneralTab.cbUnit->setCurrentIndex(m_filterData.unit); 0168 this->unitChanged(); 0169 // after unit has set 0170 uiGeneralTab.sbCutoff->setValue(m_filterData.cutoff); 0171 uiGeneralTab.cbUnit2->setCurrentIndex(m_filterData.unit2); 0172 this->unit2Changed(); 0173 // after unit has set 0174 uiGeneralTab.sbCutoff2->setValue(m_filterData.cutoff2); 0175 this->showFilterResult(); 0176 0177 uiGeneralTab.chkVisible->setChecked( m_curve->isVisible() ); 0178 0179 //Slots 0180 connect(m_filterCurve, &XYFourierFilterCurve::aspectDescriptionChanged, this, &XYFourierFilterCurveDock::curveDescriptionChanged); 0181 connect(m_filterCurve, &XYFourierFilterCurve::dataSourceTypeChanged, this, &XYFourierFilterCurveDock::curveDataSourceTypeChanged); 0182 connect(m_filterCurve, &XYFourierFilterCurve::dataSourceCurveChanged, this, &XYFourierFilterCurveDock::curveDataSourceCurveChanged); 0183 connect(m_filterCurve, &XYFourierFilterCurve::xDataColumnChanged, this, &XYFourierFilterCurveDock::curveXDataColumnChanged); 0184 connect(m_filterCurve, &XYFourierFilterCurve::yDataColumnChanged, this, &XYFourierFilterCurveDock::curveYDataColumnChanged); 0185 connect(m_filterCurve, &XYFourierFilterCurve::filterDataChanged, this, &XYFourierFilterCurveDock::curveFilterDataChanged); 0186 connect(m_filterCurve, &XYFourierFilterCurve::sourceDataChanged, this, &XYFourierFilterCurveDock::enableRecalculate); 0187 connect(m_filterCurve, QOverload<bool>::of(&XYCurve::visibilityChanged), this, &XYFourierFilterCurveDock::curveVisibilityChanged); 0188 } 0189 0190 void XYFourierFilterCurveDock::setModel() { 0191 QList<AspectType> list{AspectType::Folder, AspectType::Datapicker, AspectType::Worksheet, 0192 AspectType::CartesianPlot, AspectType::XYCurve, AspectType::XYAnalysisCurve}; 0193 cbDataSourceCurve->setTopLevelClasses(list); 0194 0195 QList<const AbstractAspect*> hiddenAspects; 0196 for (auto* curve : m_curvesList) 0197 hiddenAspects << curve; 0198 cbDataSourceCurve->setHiddenAspects(hiddenAspects); 0199 0200 list = {AspectType::Folder, AspectType::Workbook, AspectType::Datapicker, 0201 AspectType::DatapickerCurve, AspectType::Spreadsheet, AspectType::LiveDataSource, 0202 AspectType::Column, AspectType::Worksheet, AspectType::CartesianPlot, AspectType::XYFitCurve 0203 }; 0204 cbXDataColumn->setTopLevelClasses(list); 0205 cbYDataColumn->setTopLevelClasses(list); 0206 0207 cbDataSourceCurve->setModel(m_aspectTreeModel); 0208 cbXDataColumn->setModel(m_aspectTreeModel); 0209 cbYDataColumn->setModel(m_aspectTreeModel); 0210 0211 XYCurveDock::setModel(); 0212 } 0213 0214 /*! 0215 sets the curves. The properties of the curves in the list \c list can be edited in this widget. 0216 */ 0217 void XYFourierFilterCurveDock::setCurves(QList<XYCurve*> list) { 0218 m_initializing = true; 0219 m_curvesList = list; 0220 m_curve = list.first(); 0221 m_aspect = m_curve; 0222 m_filterCurve = dynamic_cast<XYFourierFilterCurve*>(m_curve); 0223 m_aspectTreeModel = new AspectTreeModel(m_curve->project()); 0224 this->setModel(); 0225 m_filterData = m_filterCurve->filterData(); 0226 0227 SET_NUMBER_LOCALE 0228 uiGeneralTab.sbMin->setLocale(numberLocale); 0229 uiGeneralTab.sbMax->setLocale(numberLocale); 0230 uiGeneralTab.sbCutoff->setLocale(numberLocale); 0231 uiGeneralTab.sbCutoff2->setLocale(numberLocale); 0232 0233 initGeneralTab(); 0234 initTabs(); 0235 m_initializing = false; 0236 } 0237 0238 //************************************************************* 0239 //**** SLOTs for changes triggered in XYFitCurveDock ***** 0240 //************************************************************* 0241 void XYFourierFilterCurveDock::dataSourceTypeChanged(int index) { 0242 auto type = (XYAnalysisCurve::DataSourceType)index; 0243 if (type == XYAnalysisCurve::DataSourceType::Spreadsheet) { 0244 uiGeneralTab.lDataSourceCurve->hide(); 0245 cbDataSourceCurve->hide(); 0246 uiGeneralTab.lXColumn->show(); 0247 cbXDataColumn->show(); 0248 uiGeneralTab.lYColumn->show(); 0249 cbYDataColumn->show(); 0250 } else { 0251 uiGeneralTab.lDataSourceCurve->show(); 0252 cbDataSourceCurve->show(); 0253 uiGeneralTab.lXColumn->hide(); 0254 cbXDataColumn->hide(); 0255 uiGeneralTab.lYColumn->hide(); 0256 cbYDataColumn->hide(); 0257 } 0258 0259 if (m_initializing) 0260 return; 0261 0262 for (auto* curve : m_curvesList) 0263 dynamic_cast<XYFourierFilterCurve*>(curve)->setDataSourceType(type); 0264 } 0265 0266 void XYFourierFilterCurveDock::dataSourceCurveChanged(const QModelIndex& index) { 0267 auto* aspect = static_cast<AbstractAspect*>(index.internalPointer()); 0268 XYCurve* dataSourceCurve{}; 0269 if (aspect) 0270 dataSourceCurve = dynamic_cast<XYCurve*>(aspect); 0271 0272 // update range of cutoff spin boxes (like a unit change) 0273 unitChanged(); 0274 unit2Changed(); 0275 0276 if (m_initializing) 0277 return; 0278 0279 for (auto* curve : m_curvesList) 0280 dynamic_cast<XYFourierFilterCurve*>(curve)->setDataSourceCurve(dataSourceCurve); 0281 } 0282 0283 void XYFourierFilterCurveDock::xDataColumnChanged(const QModelIndex& index) { 0284 if (m_initializing) 0285 return; 0286 0287 auto* aspect = static_cast<AbstractAspect*>(index.internalPointer()); 0288 auto* column = dynamic_cast<AbstractColumn*>(aspect); 0289 0290 for (auto* curve : m_curvesList) 0291 dynamic_cast<XYFourierFilterCurve*>(curve)->setXDataColumn(column); 0292 0293 // update range of cutoff spin boxes (like a unit change) 0294 unitChanged(); 0295 unit2Changed(); 0296 0297 if (column != nullptr) { 0298 if (uiGeneralTab.cbAutoRange->isChecked()) { 0299 uiGeneralTab.sbMin->setValue(column->minimum()); 0300 uiGeneralTab.sbMax->setValue(column->maximum()); 0301 } 0302 } 0303 0304 cbXDataColumn->useCurrentIndexText(true); 0305 cbXDataColumn->setInvalid(false); 0306 } 0307 0308 void XYFourierFilterCurveDock::yDataColumnChanged(const QModelIndex& index) { 0309 if (m_initializing) 0310 return; 0311 0312 auto* aspect = static_cast<AbstractAspect*>(index.internalPointer()); 0313 auto* column = dynamic_cast<AbstractColumn*>(aspect); 0314 0315 for (auto* curve : m_curvesList) 0316 dynamic_cast<XYFourierFilterCurve*>(curve)->setYDataColumn(column); 0317 0318 cbYDataColumn->useCurrentIndexText(true); 0319 cbYDataColumn->setInvalid(false); 0320 } 0321 0322 void XYFourierFilterCurveDock::autoRangeChanged() { 0323 bool autoRange = uiGeneralTab.cbAutoRange->isChecked(); 0324 m_filterData.autoRange = autoRange; 0325 0326 if (autoRange) { 0327 uiGeneralTab.lMin->setEnabled(false); 0328 uiGeneralTab.sbMin->setEnabled(false); 0329 uiGeneralTab.lMax->setEnabled(false); 0330 uiGeneralTab.sbMax->setEnabled(false); 0331 0332 const AbstractColumn* xDataColumn = nullptr; 0333 if (m_filterCurve->dataSourceType() == XYAnalysisCurve::DataSourceType::Spreadsheet) 0334 xDataColumn = m_filterCurve->xDataColumn(); 0335 else { 0336 if (m_filterCurve->dataSourceCurve()) 0337 xDataColumn = m_filterCurve->dataSourceCurve()->xColumn(); 0338 } 0339 0340 if (xDataColumn) { 0341 uiGeneralTab.sbMin->setValue(xDataColumn->minimum()); 0342 uiGeneralTab.sbMax->setValue(xDataColumn->maximum()); 0343 } 0344 } else { 0345 uiGeneralTab.lMin->setEnabled(true); 0346 uiGeneralTab.sbMin->setEnabled(true); 0347 uiGeneralTab.lMax->setEnabled(true); 0348 uiGeneralTab.sbMax->setEnabled(true); 0349 } 0350 0351 } 0352 void XYFourierFilterCurveDock::xRangeMinChanged() { 0353 double xMin = uiGeneralTab.sbMin->value(); 0354 0355 m_filterData.xRange.first() = xMin; 0356 uiGeneralTab.pbRecalculate->setEnabled(true); 0357 } 0358 0359 void XYFourierFilterCurveDock::xRangeMaxChanged() { 0360 double xMax = uiGeneralTab.sbMax->value(); 0361 0362 m_filterData.xRange.last() = xMax; 0363 uiGeneralTab.pbRecalculate->setEnabled(true); 0364 } 0365 0366 void XYFourierFilterCurveDock::typeChanged() { 0367 auto type = (nsl_filter_type)uiGeneralTab.cbType->currentIndex(); 0368 m_filterData.type = type; 0369 0370 switch (type) { 0371 case nsl_filter_type_low_pass: 0372 case nsl_filter_type_high_pass: 0373 uiGeneralTab.lCutoff->setText(i18n("Cutoff:")); 0374 uiGeneralTab.lCutoff2->setVisible(false); 0375 uiGeneralTab.sbCutoff2->setVisible(false); 0376 uiGeneralTab.cbUnit2->setVisible(false); 0377 break; 0378 case nsl_filter_type_band_pass: 0379 case nsl_filter_type_band_reject: 0380 uiGeneralTab.lCutoff2->setVisible(true); 0381 uiGeneralTab.lCutoff->setText(i18n("Lower cutoff:")); 0382 uiGeneralTab.lCutoff2->setText(i18n("Upper cutoff:")); 0383 uiGeneralTab.sbCutoff2->setVisible(true); 0384 uiGeneralTab.cbUnit2->setVisible(true); 0385 break; 0386 //TODO 0387 /* case nsl_filter_type_threshold: 0388 uiGeneralTab.lCutoff->setText(i18n("Value:")); 0389 uiGeneralTab.lCutoff2->setVisible(false); 0390 uiGeneralTab.sbCutoff2->setVisible(false); 0391 uiGeneralTab.cbUnit2->setVisible(false); 0392 */ 0393 } 0394 0395 enableRecalculate(); 0396 } 0397 0398 void XYFourierFilterCurveDock::formChanged() { 0399 auto form = (nsl_filter_form)uiGeneralTab.cbForm->currentIndex(); 0400 m_filterData.form = form; 0401 0402 switch (form) { 0403 case nsl_filter_form_ideal: 0404 uiGeneralTab.sbOrder->setVisible(false); 0405 uiGeneralTab.lOrder->setVisible(false); 0406 break; 0407 case nsl_filter_form_butterworth: 0408 case nsl_filter_form_chebyshev_i: 0409 case nsl_filter_form_chebyshev_ii: 0410 case nsl_filter_form_legendre: 0411 case nsl_filter_form_bessel: 0412 uiGeneralTab.sbOrder->setVisible(true); 0413 uiGeneralTab.lOrder->setVisible(true); 0414 break; 0415 } 0416 0417 enableRecalculate(); 0418 } 0419 0420 void XYFourierFilterCurveDock::orderChanged() { 0421 m_filterData.order = (unsigned int)uiGeneralTab.sbOrder->value(); 0422 0423 enableRecalculate(); 0424 } 0425 0426 void XYFourierFilterCurveDock::unitChanged() { 0427 auto unit = (nsl_filter_cutoff_unit)uiGeneralTab.cbUnit->currentIndex(); 0428 nsl_filter_cutoff_unit oldUnit = m_filterData.unit; 0429 double oldValue = uiGeneralTab.sbCutoff->value(); 0430 m_filterData.unit = unit; 0431 0432 int n = 100; 0433 double f = 1.0; // sample frequency 0434 const AbstractColumn* xDataColumn = nullptr; 0435 if (m_filterCurve->dataSourceType() == XYAnalysisCurve::DataSourceType::Spreadsheet) 0436 xDataColumn = m_filterCurve->xDataColumn(); 0437 else { 0438 if (m_filterCurve->dataSourceCurve()) 0439 xDataColumn = m_filterCurve->dataSourceCurve()->xColumn(); 0440 } 0441 0442 if (xDataColumn != nullptr) { 0443 n = xDataColumn->rowCount(); 0444 double range = xDataColumn->maximum() - xDataColumn->minimum(); 0445 f = (n-1)/range/2.; 0446 DEBUG(" n =" << n << " sample frequency =" << f); 0447 } 0448 0449 switch (unit) { 0450 case nsl_filter_cutoff_unit_frequency: 0451 uiGeneralTab.sbCutoff->setDecimals(6); 0452 uiGeneralTab.sbCutoff->setMaximum(f); 0453 uiGeneralTab.sbCutoff->setSingleStep(0.01*f); 0454 uiGeneralTab.sbCutoff->setSuffix(" Hz"); 0455 switch (oldUnit) { 0456 case nsl_filter_cutoff_unit_frequency: 0457 break; 0458 case nsl_filter_cutoff_unit_fraction: 0459 uiGeneralTab.sbCutoff->setValue(oldValue*f); 0460 break; 0461 case nsl_filter_cutoff_unit_index: 0462 uiGeneralTab.sbCutoff->setValue(oldValue*f/n); 0463 break; 0464 } 0465 break; 0466 case nsl_filter_cutoff_unit_fraction: 0467 uiGeneralTab.sbCutoff->setDecimals(6); 0468 uiGeneralTab.sbCutoff->setMaximum(1.0); 0469 uiGeneralTab.sbCutoff->setSingleStep(0.01); 0470 uiGeneralTab.sbCutoff->setSuffix(QString()); 0471 switch (oldUnit) { 0472 case nsl_filter_cutoff_unit_frequency: 0473 uiGeneralTab.sbCutoff->setValue(oldValue/f); 0474 break; 0475 case nsl_filter_cutoff_unit_fraction: 0476 break; 0477 case nsl_filter_cutoff_unit_index: 0478 uiGeneralTab.sbCutoff->setValue(oldValue/n); 0479 break; 0480 } 0481 break; 0482 case nsl_filter_cutoff_unit_index: 0483 uiGeneralTab.sbCutoff->setDecimals(0); 0484 uiGeneralTab.sbCutoff->setSingleStep(1); 0485 uiGeneralTab.sbCutoff->setMaximum(n); 0486 uiGeneralTab.sbCutoff->setSuffix(QString()); 0487 switch (oldUnit) { 0488 case nsl_filter_cutoff_unit_frequency: 0489 uiGeneralTab.sbCutoff->setValue(oldValue*n/f); 0490 break; 0491 case nsl_filter_cutoff_unit_fraction: 0492 uiGeneralTab.sbCutoff->setValue(oldValue*n); 0493 break; 0494 case nsl_filter_cutoff_unit_index: 0495 break; 0496 } 0497 break; 0498 } 0499 0500 enableRecalculate(); 0501 } 0502 0503 void XYFourierFilterCurveDock::unit2Changed() { 0504 auto unit = (nsl_filter_cutoff_unit)uiGeneralTab.cbUnit2->currentIndex(); 0505 nsl_filter_cutoff_unit oldUnit = m_filterData.unit2; 0506 double oldValue = uiGeneralTab.sbCutoff2->value(); 0507 m_filterData.unit2 = unit; 0508 0509 int n = 100; 0510 double f = 1.0; // sample frequency 0511 const AbstractColumn* xDataColumn = nullptr; 0512 if (m_filterCurve->dataSourceType() == XYAnalysisCurve::DataSourceType::Spreadsheet) 0513 xDataColumn = m_filterCurve->xDataColumn(); 0514 else { 0515 if (m_filterCurve->dataSourceCurve()) 0516 xDataColumn = m_filterCurve->dataSourceCurve()->xColumn(); 0517 } 0518 0519 if (xDataColumn != nullptr) { 0520 n = xDataColumn->rowCount(); 0521 double range = xDataColumn->maximum() - xDataColumn->minimum(); 0522 f = (n-1)/range/2.; 0523 DEBUG(" n =" << n << " sample frequency =" << f); 0524 } 0525 0526 switch (unit) { 0527 case nsl_filter_cutoff_unit_frequency: 0528 uiGeneralTab.sbCutoff2->setDecimals(6); 0529 uiGeneralTab.sbCutoff2->setMaximum(f); 0530 uiGeneralTab.sbCutoff2->setSingleStep(0.01*f); 0531 uiGeneralTab.sbCutoff2->setSuffix(" Hz"); 0532 switch (oldUnit) { 0533 case nsl_filter_cutoff_unit_frequency: 0534 break; 0535 case nsl_filter_cutoff_unit_fraction: 0536 uiGeneralTab.sbCutoff2->setValue(oldValue*f); 0537 break; 0538 case nsl_filter_cutoff_unit_index: 0539 uiGeneralTab.sbCutoff2->setValue(oldValue*f/n); 0540 break; 0541 } 0542 break; 0543 case nsl_filter_cutoff_unit_fraction: 0544 uiGeneralTab.sbCutoff2->setDecimals(6); 0545 uiGeneralTab.sbCutoff2->setMaximum(1.0); 0546 uiGeneralTab.sbCutoff2->setSingleStep(0.01); 0547 uiGeneralTab.sbCutoff2->setSuffix(QString()); 0548 switch (oldUnit) { 0549 case nsl_filter_cutoff_unit_frequency: 0550 uiGeneralTab.sbCutoff2->setValue(oldValue/f); 0551 break; 0552 case nsl_filter_cutoff_unit_fraction: 0553 break; 0554 case nsl_filter_cutoff_unit_index: 0555 uiGeneralTab.sbCutoff2->setValue(oldValue/n); 0556 break; 0557 } 0558 break; 0559 case nsl_filter_cutoff_unit_index: 0560 uiGeneralTab.sbCutoff2->setDecimals(0); 0561 uiGeneralTab.sbCutoff2->setSingleStep(1); 0562 uiGeneralTab.sbCutoff2->setMaximum(n); 0563 uiGeneralTab.sbCutoff2->setSuffix(QString()); 0564 switch (oldUnit) { 0565 case nsl_filter_cutoff_unit_frequency: 0566 uiGeneralTab.sbCutoff2->setValue(oldValue*n/f); 0567 break; 0568 case nsl_filter_cutoff_unit_fraction: 0569 uiGeneralTab.sbCutoff2->setValue(oldValue*n); 0570 break; 0571 case nsl_filter_cutoff_unit_index: 0572 break; 0573 } 0574 break; 0575 } 0576 0577 enableRecalculate(); 0578 } 0579 0580 void XYFourierFilterCurveDock::recalculateClicked() { 0581 m_filterData.cutoff = uiGeneralTab.sbCutoff->value(); 0582 m_filterData.cutoff2 = uiGeneralTab.sbCutoff2->value(); 0583 0584 if ((m_filterData.type == nsl_filter_type_band_pass || m_filterData.type == nsl_filter_type_band_reject) 0585 && m_filterData.cutoff2 <= m_filterData.cutoff) { 0586 KMessageBox::sorry(this, i18n("The band width is <= 0 since lower cutoff value is not smaller than upper cutoff value. Please fix this."), 0587 i18n("band width <= 0") ); 0588 return; 0589 } 0590 0591 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); 0592 for (auto* curve : m_curvesList) 0593 dynamic_cast<XYFourierFilterCurve*>(curve)->setFilterData(m_filterData); 0594 0595 uiGeneralTab.pbRecalculate->setEnabled(false); 0596 emit info(i18n("Fourier-Filter status: %1", m_filterCurve->filterResult().status)); 0597 QApplication::restoreOverrideCursor(); 0598 } 0599 0600 void XYFourierFilterCurveDock::enableRecalculate() const { 0601 if (m_initializing) 0602 return; 0603 0604 //no filtering possible without the x- and y-data 0605 bool hasSourceData = false; 0606 if (m_filterCurve->dataSourceType() == XYAnalysisCurve::DataSourceType::Spreadsheet) { 0607 AbstractAspect* aspectX = static_cast<AbstractAspect*>(cbXDataColumn->currentModelIndex().internalPointer()); 0608 AbstractAspect* aspectY = static_cast<AbstractAspect*>(cbYDataColumn->currentModelIndex().internalPointer()); 0609 hasSourceData = (aspectX != nullptr && aspectY != nullptr); 0610 if (aspectX) { 0611 cbXDataColumn->useCurrentIndexText(true); 0612 cbXDataColumn->setInvalid(false); 0613 } 0614 if (aspectY) { 0615 cbYDataColumn->useCurrentIndexText(true); 0616 cbYDataColumn->setInvalid(false); 0617 } 0618 } else { 0619 hasSourceData = (m_filterCurve->dataSourceCurve() != nullptr); 0620 } 0621 0622 uiGeneralTab.pbRecalculate->setEnabled(hasSourceData); 0623 } 0624 0625 /*! 0626 * show the result and details of the filter 0627 */ 0628 void XYFourierFilterCurveDock::showFilterResult() { 0629 const XYFourierFilterCurve::FilterResult& filterResult = m_filterCurve->filterResult(); 0630 if (!filterResult.available) { 0631 uiGeneralTab.teResult->clear(); 0632 return; 0633 } 0634 0635 QString str = i18n("status: %1", filterResult.status) + "<br>"; 0636 0637 if (!filterResult.valid) { 0638 uiGeneralTab.teResult->setText(str); 0639 return; //result is not valid, there was an error which is shown in the status-string, nothing to show more. 0640 } 0641 0642 SET_NUMBER_LOCALE 0643 if (filterResult.elapsedTime > 1000) 0644 str += i18n("calculation time: %1 s", numberLocale.toString(filterResult.elapsedTime/1000)) + "<br>"; 0645 else 0646 str += i18n("calculation time: %1 ms", numberLocale.toString(filterResult.elapsedTime)) + "<br>"; 0647 0648 str += "<br><br>"; 0649 0650 uiGeneralTab.teResult->setText(str); 0651 0652 //enable the "recalculate"-button if the source data was changed since the last filter 0653 uiGeneralTab.pbRecalculate->setEnabled(m_filterCurve->isSourceDataChangedSinceLastRecalc()); 0654 } 0655 0656 //************************************************************* 0657 //*********** SLOTs for changes triggered in XYCurve ********** 0658 //************************************************************* 0659 //General-Tab 0660 void XYFourierFilterCurveDock::curveDescriptionChanged(const AbstractAspect* aspect) { 0661 if (m_curve != aspect) 0662 return; 0663 0664 m_initializing = true; 0665 if (aspect->name() != uiGeneralTab.leName->text()) 0666 uiGeneralTab.leName->setText(aspect->name()); 0667 else if (aspect->comment() != uiGeneralTab.leComment->text()) 0668 uiGeneralTab.leComment->setText(aspect->comment()); 0669 m_initializing = false; 0670 } 0671 0672 void XYFourierFilterCurveDock::curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType type) { 0673 m_initializing = true; 0674 uiGeneralTab.cbDataSourceType->setCurrentIndex(static_cast<int>(type)); 0675 m_initializing = false; 0676 } 0677 0678 void XYFourierFilterCurveDock::curveDataSourceCurveChanged(const XYCurve* curve) { 0679 m_initializing = true; 0680 XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, curve); 0681 m_initializing = false; 0682 } 0683 0684 void XYFourierFilterCurveDock::curveXDataColumnChanged(const AbstractColumn* column) { 0685 m_initializing = true; 0686 XYCurveDock::setModelIndexFromAspect(cbXDataColumn, column); 0687 m_initializing = false; 0688 } 0689 0690 void XYFourierFilterCurveDock::curveYDataColumnChanged(const AbstractColumn* column) { 0691 m_initializing = true; 0692 XYCurveDock::setModelIndexFromAspect(cbYDataColumn, column); 0693 m_initializing = false; 0694 } 0695 0696 void XYFourierFilterCurveDock::curveFilterDataChanged(const XYFourierFilterCurve::FilterData& filterData) { 0697 m_initializing = true; 0698 m_filterData = filterData; 0699 uiGeneralTab.cbType->setCurrentIndex(m_filterData.type); 0700 this->typeChanged(); 0701 0702 this->showFilterResult(); 0703 m_initializing = false; 0704 } 0705 0706 void XYFourierFilterCurveDock::dataChanged() { 0707 this->enableRecalculate(); 0708 } 0709 0710 void XYFourierFilterCurveDock::curveVisibilityChanged(bool on) { 0711 m_initializing = true; 0712 uiGeneralTab.chkVisible->setChecked(on); 0713 m_initializing = false; 0714 }