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 }