File indexing completed on 2025-01-05 03:35:41
0001 /* 0002 File : PlotDataDialog.cpp 0003 Project : LabPlot 0004 Description : Dialog for generating plots for the spreadsheet data 0005 -------------------------------------------------------------------- 0006 SPDX-FileCopyrightText: 2017-2023 Alexander Semke <alexander.semke@web.de> 0007 SPDX-FileCopyrightText: 2022 Stefan Gerlach <stefan.gerlach@uni.kn> 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 0011 #include "PlotDataDialog.h" 0012 #include "backend/core/AspectTreeModel.h" 0013 #include "backend/core/Project.h" 0014 #include "backend/core/Settings.h" 0015 #include "backend/core/column/Column.h" 0016 #include "backend/lib/Range.h" 0017 #include "backend/spreadsheet/Spreadsheet.h" 0018 #include "backend/worksheet/TextLabel.h" 0019 #include "backend/worksheet/Worksheet.h" 0020 #include "backend/worksheet/plots/cartesian/Axis.h" 0021 #include "backend/worksheet/plots/cartesian/BarPlot.h" 0022 #include "backend/worksheet/plots/cartesian/BoxPlot.h" 0023 #include "backend/worksheet/plots/cartesian/CartesianPlot.h" 0024 #include "backend/worksheet/plots/cartesian/Histogram.h" 0025 #include "backend/worksheet/plots/cartesian/KDEPlot.h" 0026 #include "backend/worksheet/plots/cartesian/LollipopPlot.h" 0027 #include "backend/worksheet/plots/cartesian/QQPlot.h" 0028 #include "backend/worksheet/plots/cartesian/XYAnalysisCurve.h" 0029 #include "backend/worksheet/plots/cartesian/XYCurve.h" 0030 #include "backend/worksheet/plots/cartesian/XYDataReductionCurve.h" 0031 #include "backend/worksheet/plots/cartesian/XYDifferentiationCurve.h" 0032 #include "backend/worksheet/plots/cartesian/XYFitCurve.h" 0033 #include "backend/worksheet/plots/cartesian/XYFourierFilterCurve.h" 0034 #include "backend/worksheet/plots/cartesian/XYIntegrationCurve.h" 0035 #include "backend/worksheet/plots/cartesian/XYInterpolationCurve.h" 0036 #include "backend/worksheet/plots/cartesian/XYSmoothCurve.h" 0037 #include "commonfrontend/widgets/TreeViewComboBox.h" 0038 0039 #ifdef HAVE_MQTT 0040 #include "backend/datasources/MQTTTopic.h" 0041 #endif 0042 0043 #include <QAction> 0044 #include <QActionGroup> 0045 #include <QDialogButtonBox> 0046 #include <QMenu> 0047 #include <QPushButton> 0048 #include <QWindow> 0049 0050 #include <KConfigGroup> 0051 0052 #include <KWindowConfig> 0053 0054 #include "ui_plotdatawidget.h" 0055 0056 /*! 0057 \class PlotDataDialog 0058 \brief Dialog for generating plots for the spreadsheet data. 0059 0060 \ingroup kdefrontend 0061 */ 0062 PlotDataDialog::PlotDataDialog(AbstractAspect* parentAspect, PlotType type, QWidget* parent) 0063 : QDialog(parent) 0064 , ui(new Ui::PlotDataWidget()) 0065 , m_parentAspect(parentAspect) 0066 , m_plotsModel(new AspectTreeModel(m_parentAspect->project())) 0067 , m_worksheetsModel(new AspectTreeModel(m_parentAspect->project())) 0068 , m_plotType(type) { 0069 setAttribute(Qt::WA_DeleteOnClose); 0070 setWindowTitle(i18nc("@title:window", "Plot Spreadsheet Data")); 0071 setWindowIcon(QIcon::fromTheme(QStringLiteral("office-chart-line"))); 0072 0073 auto* mainWidget = new QWidget(this); 0074 ui->setupUi(mainWidget); 0075 0076 auto* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); 0077 m_okButton = buttonBox->button(QDialogButtonBox::Ok); 0078 m_okButton->setDefault(true); 0079 m_okButton->setToolTip(i18n("Plot the selected data")); 0080 m_okButton->setText(i18n("&Plot")); 0081 0082 auto* layout = new QVBoxLayout(this); 0083 layout->addWidget(mainWidget); 0084 layout->addWidget(buttonBox); 0085 setLayout(layout); 0086 0087 // create combox boxes for the existing plots and worksheets 0088 auto* gridLayout = dynamic_cast<QGridLayout*>(ui->gbPlotPlacement->layout()); 0089 cbExistingPlots = new TreeViewComboBox(ui->gbPlotPlacement); 0090 cbExistingPlots->setMinimumWidth(250); // TODO: use proper sizeHint in TreeViewComboBox 0091 gridLayout->addWidget(cbExistingPlots, 0, 1, 1, 1); 0092 0093 cbExistingWorksheets = new TreeViewComboBox(ui->gbPlotPlacement); 0094 cbExistingWorksheets->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred)); 0095 gridLayout->addWidget(cbExistingWorksheets, 1, 1, 1, 1); 0096 0097 QList<AspectType> list{AspectType::Folder, AspectType::Worksheet, AspectType::CartesianPlot}; 0098 cbExistingPlots->setTopLevelClasses(list); 0099 list = {AspectType::CartesianPlot}; 0100 m_plotsModel->setSelectableAspects(list); 0101 cbExistingPlots->setModel(m_plotsModel); 0102 0103 // select the first available plot, if available 0104 auto plots = m_parentAspect->project()->children<CartesianPlot>(AbstractAspect::ChildIndexFlag::Recursive); 0105 if (!plots.isEmpty()) { 0106 const auto* plot = plots.first(); 0107 cbExistingPlots->setCurrentModelIndex(m_plotsModel->modelIndexOfAspect(plot)); 0108 } 0109 0110 list = {AspectType::Folder, AspectType::Worksheet}; 0111 cbExistingWorksheets->setTopLevelClasses(list); 0112 list = {AspectType::Worksheet}; 0113 m_worksheetsModel->setSelectableAspects(list); 0114 cbExistingWorksheets->setModel(m_worksheetsModel); 0115 0116 // select the first available worksheet, if available 0117 auto worksheets = m_parentAspect->project()->children<Worksheet>(AbstractAspect::ChildIndexFlag::Recursive); 0118 if (!worksheets.isEmpty()) { 0119 const auto* worksheet = worksheets.first(); 0120 cbExistingWorksheets->setCurrentModelIndex(m_worksheetsModel->modelIndexOfAspect(worksheet)); 0121 } 0122 0123 // in the grid layout of the scroll area we have on default one row for the x-column, 0124 // one row for the separating line and one line for the y-column. 0125 // set the height of this default content as the minimal size of the scroll area. 0126 gridLayout = dynamic_cast<QGridLayout*>(ui->scrollAreaColumns->widget()->layout()); 0127 int height = 2 * ui->cbXColumn->height() + ui->line->height() + 2 * gridLayout->verticalSpacing() + gridLayout->contentsMargins().top() 0128 + gridLayout->contentsMargins().bottom(); 0129 ui->scrollAreaColumns->setMinimumSize(0, height); 0130 0131 // hide the check box for creation of original data, only shown if analysis curves are to be created 0132 ui->spacer->changeSize(0, 0); 0133 ui->chkCreateDataCurve->hide(); 0134 0135 // SIGNALs/SLOTs 0136 connect(buttonBox, &QDialogButtonBox::accepted, this, [=]() { 0137 hide(); 0138 plot(); 0139 }); 0140 connect(buttonBox, &QDialogButtonBox::rejected, this, &PlotDataDialog::reject); 0141 connect(buttonBox, &QDialogButtonBox::accepted, this, &PlotDataDialog::accept); 0142 connect(ui->rbCurvePlacementAllInOnePlotArea, &QRadioButton::toggled, this, &PlotDataDialog::curvePlacementChanged); 0143 connect(ui->rbCurvePlacementAllInOwnPlotArea, &QRadioButton::toggled, this, &PlotDataDialog::curvePlacementChanged); 0144 connect(ui->rbPlotPlacementExistingPlotArea, &QRadioButton::toggled, this, &PlotDataDialog::plotPlacementChanged); 0145 connect(ui->rbPlotPlacementExistingWorksheet, &QRadioButton::toggled, this, &PlotDataDialog::plotPlacementChanged); 0146 connect(ui->rbPlotPlacementNewWorksheet, &QRadioButton::toggled, this, &PlotDataDialog::plotPlacementChanged); 0147 connect(cbExistingPlots, &TreeViewComboBox::currentModelIndexChanged, this, &PlotDataDialog::checkOkButton); 0148 connect(cbExistingWorksheets, &TreeViewComboBox::currentModelIndexChanged, this, &PlotDataDialog::checkOkButton); 0149 0150 // restore saved settings if available 0151 create(); // ensure there's a window created 0152 KConfigGroup conf = Settings::group(QStringLiteral("PlotDataDialog")); 0153 if (conf.exists()) { 0154 int index = conf.readEntry("CurvePlacement", 0); 0155 if (index == 2) 0156 ui->rbCurvePlacementAllInOwnPlotArea->setChecked(true); 0157 0158 index = conf.readEntry("PlotPlacement", 0); 0159 if (index == 2) 0160 ui->rbPlotPlacementExistingWorksheet->setChecked(true); 0161 if (index == 3) 0162 ui->rbPlotPlacementNewWorksheet->setChecked(true); 0163 0164 KWindowConfig::restoreWindowSize(windowHandle(), conf); 0165 resize(windowHandle()->size()); // workaround for QTBUG-40584 0166 } else 0167 resize(QSize(0, 0).expandedTo(minimumSize())); 0168 } 0169 0170 PlotDataDialog::~PlotDataDialog() { 0171 // save current settings 0172 KConfigGroup conf = Settings::group(QStringLiteral("PlotDataDialog")); 0173 int index = 0; 0174 if (ui->rbCurvePlacementAllInOnePlotArea->isChecked()) 0175 index = 1; 0176 if (ui->rbCurvePlacementAllInOwnPlotArea->isChecked()) 0177 index = 2; 0178 conf.writeEntry("CurvePlacement", index); 0179 0180 if (ui->rbPlotPlacementExistingPlotArea->isChecked()) 0181 index = 1; 0182 if (ui->rbPlotPlacementExistingWorksheet->isChecked()) 0183 index = 2; 0184 if (ui->rbPlotPlacementNewWorksheet->isChecked()) 0185 index = 3; 0186 conf.writeEntry("PlotPlacement", index); 0187 0188 KWindowConfig::saveWindowSize(windowHandle(), conf); 0189 0190 delete m_plotsModel; 0191 delete m_worksheetsModel; 0192 delete ui; 0193 } 0194 0195 void PlotDataDialog::setAnalysisAction(XYAnalysisCurve::AnalysisAction action) { 0196 m_analysisAction = action; 0197 m_analysisMode = true; 0198 ui->spacer->changeSize(0, 40); 0199 ui->chkCreateDataCurve->show(); 0200 } 0201 0202 void PlotDataDialog::fillMenu(QMenu* menu, QActionGroup* group) { 0203 // synchronize with the menu in CartesianPlot 0204 0205 auto* action = new QAction(QIcon::fromTheme(QStringLiteral("labplot-xy-curve")), i18n("xy-curve"), group); 0206 action->setData(static_cast<int>(PlotDataDialog::PlotType::XYCurve)); 0207 menu->addAction(action); 0208 0209 auto* addNewStatisticalPlotsMenu = new QMenu(i18n("Statistical Plots"), menu); 0210 action = new QAction(QIcon::fromTheme(QStringLiteral("view-object-histogram-linear")), i18n("Histogram"), group); 0211 action->setData(static_cast<int>(PlotDataDialog::PlotType::Histogram)); 0212 addNewStatisticalPlotsMenu->addAction(action); 0213 0214 action = new QAction(BoxPlot::staticIcon(), i18n("Box Plot"), group); 0215 action->setData(static_cast<int>(PlotDataDialog::PlotType::BoxPlot)); 0216 addNewStatisticalPlotsMenu->addAction(action); 0217 0218 action = new QAction(i18n("KDE Plot"), group); 0219 action->setData(static_cast<int>(PlotDataDialog::PlotType::KDEPlot)); 0220 addNewStatisticalPlotsMenu->addAction(action); 0221 0222 action = new QAction(i18n("Q-Q Plot"), group); 0223 action->setData(static_cast<int>(PlotDataDialog::PlotType::QQPlot)); 0224 addNewStatisticalPlotsMenu->addAction(action); 0225 menu->addMenu(addNewStatisticalPlotsMenu); 0226 0227 auto* addNewBarPlotsMenu = new QMenu(i18n("Bar Plots"), menu); 0228 action = new QAction(QIcon::fromTheme(QStringLiteral("office-chart-bar")), i18n("Bar Plot"), group); 0229 action->setData(static_cast<int>(PlotDataDialog::PlotType::BarPlot)); 0230 addNewBarPlotsMenu->addAction(action); 0231 0232 action = new QAction(LollipopPlot::staticIcon(), i18n("Lollipop Plot"), group); 0233 action->setData(static_cast<int>(PlotDataDialog::PlotType::LollipopPlot)); 0234 addNewBarPlotsMenu->addAction(action); 0235 menu->addMenu(addNewBarPlotsMenu); 0236 } 0237 0238 void PlotDataDialog::setSelectedColumns(QVector<Column*> selectedColumns) { 0239 // skip error and non-plottable columns 0240 for (Column* col : selectedColumns) { 0241 if ((col->plotDesignation() == AbstractColumn::PlotDesignation::X || col->plotDesignation() == AbstractColumn::PlotDesignation::Y 0242 || col->plotDesignation() == AbstractColumn::PlotDesignation::NoDesignation) 0243 && col->isPlottable()) 0244 m_columns << col; 0245 } 0246 0247 // disable everything if the spreadsheet doesn't have any columns to plot 0248 if (m_columns.isEmpty()) { 0249 ui->gbCurvePlacement->setEnabled(false); 0250 ui->gbPlotPlacement->setEnabled(false); 0251 return; 0252 } 0253 0254 // determine the column names 0255 // and the name of the first column having "X" as the plot designation (relevant for xy-curves only) 0256 QStringList columnNames; 0257 QString xColumnName; 0258 for (const Column* column : m_columns) { 0259 columnNames << column->name(); 0260 if (m_plotType == PlotType::XYCurve && xColumnName.isEmpty() && column->plotDesignation() == AbstractColumn::PlotDesignation::X) 0261 xColumnName = column->name(); 0262 } 0263 0264 if (m_plotType == PlotType::XYCurve && xColumnName.isEmpty()) { 0265 // no X-column was selected -> look for the first non-selected X-column left to the first selected column 0266 const auto& columns = m_parentAspect->children<Column>(); 0267 const int index = columns.indexOf(selectedColumns.first()) - 1; 0268 for (int i = index; i >= 0; --i) { 0269 auto* column = columns.at(i); 0270 if (column->plotDesignation() == AbstractColumn::PlotDesignation::X && column->isPlottable()) { 0271 xColumnName = column->name(); 0272 m_columns.prepend(column); 0273 columnNames.prepend(xColumnName); 0274 break; 0275 } 0276 } 0277 0278 if (xColumnName.isEmpty()) { 0279 // no X-column was found left to the first selected column -> look right to the last selected column 0280 const int index = columns.indexOf(selectedColumns.last()) + 1; 0281 for (int i = index; i < columns.count(); ++i) { 0282 auto* column = columns.at(i); 0283 if (column->plotDesignation() == AbstractColumn::PlotDesignation::X && column->isPlottable()) { 0284 xColumnName = column->name(); 0285 m_columns.prepend(column); 0286 columnNames.prepend(xColumnName); 0287 break; 0288 } 0289 } 0290 } 0291 } 0292 0293 switch (m_plotType) { 0294 case PlotType::XYCurve: 0295 processColumnsForXYCurve(columnNames, xColumnName); 0296 break; 0297 case PlotType::Histogram: 0298 case PlotType::KDEPlot: 0299 case PlotType::QQPlot: 0300 case PlotType::BoxPlot: 0301 case PlotType::BarPlot: 0302 case PlotType::LollipopPlot: 0303 processColumnsForHistogram(columnNames); 0304 break; 0305 } 0306 0307 // resize the scroll area to show five ComboBoxes at maximum without showing the scroll bars 0308 int size = m_columnComboBoxes.size() >= 5 ? 5 : m_columnComboBoxes.size(); 0309 int height = size * ui->cbXColumn->height(); 0310 auto* layout = dynamic_cast<QGridLayout*>(ui->scrollAreaColumns->widget()->layout()); 0311 if (layout) { 0312 height += (size + 1) * layout->verticalSpacing(); 0313 if (m_plotType == PlotType::XYCurve) 0314 height += layout->verticalSpacing(); // one more spacing for the separating line 0315 } 0316 ui->scrollAreaColumns->setMinimumSize(ui->scrollAreaColumns->width(), height); 0317 0318 plotPlacementChanged(); 0319 } 0320 0321 void PlotDataDialog::processColumnsForXYCurve(const QStringList& columnNames, const QString& xColumnName) { 0322 m_columnComboBoxes << ui->cbXColumn; 0323 m_columnComboBoxes << ui->cbYColumn; 0324 0325 // ui-widget only has one combobox for the y-data -> add additional comboboxes dynamically if required 0326 if (m_columns.size() > 2) { 0327 auto* gridLayout = dynamic_cast<QGridLayout*>(ui->scrollAreaColumns->widget()->layout()); 0328 for (int i = 2; i < m_columns.size(); ++i) { 0329 QLabel* label = new QLabel(i18n("Y-data")); 0330 auto* comboBox = new QComboBox(); 0331 gridLayout->addWidget(label, i + 1, 0, 1, 1); 0332 gridLayout->addWidget(comboBox, i + 1, 2, 1, 1); 0333 m_columnComboBoxes << comboBox; 0334 } 0335 } else { 0336 // two columns provided, only one curve is possible -> hide the curve placement options 0337 ui->rbCurvePlacementAllInOnePlotArea->setChecked(true); 0338 ui->gbCurvePlacement->hide(); 0339 ui->gbPlotPlacement->setTitle(i18n("Add Plot to")); 0340 } 0341 0342 // show all selected/available column names in the data comboboxes 0343 for (auto* const comboBox : m_columnComboBoxes) 0344 comboBox->addItems(columnNames); 0345 0346 if (!xColumnName.isEmpty()) { 0347 // show in the X-data combobox the first column having X as the plot designation 0348 ui->cbXColumn->setCurrentIndex(ui->cbXColumn->findText(xColumnName)); 0349 0350 // for the remaining columns, show the names in the comboboxes for the Y-data 0351 // TODO: handle columns with error-designations 0352 int yColumnIndex = 1; // the index of the first Y-data comboBox in m_columnComboBoxes 0353 for (const QString& name : columnNames) { 0354 if (name != xColumnName) { 0355 QComboBox* comboBox = m_columnComboBoxes[yColumnIndex]; 0356 comboBox->setCurrentIndex(comboBox->findText(name)); 0357 yColumnIndex++; 0358 } 0359 } 0360 } else { 0361 // no column with "x plot designation" is selected, simply show all columns in the order they were selected. 0362 // first selected column will serve as the x-column. 0363 int yColumnIndex = 0; 0364 for (const QString& name : columnNames) { 0365 QComboBox* comboBox = m_columnComboBoxes[yColumnIndex]; 0366 comboBox->setCurrentIndex(comboBox->findText(name)); 0367 yColumnIndex++; 0368 } 0369 } 0370 } 0371 0372 /*! 0373 * processes columns for cases where one single column 0374 * is required per "plot" (histogram, boxplot, etc.) 0375 * */ 0376 void PlotDataDialog::processColumnsForHistogram(const QStringList& columnNames) { 0377 ui->line->hide(); 0378 ui->spacer->changeSize(0, 0); 0379 ui->chkCreateDataCurve->hide(); 0380 0381 // use the already available cbXColumn combo box 0382 ui->lXColumn->setText(i18n("Data")); 0383 m_columnComboBoxes << ui->cbXColumn; 0384 ui->cbXColumn->addItems(columnNames); 0385 ui->cbXColumn->setCurrentIndex(0); 0386 0387 if (m_columns.size() == 1) { 0388 // one column provided, only one histogram is possible 0389 //-> hide the curve placement options and the scroll areas for further columns 0390 ui->lYColumn->hide(); 0391 ui->cbYColumn->hide(); 0392 ui->rbCurvePlacementAllInOnePlotArea->setChecked(true); 0393 ui->gbCurvePlacement->hide(); 0394 ui->gbPlotPlacement->setTitle(i18n("Add Plot to")); 0395 ui->scrollAreaColumns->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 0396 } else { 0397 ui->gbPlotPlacement->setTitle(i18n("Add Plots to")); 0398 ui->scrollAreaColumns->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); 0399 0400 // use the already available cbYColumn combo box 0401 ui->lYColumn->setText(i18n("Data")); 0402 m_columnComboBoxes << ui->cbYColumn; 0403 ui->cbYColumn->addItems(columnNames); 0404 ui->cbYColumn->setCurrentIndex(1); 0405 0406 // add a ComboBox for every further column to be plotted 0407 auto* gridLayout = dynamic_cast<QGridLayout*>(ui->scrollAreaColumns->widget()->layout()); 0408 for (int i = 2; i < m_columns.size(); ++i) { 0409 auto* label = new QLabel(i18n("Data")); 0410 auto* comboBox = new QComboBox(); 0411 gridLayout->addWidget(label, i + 1, 0, 1, 1); 0412 gridLayout->addWidget(comboBox, i + 1, 2, 1, 1); 0413 comboBox->addItems(columnNames); 0414 comboBox->setCurrentIndex(i); 0415 m_columnComboBoxes << comboBox; 0416 } 0417 } 0418 } 0419 0420 void PlotDataDialog::plot() { 0421 WAIT_CURSOR; 0422 m_parentAspect->project()->setSuppressAspectAddedSignal(true); 0423 m_lastAddedCurve = nullptr; 0424 0425 if (ui->rbPlotPlacementExistingPlotArea->isChecked()) { 0426 // add curves to an existing plot 0427 auto* aspect = static_cast<AbstractAspect*>(cbExistingPlots->currentModelIndex().internalPointer()); 0428 auto* plot = static_cast<CartesianPlot*>(aspect); 0429 plot->beginMacro(i18n("Plot Area - %1", m_parentAspect->name())); 0430 addCurvesToPlot(plot); 0431 plot->endMacro(); 0432 } else if (ui->rbPlotPlacementExistingWorksheet->isChecked()) { 0433 // add curves to a new plot in an existing worksheet 0434 auto* aspect = static_cast<AbstractAspect*>(cbExistingWorksheets->currentModelIndex().internalPointer()); 0435 auto* worksheet = static_cast<Worksheet*>(aspect); 0436 worksheet->beginMacro(i18n("Worksheet - %1", m_parentAspect->name())); 0437 0438 if (ui->rbCurvePlacementAllInOnePlotArea->isChecked()) { 0439 // all curves in one plot 0440 auto* plot = new CartesianPlot(i18n("Plot Area - %1", m_parentAspect->name())); 0441 plot->setType(CartesianPlot::Type::FourAxes); 0442 worksheet->addChild(plot); 0443 if (m_columnComboBoxes.count() == 2) 0444 setAxesColumnLabels(plot, m_columnComboBoxes.at(1)->currentText()); 0445 0446 addCurvesToPlot(plot); 0447 setAxesTitles(plot); 0448 } else { 0449 // one plot per curve 0450 addCurvesToPlots(worksheet); 0451 } 0452 worksheet->endMacro(); 0453 } else { 0454 // add curves to a new plot(s) in a new worksheet 0455 auto* parent = m_parentAspect->parentAspect(); 0456 if (parent->type() == AspectType::DatapickerCurve) 0457 parent = parent->parentAspect()->parentAspect(); 0458 else if (parent->type() == AspectType::Workbook) 0459 parent = parent->parentAspect(); 0460 #ifdef HAVE_MQTT 0461 else if (dynamic_cast<MQTTTopic*>(m_parentAspect)) 0462 parent = m_parentAspect->project(); 0463 #endif 0464 parent->beginMacro(i18n("Plot data from %1", m_parentAspect->name())); 0465 auto* worksheet = new Worksheet(i18n("Worksheet - %1", m_parentAspect->name())); 0466 parent->addChild(worksheet); 0467 0468 if (ui->rbCurvePlacementAllInOnePlotArea->isChecked()) { 0469 // all curves in one plot 0470 auto* plot = new CartesianPlot(i18n("Plot Area - %1", m_parentAspect->name())); 0471 plot->setType(CartesianPlot::Type::FourAxes); 0472 worksheet->addChild(plot); 0473 if (m_columnComboBoxes.count() == 2) 0474 setAxesColumnLabels(plot, m_columnComboBoxes.at(1)->currentText()); 0475 addCurvesToPlot(plot); 0476 setAxesTitles(plot); 0477 } else { 0478 // one plot per curve 0479 addCurvesToPlots(worksheet); 0480 } 0481 0482 // when creating a new worksheet with many plots it can happen the place available for the data in the plot 0483 // is small and the result created here doesn't look nice. To avoid this and as a quick a dirty workaround, 0484 // we increase the size of the worksheet so the plots have a certain minimal size. 0485 // TODO: 0486 // A more sophisticated and better logic would require further adjustments for properties like plot area 0487 // paddings, the font size of axis ticks and title labels, etc. Also, this logic should be applied maybe if 0488 // we add plots to an already created worksheet. 0489 adjustWorksheetSize(worksheet); 0490 0491 parent->endMacro(); 0492 } 0493 0494 // if new curves are created via this dialog, select the parent plot of the last added curve in the project explorer. 0495 // if a custom fit is being done, select the last created fit curve independent of the number of created curves. 0496 m_parentAspect->project()->setSuppressAspectAddedSignal(false); 0497 if (m_lastAddedCurve) { 0498 QString path; 0499 if (!m_analysisMode) { 0500 path = m_lastAddedCurve->parentAspect()->path(); 0501 } else { 0502 if (m_analysisAction == XYAnalysisCurve::AnalysisAction::FitCustom) 0503 path = m_lastAddedCurve->path(); 0504 else 0505 path = m_lastAddedCurve->parentAspect()->path(); 0506 } 0507 0508 Q_EMIT m_parentAspect->project()->requestNavigateTo(path); 0509 } 0510 RESET_CURSOR; 0511 } 0512 0513 Column* PlotDataDialog::columnFromName(const QString& name) const { 0514 for (auto* column : m_columns) { 0515 if (column->name() == name) 0516 return column; 0517 } 0518 return nullptr; 0519 } 0520 0521 /*! 0522 * * for the selected columns in this dialog, creates a curve in the already existing plot \c plot. 0523 */ 0524 void PlotDataDialog::addCurvesToPlot(CartesianPlot* plot) { 0525 QApplication::processEvents(QEventLoop::AllEvents, 100); 0526 switch (m_plotType) { 0527 case PlotType::XYCurve: { 0528 Column* xColumn = columnFromName(ui->cbXColumn->currentText()); 0529 for (auto* comboBox : m_columnComboBoxes) { 0530 const QString& name = comboBox->currentText(); 0531 Column* yColumn = columnFromName(name); 0532 0533 // if only one column was selected, allow to use this column for x and for y. 0534 // otherwise, don't assign xColumn to y 0535 if (yColumn == xColumn) { 0536 if (m_columns.size() == 1) { 0537 addCurve(name, xColumn, yColumn, plot); 0538 break; 0539 } else 0540 continue; 0541 } 0542 0543 addCurve(name, xColumn, yColumn, plot); 0544 } 0545 break; 0546 } 0547 case PlotType::Histogram: 0548 case PlotType::KDEPlot: 0549 case PlotType::QQPlot: { 0550 for (auto* comboBox : m_columnComboBoxes) { 0551 const QString& name = comboBox->currentText(); 0552 Column* column = columnFromName(name); 0553 addSingleSourceColumnPlot(column, plot); 0554 } 0555 break; 0556 } 0557 case PlotType::BoxPlot: 0558 case PlotType::BarPlot: 0559 case PlotType::LollipopPlot: { 0560 QVector<const AbstractColumn*> columns; 0561 for (auto* comboBox : m_columnComboBoxes) 0562 columns << columnFromName(comboBox->currentText()); 0563 0564 addMultiSourceColumnsPlot(columns, plot); 0565 break; 0566 } 0567 } 0568 0569 // TODO: check if it really needs to update all ranges 0570 plot->dataChanged(-1, -1); 0571 } 0572 0573 /*! 0574 * for the selected columns in this dialog, creates a plot and a curve in the already existing worksheet \c worksheet. 0575 */ 0576 void PlotDataDialog::addCurvesToPlots(Worksheet* worksheet) { 0577 QApplication::processEvents(QEventLoop::AllEvents, 100); 0578 worksheet->setSuppressLayoutUpdate(true); 0579 0580 switch (m_plotType) { 0581 case PlotType::XYCurve: { 0582 const QString& xColumnName = ui->cbXColumn->currentText(); 0583 Column* xColumn = columnFromName(xColumnName); 0584 for (auto* comboBox : m_columnComboBoxes) { 0585 const QString& name = comboBox->currentText(); 0586 Column* yColumn = columnFromName(name); 0587 if (yColumn == xColumn) 0588 continue; 0589 0590 auto* plot = new CartesianPlot(i18n("Plot Area %1", name)); 0591 plot->setType(CartesianPlot::Type::FourAxes); 0592 worksheet->addChild(plot); 0593 setAxesColumnLabels(plot, yColumn); 0594 addCurve(name, xColumn, yColumn, plot); 0595 plot->scaleAuto(-1, -1); 0596 plot->retransform(); 0597 setAxesTitles(plot, name); 0598 } 0599 break; 0600 } 0601 case PlotType::Histogram: 0602 case PlotType::KDEPlot: 0603 case PlotType::QQPlot: { 0604 for (auto* comboBox : m_columnComboBoxes) { 0605 const QString& name = comboBox->currentText(); 0606 Column* column = columnFromName(name); 0607 0608 auto* plot = new CartesianPlot(i18n("Plot Area %1", name)); 0609 plot->setType(CartesianPlot::Type::FourAxes); 0610 setAxesTitles(plot, name); 0611 worksheet->addChild(plot); 0612 addSingleSourceColumnPlot(column, plot); 0613 plot->scaleAuto(-1, -1); 0614 plot->retransform(); 0615 } 0616 break; 0617 } 0618 case PlotType::BoxPlot: 0619 case PlotType::BarPlot: 0620 case PlotType::LollipopPlot: { 0621 for (auto* comboBox : m_columnComboBoxes) { 0622 const QString& name = comboBox->currentText(); 0623 Column* column = columnFromName(name); 0624 0625 auto* plot = new CartesianPlot(i18n("Plot Area %1", name)); 0626 plot->setType(CartesianPlot::Type::FourAxes); 0627 worksheet->addChild(plot); 0628 addMultiSourceColumnsPlot(QVector<const AbstractColumn*>{column}, plot); 0629 plot->scaleAuto(-1, -1); 0630 plot->retransform(); 0631 setAxesTitles(plot, name); 0632 } 0633 break; 0634 } 0635 } 0636 0637 worksheet->setSuppressLayoutUpdate(false); 0638 worksheet->updateLayout(); 0639 } 0640 0641 /*! 0642 * helper function that does the actual creation of the curve and adding it as child to the \c plot. 0643 */ 0644 void PlotDataDialog::addCurve(const QString& name, Column* xColumn, Column* yColumn, CartesianPlot* plot) { 0645 if (!m_analysisMode) { 0646 auto* curve = new XYCurve(name); 0647 curve->setSuppressRetransform(true); 0648 curve->setXColumn(xColumn); 0649 curve->setYColumn(yColumn); 0650 curve->setSuppressRetransform(false); 0651 plot->addChild(curve); 0652 m_lastAddedCurve = curve; 0653 } else { 0654 bool createDataCurve = ui->chkCreateDataCurve->isChecked(); 0655 XYCurve* curve = nullptr; 0656 if (createDataCurve) { 0657 curve = new XYCurve(name); 0658 curve->setSuppressRetransform(true); 0659 curve->setXColumn(xColumn); 0660 curve->setYColumn(yColumn); 0661 curve->setSuppressRetransform(false); 0662 plot->addChild(curve); 0663 m_lastAddedCurve = curve; 0664 } 0665 0666 XYAnalysisCurve* analysisCurve = nullptr; 0667 switch (m_analysisAction) { 0668 case XYAnalysisCurve::AnalysisAction::DataReduction: 0669 analysisCurve = new XYDataReductionCurve(i18n("Reduction of '%1'", name)); 0670 break; 0671 case XYAnalysisCurve::AnalysisAction::Differentiation: 0672 analysisCurve = new XYDifferentiationCurve(i18n("Derivative of '%1'", name)); 0673 break; 0674 case XYAnalysisCurve::AnalysisAction::Integration: 0675 analysisCurve = new XYIntegrationCurve(i18n("Integral of '%1'", name)); 0676 break; 0677 case XYAnalysisCurve::AnalysisAction::Interpolation: 0678 analysisCurve = new XYInterpolationCurve(i18n("Interpolation of '%1'", name)); 0679 break; 0680 case XYAnalysisCurve::AnalysisAction::Smoothing: 0681 analysisCurve = new XYSmoothCurve(i18n("Smoothing of '%1'", name)); 0682 break; 0683 case XYAnalysisCurve::AnalysisAction::FitLinear: 0684 case XYAnalysisCurve::AnalysisAction::FitPower: 0685 case XYAnalysisCurve::AnalysisAction::FitExp1: 0686 case XYAnalysisCurve::AnalysisAction::FitExp2: 0687 case XYAnalysisCurve::AnalysisAction::FitInvExp: 0688 case XYAnalysisCurve::AnalysisAction::FitGauss: 0689 case XYAnalysisCurve::AnalysisAction::FitCauchyLorentz: 0690 case XYAnalysisCurve::AnalysisAction::FitTan: 0691 case XYAnalysisCurve::AnalysisAction::FitTanh: 0692 case XYAnalysisCurve::AnalysisAction::FitErrFunc: 0693 case XYAnalysisCurve::AnalysisAction::FitCustom: 0694 analysisCurve = new XYFitCurve(i18n("Fit to '%1'", name)); 0695 static_cast<XYFitCurve*>(analysisCurve)->initFitData(m_analysisAction); 0696 static_cast<XYFitCurve*>(analysisCurve)->initStartValues(curve); 0697 break; 0698 case XYAnalysisCurve::AnalysisAction::FourierFilter: 0699 analysisCurve = new XYFourierFilterCurve(i18n("Fourier Filter of '%1'", name)); 0700 break; 0701 } 0702 0703 if (analysisCurve != nullptr) { 0704 analysisCurve->setSuppressRetransform(true); 0705 analysisCurve->setXDataColumn(xColumn); 0706 analysisCurve->setYDataColumn(yColumn); 0707 if (m_analysisAction != XYAnalysisCurve::AnalysisAction::FitCustom) // no custom fit-model set yet, no need to recalculate 0708 analysisCurve->recalculate(); 0709 analysisCurve->setSuppressRetransform(false); 0710 plot->addChild(analysisCurve); 0711 m_lastAddedCurve = analysisCurve; 0712 } 0713 } 0714 } 0715 0716 void PlotDataDialog::addSingleSourceColumnPlot(const Column* column, CartesianPlot* plotArea) { 0717 const QString& name = column->name(); 0718 QApplication::processEvents(QEventLoop::AllEvents, 100); 0719 Plot* plot{nullptr}; 0720 if (m_plotType == PlotType::Histogram) { 0721 auto* hist = new Histogram(name); 0722 hist->setDataColumn(column); 0723 plot = hist; 0724 } else if (m_plotType == PlotType::KDEPlot) { 0725 auto* kdePlot = new KDEPlot(name); 0726 kdePlot->setDataColumn(column); 0727 plot = kdePlot; 0728 } else if (m_plotType == PlotType::QQPlot) { 0729 auto* qqPlot = new QQPlot(name); 0730 qqPlot->setDataColumn(column); 0731 plot = qqPlot; 0732 } 0733 0734 if (plot) { 0735 plotArea->addChild(plot); 0736 m_lastAddedCurve = plot; 0737 } 0738 } 0739 0740 void PlotDataDialog::addMultiSourceColumnsPlot(const QVector<const AbstractColumn*>& columns, CartesianPlot* plotArea) { 0741 QString name; 0742 if (columns.size() > 1) { 0743 if (m_plotType == PlotType::BoxPlot) 0744 name = i18n("Box Plot"); 0745 else if (m_plotType == PlotType::BarPlot) 0746 name = i18n("Bar Plot"); 0747 else if (m_plotType == PlotType::LollipopPlot) 0748 name = i18n("Bar Plot"); 0749 } else 0750 name = columns.constFirst()->name(); 0751 0752 QApplication::processEvents(QEventLoop::AllEvents, 100); 0753 Plot* plot{nullptr}; 0754 if (m_plotType == PlotType::BoxPlot) { 0755 auto* boxPlot = new BoxPlot(name); 0756 boxPlot->setDataColumns(columns); 0757 plot = boxPlot; 0758 } else if (m_plotType == PlotType::BarPlot) { 0759 auto* barPlot = new BarPlot(name); 0760 barPlot->setDataColumns(columns); 0761 plot = barPlot; 0762 } else if (m_plotType == PlotType::LollipopPlot) { 0763 auto* lollipopPlot = new LollipopPlot(name); 0764 lollipopPlot->setDataColumns(columns); 0765 plot = lollipopPlot; 0766 } 0767 0768 if (plot) { 0769 plotArea->addChild(plot); 0770 m_lastAddedCurve = plot; 0771 } 0772 } 0773 0774 void PlotDataDialog::adjustWorksheetSize(Worksheet* worksheet) const { 0775 // adjust the sizes 0776 const auto layout = worksheet->layout(); 0777 const auto plots = worksheet->children<CartesianPlot>(); 0778 const int count = plots.size(); 0779 const double minSize = 4.0; 0780 switch (layout) { 0781 case Worksheet::Layout::NoLayout: 0782 case Worksheet::Layout::VerticalLayout: { 0783 if (layout == Worksheet::Layout::NoLayout) 0784 worksheet->setLayout(Worksheet::Layout::VerticalLayout); 0785 0786 const auto plot = plots.constFirst(); 0787 double height = Worksheet::convertFromSceneUnits(plot->rect().height(), Worksheet::Unit::Centimeter); 0788 if (height < 4.) { 0789 double newHeight = worksheet->layoutTopMargin() + worksheet->layoutBottomMargin() + (count - 1) * worksheet->layoutHorizontalSpacing() 0790 + count * Worksheet::convertToSceneUnits(minSize, Worksheet::Unit::Centimeter); 0791 QRectF newRect = worksheet->pageRect(); 0792 newRect.setHeight(round(newHeight)); 0793 worksheet->setPageRect(newRect); 0794 } 0795 break; 0796 } 0797 case Worksheet::Layout::HorizontalLayout: { 0798 const auto plot = plots.constFirst(); 0799 double width = Worksheet::convertFromSceneUnits(plot->rect().width(), Worksheet::Unit::Centimeter); 0800 if (width < 4.) { 0801 double newWidth = worksheet->layoutLeftMargin() + worksheet->layoutRightMargin() + (count - 1) * worksheet->layoutVerticalSpacing() 0802 + count * Worksheet::convertToSceneUnits(minSize, Worksheet::Unit::Centimeter); 0803 QRectF newRect = worksheet->pageRect(); 0804 newRect.setWidth(round(newWidth)); 0805 worksheet->setPageRect(newRect); 0806 } 0807 break; 0808 } 0809 case Worksheet::Layout::GridLayout: { 0810 const auto plot = plots.constFirst(); 0811 double width = Worksheet::convertFromSceneUnits(plot->rect().width(), Worksheet::Unit::Centimeter); 0812 double height = Worksheet::convertFromSceneUnits(plot->rect().height(), Worksheet::Unit::Centimeter); 0813 if (width < 4. || height < 4.) { 0814 QRectF newRect = worksheet->pageRect(); 0815 if (height < 4.) { 0816 double newHeight = worksheet->layoutTopMargin() + worksheet->layoutBottomMargin() + (count - 1) * worksheet->layoutHorizontalSpacing() 0817 + count * Worksheet::convertToSceneUnits(minSize, Worksheet::Unit::Centimeter); 0818 newRect.setHeight(round(newHeight)); 0819 } else { 0820 double newWidth = worksheet->layoutLeftMargin() + worksheet->layoutRightMargin() + (count - 1) * worksheet->layoutVerticalSpacing() 0821 + count * Worksheet::convertToSceneUnits(minSize, Worksheet::Unit::Centimeter); 0822 newRect.setWidth(round(newWidth)); 0823 } 0824 worksheet->setPageRect(newRect); 0825 } 0826 break; 0827 } 0828 } 0829 } 0830 0831 void PlotDataDialog::setAxesColumnLabels(CartesianPlot* plot, const Column* column) { 0832 // Use value labels if possible 0833 if (column && column->valueLabelsInitialized()) { 0834 auto axes = plot->children(AspectType::Axis); 0835 for (auto a : axes) { 0836 auto axis = static_cast<Axis*>(a); 0837 if (axis->orientation() == Axis::Orientation::Vertical) { 0838 // Use first vertical axis 0839 axis->setMajorTicksType(Axis::TicksType::ColumnLabels); 0840 axis->setMajorTicksColumn(column); 0841 break; 0842 } 0843 } 0844 } 0845 } 0846 0847 void PlotDataDialog::setAxesColumnLabels(CartesianPlot* plot, const QString& columnName) { 0848 Column* column = columnFromName(columnName); 0849 setAxesColumnLabels(plot, column); 0850 } 0851 0852 void PlotDataDialog::setAxesTitles(CartesianPlot* plot, const QString& name) const { 0853 DEBUG(Q_FUNC_INFO) 0854 const auto& axes = plot->children<Axis>(); 0855 switch (m_plotType) { 0856 case PlotType::XYCurve: { 0857 // x-axis title 0858 const QString& xColumnName = ui->cbXColumn->currentText(); 0859 // DEBUG(Q_FUNC_INFO << ", x column name = " << STDSTRING(xColumnName)) 0860 0861 for (auto* axis : axes) { 0862 if (axis->orientation() == Axis::Orientation::Horizontal) { 0863 axis->title()->setText(xColumnName); 0864 break; 0865 } 0866 } 0867 0868 // y-axis title 0869 for (auto* axis : axes) { 0870 if (axis->orientation() == Axis::Orientation::Vertical) { 0871 if (!name.isEmpty()) { 0872 // multiple columns are plotted with "one curve per plot", 0873 // the function is called with the column name. 0874 // use it for the x-axis title 0875 axis->title()->setText(name); 0876 } else if (m_columnComboBoxes.size() == 2) { 0877 // if we only have one single y-column to plot, we can set the title of the y-axes 0878 const QString& yColumnName = m_columnComboBoxes[1]->currentText(); 0879 axis->title()->setText(yColumnName); 0880 } 0881 0882 break; 0883 } 0884 } 0885 break; 0886 } 0887 case PlotType::Histogram: 0888 case PlotType::KDEPlot: { 0889 // x-axis title 0890 for (auto* axis : axes) { 0891 if (axis->orientation() == Axis::Orientation::Horizontal) { 0892 if (!name.isEmpty()) { 0893 // multiple columns are plotted with "one curve per plot", 0894 // the function is called with the column name. 0895 // use it for the x-axis title 0896 axis->title()->setText(name); 0897 } else if (m_columnComboBoxes.size() == 1) { 0898 const QString& yColumnName = m_columnComboBoxes.constFirst()->currentText(); 0899 axis->title()->setText(yColumnName); 0900 } 0901 0902 break; 0903 } 0904 } 0905 0906 // y-axis title 0907 for (auto* axis : axes) { 0908 if (axis->orientation() == Axis::Orientation::Vertical) { 0909 if (m_plotType == PlotType::Histogram) 0910 axis->title()->setText(i18n("Frequency")); 0911 else 0912 axis->title()->setText(i18n("Density")); 0913 break; 0914 } 0915 } 0916 break; 0917 } 0918 case PlotType::QQPlot: { 0919 // x-axis title 0920 for (auto* axis : axes) { 0921 if (axis->orientation() == Axis::Orientation::Horizontal) { 0922 axis->title()->setText(i18n("Theoretical Quantiles")); 0923 break; 0924 } 0925 } 0926 0927 // y-axis title 0928 for (auto* axis : axes) { 0929 if (axis->orientation() == Axis::Orientation::Vertical) { 0930 axis->title()->setText(i18n("Sample Quantiles")); 0931 break; 0932 } 0933 } 0934 break; 0935 } 0936 case PlotType::BoxPlot: { 0937 auto* boxPlot = static_cast<BoxPlot*>(m_lastAddedCurve); 0938 auto orientation = boxPlot->orientation(); 0939 0940 // number of box plots per plot: 0941 int count = 1; // 1 if we create a separate plot for every box plot 0942 if (ui->rbCurvePlacementAllInOnePlotArea->isChecked()) 0943 count = m_columnComboBoxes.count(); // all box plots in one single plot 0944 0945 // set the range of the plot and the number of ticks manually. 0946 // the n-th box plot is positioned at x=n and has the width=0.5 in logical coordinatates. 0947 // manually set the range to (0.5, n+0.5) and ajdust the number of ticks starting at 0.5 0948 // to make sure we have every tick precisely under the middle of the box plot 0949 const auto xIndex = plot->coordinateSystem(boxPlot->coordinateSystemIndex())->index(Dimension::X); 0950 const auto yIndex = plot->coordinateSystem(boxPlot->coordinateSystemIndex())->index(Dimension::Y); 0951 Range<double> range(0.5, count + 0.5); 0952 if (orientation == WorksheetElement::Orientation::Vertical) 0953 plot->setRange(Dimension::X, xIndex, range); 0954 else 0955 plot->setRange(Dimension::Y, yIndex, range); 0956 0957 for (auto* axis : axes) { 0958 if (axis->orientation() != orientation) { 0959 axis->setMajorTicksType(Axis::TicksType::Spacing); 0960 axis->setMajorTicksSpacing(1.); 0961 axis->setMajorTickStartOffset(0.5); 0962 axis->setMinorTicksNumber(0); 0963 } 0964 axis->title()->setText(QString()); // no title 0965 } 0966 0967 // y-axis title 0968 for (auto* axis : axes) { 0969 if (axis->orientation() == Axis::Orientation::Vertical) { 0970 if (!name.isEmpty()) { 0971 // multiple columns are plotted with "one curve per plot", 0972 // the function is called with the column name. 0973 // use it for the x-axis title 0974 axis->title()->setText(name); 0975 } else if (count == 1) { 0976 const QString& yColumnName = m_columnComboBoxes.constFirst()->currentText(); 0977 axis->title()->setText(yColumnName); 0978 } 0979 break; 0980 } 0981 } 0982 break; 0983 } 0984 case PlotType::BarPlot: 0985 case PlotType::LollipopPlot: { 0986 WorksheetElement::Orientation orientation; 0987 if (m_plotType == PlotType::BarPlot) { 0988 auto* barPlot = static_cast<BarPlot*>(m_lastAddedCurve); 0989 orientation = barPlot->orientation(); 0990 } else { 0991 auto* lollipopPlot = static_cast<LollipopPlot*>(m_lastAddedCurve); 0992 orientation = lollipopPlot->orientation(); 0993 } 0994 0995 plot->setNiceExtend(false); 0996 0997 for (auto* axis : axes) { 0998 if (axis->orientation() != orientation) { 0999 axis->setMajorTicksType(Axis::TicksType::Spacing); 1000 axis->setMajorTickStartOffset(0.5); 1001 axis->setMajorTicksSpacing(1.); 1002 axis->setLabelsPosition(Axis::LabelsPosition::NoLabels); 1003 axis->setMinorTicksDirection(Axis::noTicks); 1004 } 1005 axis->title()->setText(QString()); // no title 1006 } 1007 } 1008 } 1009 } 1010 1011 // ################################################################ 1012 // ########################## Slots ############################### 1013 // ################################################################ 1014 void PlotDataDialog::curvePlacementChanged() { 1015 if (ui->rbCurvePlacementAllInOnePlotArea->isChecked()) { 1016 ui->rbPlotPlacementExistingPlotArea->setEnabled(true); 1017 } else { 1018 ui->rbPlotPlacementExistingPlotArea->setEnabled(false); 1019 if (ui->rbPlotPlacementExistingPlotArea->isChecked()) 1020 ui->rbPlotPlacementExistingWorksheet->setChecked(true); 1021 } 1022 } 1023 1024 void PlotDataDialog::plotPlacementChanged() { 1025 if (ui->rbPlotPlacementExistingPlotArea->isChecked()) { 1026 cbExistingPlots->setEnabled(true); 1027 cbExistingWorksheets->setEnabled(false); 1028 } else if (ui->rbPlotPlacementExistingWorksheet->isChecked()) { 1029 cbExistingPlots->setEnabled(false); 1030 cbExistingWorksheets->setEnabled(true); 1031 } else { 1032 cbExistingPlots->setEnabled(false); 1033 cbExistingWorksheets->setEnabled(false); 1034 } 1035 1036 checkOkButton(); 1037 } 1038 1039 void PlotDataDialog::checkOkButton() { 1040 bool enable = false; 1041 QString msg; 1042 if ((m_plotType == PlotType::XYCurve && (ui->cbXColumn->currentIndex() == -1 || ui->cbYColumn->currentIndex() == -1)) 1043 || (m_plotType == PlotType::Histogram && ui->cbXColumn->currentIndex() == -1)) 1044 msg = i18n("No data selected to plot."); 1045 else if (ui->rbPlotPlacementExistingPlotArea->isChecked()) { 1046 AbstractAspect* aspect = static_cast<AbstractAspect*>(cbExistingPlots->currentModelIndex().internalPointer()); 1047 enable = (aspect != nullptr); 1048 if (!enable) 1049 msg = i18n("An already existing plot area has to be selected."); 1050 } else if (ui->rbPlotPlacementExistingWorksheet->isChecked()) { 1051 AbstractAspect* aspect = static_cast<AbstractAspect*>(cbExistingWorksheets->currentModelIndex().internalPointer()); 1052 enable = (aspect != nullptr); 1053 if (!enable) 1054 msg = i18n("An already existing worksheet has to be selected."); 1055 } else 1056 enable = true; 1057 1058 m_okButton->setEnabled(enable); 1059 if (enable) 1060 m_okButton->setToolTip(i18n("Close the dialog and plot the data.")); 1061 else 1062 m_okButton->setToolTip(msg); 1063 }