File indexing completed on 2025-01-05 03:35:30
0001 /* 0002 File : KDEPlotDock.cpp 0003 Project : LabPlot 0004 Description : widget for KDE-plot properties 0005 -------------------------------------------------------------------- 0006 SPDX-FileCopyrightText: 2023 Alexander Semke <alexander.semke@web.de> 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "KDEPlotDock.h" 0011 #include "backend/core/AspectTreeModel.h" 0012 #include "backend/core/Project.h" 0013 #include "backend/core/column/Column.h" 0014 #include "backend/worksheet/plots/cartesian/KDEPlot.h" 0015 #include "commonfrontend/widgets/TreeViewComboBox.h" 0016 #include "kdefrontend/GuiTools.h" 0017 #include "kdefrontend/TemplateHandler.h" 0018 #include "kdefrontend/widgets/BackgroundWidget.h" 0019 #include "kdefrontend/widgets/LineWidget.h" 0020 0021 #include <KConfig> 0022 #include <KLocalizedString> 0023 0024 /*! 0025 \class KDEPlotDock 0026 \brief Provides a widget for editing the properties of KDE-plots. 0027 0028 \ingroup kdefrontend 0029 */ 0030 KDEPlotDock::KDEPlotDock(QWidget* parent) 0031 : BaseDock(parent) 0032 , cbDataColumn(new TreeViewComboBox) { 0033 ui.setupUi(this); 0034 setPlotRangeCombobox(ui.cbPlotRanges); 0035 setBaseWidgets(ui.leName, ui.teComment); 0036 setVisibilityWidgets(ui.chkVisible, ui.chkLegendVisible); 0037 0038 // Tab "General" 0039 auto* gridLayout = qobject_cast<QGridLayout*>(ui.tabGeneral->layout()); 0040 gridLayout->addWidget(cbDataColumn, 3, 2, 1, 1); 0041 0042 // Tab "Estimation" 0043 auto* hBoxLayout = static_cast<QHBoxLayout*>(ui.tabKDE->layout()); 0044 estimationLineWidget = new LineWidget(ui.tabKDE); 0045 hBoxLayout->insertWidget(1, estimationLineWidget); 0046 0047 estimationBackgroundWidget = new BackgroundWidget(ui.tabKDE); 0048 hBoxLayout->insertWidget(5, estimationBackgroundWidget); 0049 0050 // adjust layouts in the tabs 0051 for (int i = 0; i < ui.tabWidget->count(); ++i) { 0052 auto* layout = dynamic_cast<QGridLayout*>(ui.tabWidget->widget(i)->layout()); 0053 if (!layout) 0054 continue; 0055 0056 layout->setContentsMargins(2, 2, 2, 2); 0057 layout->setHorizontalSpacing(2); 0058 layout->setVerticalSpacing(2); 0059 } 0060 0061 // Slots 0062 // General 0063 connect(cbDataColumn, &TreeViewComboBox::currentModelIndexChanged, this, &KDEPlotDock::dataColumnChanged); 0064 connect(ui.cbKernelType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &KDEPlotDock::kernelTypeChanged); 0065 connect(ui.cbBandwidthType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &KDEPlotDock::bandwidthTypeChanged); 0066 connect(ui.sbBandwidth, QOverload<double>::of(&NumberSpinBox::valueChanged), this, &KDEPlotDock::bandwidthChanged); 0067 0068 // Margin Plots 0069 connect(ui.chkRugEnabled, &QCheckBox::toggled, this, &KDEPlotDock::rugEnabledChanged); 0070 connect(ui.sbRugLength, QOverload<double>::of(&NumberSpinBox::valueChanged), this, &KDEPlotDock::rugLengthChanged); 0071 connect(ui.sbRugWidth, QOverload<double>::of(&NumberSpinBox::valueChanged), this, &KDEPlotDock::rugWidthChanged); 0072 connect(ui.sbRugOffset, QOverload<double>::of(&NumberSpinBox::valueChanged), this, &KDEPlotDock::rugOffsetChanged); 0073 0074 // template handler 0075 auto* frame = new QFrame(this); 0076 auto* layout = new QHBoxLayout(frame); 0077 layout->setContentsMargins(0, 11, 0, 11); 0078 0079 auto* templateHandler = new TemplateHandler(this, QLatin1String("KDEPlot")); 0080 layout->addWidget(templateHandler); 0081 connect(templateHandler, &TemplateHandler::loadConfigRequested, this, &KDEPlotDock::loadConfigFromTemplate); 0082 connect(templateHandler, &TemplateHandler::saveConfigRequested, this, &KDEPlotDock::saveConfigAsTemplate); 0083 connect(templateHandler, &TemplateHandler::info, this, &KDEPlotDock::info); 0084 0085 ui.verticalLayout->addWidget(frame); 0086 0087 updateLocale(); 0088 retranslateUi(); 0089 } 0090 0091 KDEPlotDock::~KDEPlotDock() { 0092 if (m_aspectTreeModel) 0093 delete m_aspectTreeModel; 0094 } 0095 0096 void KDEPlotDock::setModel() { 0097 m_aspectTreeModel->enablePlottableColumnsOnly(true); 0098 m_aspectTreeModel->enableShowPlotDesignation(true); 0099 0100 QList<AspectType> list{AspectType::Folder, 0101 AspectType::Workbook, 0102 AspectType::Datapicker, 0103 AspectType::DatapickerCurve, 0104 AspectType::Spreadsheet, 0105 AspectType::LiveDataSource, 0106 AspectType::Column, 0107 AspectType::Worksheet, 0108 AspectType::CartesianPlot, 0109 AspectType::XYFitCurve, 0110 AspectType::XYSmoothCurve, 0111 AspectType::CantorWorksheet}; 0112 0113 cbDataColumn->setTopLevelClasses(list); 0114 0115 list = {AspectType::Column}; 0116 m_aspectTreeModel->setSelectableAspects(list); 0117 0118 cbDataColumn->setModel(m_aspectTreeModel); 0119 } 0120 0121 void KDEPlotDock::setPlots(QList<KDEPlot*> list) { 0122 Lock lock(m_initializing); 0123 m_plots = list; 0124 m_plot = list.first(); 0125 setAspects(list); 0126 Q_ASSERT(m_plot); 0127 m_aspectTreeModel = new AspectTreeModel(m_plot->project()); 0128 setModel(); 0129 0130 // initialize widgets for common properties 0131 QList<Line*> estimationLines; 0132 QList<Background*> estimationBackgrounds; 0133 QList<Line*> histogramLines; 0134 QList<Background*> histogramBackgrounds; 0135 for (auto* plot : m_plots) { 0136 estimationLines << plot->estimationCurve()->line(); 0137 estimationBackgrounds << plot->estimationCurve()->background(); 0138 } 0139 estimationLineWidget->setLines(estimationLines); 0140 estimationBackgroundWidget->setBackgrounds(estimationBackgrounds); 0141 0142 // if there are more then one curve in the list, disable the content in the tab "general" 0143 if (m_plots.size() == 1) { 0144 cbDataColumn->setEnabled(true); 0145 cbDataColumn->setColumn(m_plot->dataColumn(), m_plot->dataColumnPath()); 0146 ui.leName->setText(m_plot->name()); 0147 ui.teComment->setText(m_plot->comment()); 0148 } else { 0149 cbDataColumn->setEnabled(false); 0150 cbDataColumn->setCurrentModelIndex(QModelIndex()); 0151 } 0152 0153 ui.chkLegendVisible->setChecked(m_plot->legendVisible()); 0154 ui.chkVisible->setChecked(m_plot->isVisible()); 0155 0156 // load the remaining properties 0157 load(); 0158 0159 updatePlotRangeList(); 0160 0161 // Slots 0162 // General-tab 0163 connect(m_plot, &KDEPlot::dataColumnChanged, this, &KDEPlotDock::plotDataColumnChanged); 0164 connect(m_plot, &KDEPlot::kernelTypeChanged, this, &KDEPlotDock::plotKernelTypeChanged); 0165 connect(m_plot, &KDEPlot::bandwidthTypeChanged, this, &KDEPlotDock::plotBandwidthTypeChanged); 0166 connect(m_plot, &KDEPlot::bandwidthChanged, this, &KDEPlotDock::plotBandwidthChanged); 0167 0168 //"Margin Plots"-Tab 0169 auto* curve = m_plot->rugCurve(); 0170 connect(curve, &XYCurve::rugEnabledChanged, this, &KDEPlotDock::plotRugEnabledChanged); 0171 connect(curve, &XYCurve::rugLengthChanged, this, &KDEPlotDock::plotRugLengthChanged); 0172 connect(curve, &XYCurve::rugWidthChanged, this, &KDEPlotDock::plotRugWidthChanged); 0173 connect(curve, &XYCurve::rugOffsetChanged, this, &KDEPlotDock::plotRugOffsetChanged); 0174 } 0175 0176 void KDEPlotDock::retranslateUi() { 0177 // TODO unify with nsl_smooth_weight_type_name in nsl_smooth.c. 0178 ui.cbKernelType->clear(); 0179 ui.cbKernelType->addItem(i18n("Gauss"), static_cast<int>(nsl_kernel_gauss)); 0180 ui.cbKernelType->addItem(i18n("Uniform (Rectangular)"), static_cast<int>(nsl_kernel_uniform)); 0181 ui.cbKernelType->addItem(i18n("Triangular"), static_cast<int>(nsl_kernel_triangular)); 0182 ui.cbKernelType->addItem(i18n("Parabolic (Epanechnikov)"), static_cast<int>(nsl_kernel_parabolic)); 0183 ui.cbKernelType->addItem(i18n("Quartic (Biweight)"), static_cast<int>(nsl_kernel_quartic)); 0184 ui.cbKernelType->addItem(i18n("Triweight"), static_cast<int>(nsl_kernel_triweight)); 0185 ui.cbKernelType->addItem(i18n("Tricube"), static_cast<int>(nsl_kernel_tricube)); 0186 ui.cbKernelType->addItem(i18n("Cosine"), static_cast<int>(nsl_kernel_cosine)); 0187 0188 ui.cbBandwidthType->clear(); 0189 ui.cbBandwidthType->addItem(i18n("Silverman"), static_cast<int>(nsl_kde_bandwidth_silverman)); 0190 ui.cbBandwidthType->addItem(i18n("Scott"), static_cast<int>(nsl_kde_bandwidth_scott)); 0191 ui.cbBandwidthType->addItem(i18n("Custom"), static_cast<int>(nsl_kde_bandwidth_custom)); 0192 } 0193 0194 /* 0195 * updates the locale in the widgets. called when the application settins are changed. 0196 */ 0197 void KDEPlotDock::updateLocale() { 0198 estimationLineWidget->updateLocale(); 0199 } 0200 0201 //************************************************************* 0202 //********* SLOTs for changes triggered in KDEPlotDock ******** 0203 //************************************************************* 0204 0205 // "General"-tab 0206 void KDEPlotDock::dataColumnChanged(const QModelIndex& index) { 0207 if (m_initializing) 0208 return; 0209 0210 auto aspect = static_cast<AbstractAspect*>(index.internalPointer()); 0211 AbstractColumn* column(nullptr); 0212 if (aspect) { 0213 column = dynamic_cast<AbstractColumn*>(aspect); 0214 Q_ASSERT(column); 0215 } 0216 0217 for (auto* plot : m_plots) 0218 plot->setDataColumn(column); 0219 } 0220 0221 void KDEPlotDock::kernelTypeChanged(int index) { 0222 const auto type = static_cast<nsl_kernel_type>(ui.cbKernelType->itemData(index).toInt()); 0223 0224 CONDITIONAL_LOCK_RETURN; 0225 0226 for (auto* plot : m_plots) 0227 plot->setKernelType(type); 0228 } 0229 0230 void KDEPlotDock::bandwidthTypeChanged(int index) { 0231 const auto type = static_cast<nsl_kde_bandwidth_type>(ui.cbBandwidthType->itemData(index).toInt()); 0232 0233 bool custom = (type == nsl_kde_bandwidth_custom); 0234 ui.lBandwidth->setVisible(custom); 0235 ui.sbBandwidth->setVisible(custom); 0236 0237 CONDITIONAL_LOCK_RETURN; 0238 0239 for (auto* plot : m_plots) 0240 plot->setBandwidthType(type); 0241 } 0242 0243 void KDEPlotDock::bandwidthChanged(double value) { 0244 CONDITIONAL_LOCK_RETURN; 0245 0246 for (auto* plot : m_plots) 0247 plot->setBandwidth(value); 0248 } 0249 0250 //"Margin Plots"-Tab 0251 void KDEPlotDock::rugEnabledChanged(bool state) { 0252 CONDITIONAL_LOCK_RETURN; 0253 0254 for (auto* plot : m_plots) 0255 plot->rugCurve()->setRugEnabled(state); 0256 } 0257 0258 void KDEPlotDock::rugLengthChanged(double value) { 0259 CONDITIONAL_RETURN_NO_LOCK; 0260 0261 const double length = Worksheet::convertToSceneUnits(value, Worksheet::Unit::Point); 0262 for (auto* plot : m_plots) 0263 plot->rugCurve()->setRugLength(length); 0264 } 0265 0266 void KDEPlotDock::rugWidthChanged(double value) { 0267 CONDITIONAL_RETURN_NO_LOCK; 0268 0269 const double width = Worksheet::convertToSceneUnits(value, Worksheet::Unit::Point); 0270 for (auto* plot : m_plots) 0271 plot->rugCurve()->setRugWidth(width); 0272 } 0273 0274 void KDEPlotDock::rugOffsetChanged(double value) { 0275 CONDITIONAL_RETURN_NO_LOCK; 0276 0277 const double offset = Worksheet::convertToSceneUnits(value, Worksheet::Unit::Point); 0278 for (auto* plot : m_plots) 0279 plot->rugCurve()->setRugOffset(offset); 0280 } 0281 0282 //************************************************************* 0283 //*********** SLOTs for changes triggered in KDEPlot ********** 0284 //************************************************************* 0285 // General-Tab 0286 void KDEPlotDock::plotDataColumnChanged(const AbstractColumn* column) { 0287 CONDITIONAL_LOCK_RETURN; 0288 cbDataColumn->setColumn(column, m_plot->dataColumnPath()); 0289 } 0290 0291 void KDEPlotDock::plotKernelTypeChanged(nsl_kernel_type type) { 0292 CONDITIONAL_LOCK_RETURN; 0293 int index = ui.cbKernelType->findData(static_cast<int>(type)); 0294 ui.cbKernelType->setCurrentIndex(index); 0295 } 0296 0297 void KDEPlotDock::plotBandwidthTypeChanged(nsl_kde_bandwidth_type type) { 0298 CONDITIONAL_LOCK_RETURN; 0299 int index = ui.cbBandwidthType->findData(static_cast<int>(type)); 0300 ui.cbBandwidthType->setCurrentIndex(index); 0301 } 0302 0303 void KDEPlotDock::plotBandwidthChanged(double value) { 0304 CONDITIONAL_LOCK_RETURN; 0305 ui.sbBandwidth->setValue(value); 0306 } 0307 0308 //"Margin Plot"-Tab 0309 void KDEPlotDock::plotRugEnabledChanged(bool status) { 0310 CONDITIONAL_LOCK_RETURN; 0311 ui.chkRugEnabled->setChecked(status); 0312 } 0313 void KDEPlotDock::plotRugLengthChanged(double value) { 0314 CONDITIONAL_LOCK_RETURN; 0315 ui.sbRugLength->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Unit::Point)); 0316 } 0317 void KDEPlotDock::plotRugWidthChanged(double value) { 0318 CONDITIONAL_LOCK_RETURN; 0319 ui.sbRugWidth->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Unit::Point)); 0320 } 0321 void KDEPlotDock::plotRugOffsetChanged(double value) { 0322 CONDITIONAL_LOCK_RETURN; 0323 ui.sbRugOffset->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Unit::Point)); 0324 } 0325 0326 //************************************************************* 0327 //************************* Settings ************************** 0328 //************************************************************* 0329 void KDEPlotDock::load() { 0330 // general 0331 cbDataColumn->setColumn(m_plot->dataColumn(), m_plot->dataColumnPath()); 0332 0333 int index = ui.cbKernelType->findData(static_cast<int>(m_plot->kernelType())); 0334 ui.cbKernelType->setCurrentIndex(index); 0335 0336 index = ui.cbBandwidthType->findData(static_cast<int>(m_plot->bandwidthType())); 0337 ui.cbBandwidthType->setCurrentIndex(index); 0338 0339 ui.sbBandwidth->setValue(m_plot->bandwidth()); 0340 0341 // Margin plots 0342 const auto* curve = m_plot->rugCurve(); 0343 ui.chkRugEnabled->setChecked(curve->rugEnabled()); 0344 ui.sbRugWidth->setValue(Worksheet::convertFromSceneUnits(curve->rugWidth(), Worksheet::Unit::Point)); 0345 ui.sbRugLength->setValue(Worksheet::convertFromSceneUnits(curve->rugLength(), Worksheet::Unit::Point)); 0346 ui.sbRugOffset->setValue(Worksheet::convertFromSceneUnits(curve->rugOffset(), Worksheet::Unit::Point)); 0347 } 0348 0349 void KDEPlotDock::loadConfig(KConfig& config) { 0350 KConfigGroup group = config.group(QStringLiteral("KDEPlot")); 0351 0352 // general 0353 auto kernelType = group.readEntry(QStringLiteral("kernelType"), static_cast<int>(m_plot->kernelType())); 0354 int index = ui.cbKernelType->findData(kernelType); 0355 ui.cbKernelType->setCurrentIndex(index); 0356 0357 auto bandwidthType = group.readEntry(QStringLiteral("bandwidthType"), static_cast<int>(m_plot->bandwidthType())); 0358 index = ui.cbBandwidthType->findData(bandwidthType); 0359 ui.cbBandwidthType->setCurrentIndex(index); 0360 0361 ui.sbBandwidth->setValue(group.readEntry(QStringLiteral("bandwidth"), m_plot->bandwidth())); 0362 0363 // properties of the estimation and margin curves 0364 // lineWidget->loadConfig(group); 0365 } 0366 0367 void KDEPlotDock::loadConfigFromTemplate(KConfig& config) { 0368 auto name = TemplateHandler::templateName(config); 0369 int size = m_plots.size(); 0370 if (size > 1) 0371 m_plot->beginMacro(i18n("%1 xy-curves: template \"%2\" loaded", size, name)); 0372 else 0373 m_plot->beginMacro(i18n("%1: template \"%2\" loaded", m_plot->name(), name)); 0374 0375 this->loadConfig(config); 0376 0377 m_plot->endMacro(); 0378 } 0379 0380 void KDEPlotDock::saveConfigAsTemplate(KConfig& config) { 0381 KConfigGroup group = config.group(QStringLiteral("KDEPlot")); 0382 0383 // General 0384 group.writeEntry(QStringLiteral("kernelType"), static_cast<int>(m_plot->kernelType())); 0385 group.writeEntry(QStringLiteral("bandwidthType"), static_cast<int>(m_plot->bandwidthType())); 0386 group.writeEntry(QStringLiteral("bandwidth"), m_plot->bandwidth()); 0387 0388 // properties of the estimation and rug curves 0389 // lineWidget->saveConfig(group); 0390 0391 config.sync(); 0392 }