"Fossies" - the Fresh Open Source Software Archive

Member "labplot-2.8.2/src/kdefrontend/spreadsheet/PlotDataDialog.cpp" (24 Feb 2021, 31649 Bytes) of package /linux/privat/labplot-2.8.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "PlotDataDialog.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.8.1_vs_2.8.2.

    1 /***************************************************************************
    2     File                 : PlotDataDialog.cpp
    3     Project              : LabPlot
    4     Description          : Dialog for generating plots for the spreadsheet data
    5     --------------------------------------------------------------------
    6     Copyright            : (C) 2017-2019 by Alexander Semke (alexander.semke@web.de)
    7 
    8  ***************************************************************************/
    9 
   10 /***************************************************************************
   11  *                                                                         *
   12  *  This program is free software; you can redistribute it and/or modify   *
   13  *  it under the terms of the GNU General Public License as published by   *
   14  *  the Free Software Foundation; either version 2 of the License, or      *
   15  *  (at your option) any later version.                                    *
   16  *                                                                         *
   17  *  This program is distributed in the hope that it will be useful,        *
   18  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
   19  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
   20  *  GNU General Public License for more details.                           *
   21  *                                                                         *
   22  *   You should have received a copy of the GNU General Public License     *
   23  *   along with this program; if not, write to the Free Software           *
   24  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
   25  *   Boston, MA  02110-1301  USA                                           *
   26  *                                                                         *
   27  ***************************************************************************/
   28 
   29 #include "PlotDataDialog.h"
   30 #include "backend/core/AspectTreeModel.h"
   31 #include "backend/core/Project.h"
   32 #include "backend/core/column/Column.h"
   33 #include "backend/datapicker/DatapickerCurve.h"
   34 #include "backend/spreadsheet/Spreadsheet.h"
   35 #include "backend/worksheet/plots/cartesian/Axis.h"
   36 #include "backend/worksheet/plots/cartesian/XYAnalysisCurve.h"
   37 #include "backend/worksheet/plots/cartesian/XYCurve.h"
   38 #include "backend/worksheet/plots/cartesian/XYDataReductionCurve.h"
   39 #include "backend/worksheet/plots/cartesian/XYDifferentiationCurve.h"
   40 #include "backend/worksheet/plots/cartesian/XYIntegrationCurve.h"
   41 #include "backend/worksheet/plots/cartesian/XYInterpolationCurve.h"
   42 #include "backend/worksheet/plots/cartesian/XYSmoothCurve.h"
   43 #include "backend/worksheet/plots/cartesian/XYFitCurve.h"
   44 #include "backend/worksheet/plots/cartesian/XYFourierFilterCurve.h"
   45 
   46 #ifdef HAVE_MQTT
   47 #include "backend/datasources/MQTTTopic.h"
   48 #endif
   49 
   50 #include "backend/worksheet/plots/cartesian/CartesianPlot.h"
   51 #include "backend/worksheet/Worksheet.h"
   52 #include "backend/worksheet/TextLabel.h"
   53 #include "commonfrontend/spreadsheet/SpreadsheetView.h"
   54 #include "commonfrontend/widgets/TreeViewComboBox.h"
   55 
   56 #include <QDialogButtonBox>
   57 #include <QPushButton>
   58 #include <QWindow>
   59 
   60 #include <KConfigGroup>
   61 #include <KSharedConfig>
   62 #include <KWindowConfig>
   63 
   64 #include "ui_plotdatawidget.h"
   65 
   66 /*!
   67     \class PlotDataDialog
   68     \brief Dialog for generating plots for the spreadsheet data.
   69 
   70     \ingroup kdefrontend
   71  */
   72 PlotDataDialog::PlotDataDialog(Spreadsheet* s, PlotType type, QWidget* parent) : QDialog(parent),
   73     ui(new Ui::PlotDataWidget()),
   74     m_spreadsheet(s),
   75     m_plotsModel(new AspectTreeModel(m_spreadsheet->project())),
   76     m_worksheetsModel(new AspectTreeModel(m_spreadsheet->project())),
   77     m_plotType(type) {
   78 
   79     setAttribute(Qt::WA_DeleteOnClose);
   80     setWindowTitle(i18nc("@title:window", "Plot Spreadsheet Data"));
   81     setWindowIcon(QIcon::fromTheme("office-chart-line"));
   82 
   83     QWidget* mainWidget = new QWidget(this);
   84     ui->setupUi(mainWidget);
   85 
   86     auto* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
   87     m_okButton = buttonBox->button(QDialogButtonBox::Ok);
   88     m_okButton->setDefault(true);
   89     m_okButton->setToolTip(i18n("Plot the selected data"));
   90     m_okButton->setText(i18n("&Plot"));
   91 
   92     auto* layout = new QVBoxLayout(this);
   93     layout->addWidget(mainWidget);
   94     layout->addWidget(buttonBox);
   95     setLayout(layout);
   96 
   97     //create combox boxes for the existing plots and worksheets
   98     auto* gridLayout = dynamic_cast<QGridLayout*>(ui->gbPlotPlacement->layout());
   99     cbExistingPlots = new TreeViewComboBox(ui->gbPlotPlacement);
  100     cbExistingPlots->setMinimumWidth(250);//TODO: use proper sizeHint in TreeViewComboBox
  101     gridLayout->addWidget(cbExistingPlots, 0, 1, 1, 1);
  102 
  103     cbExistingWorksheets = new TreeViewComboBox(ui->gbPlotPlacement);
  104     cbExistingWorksheets->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred));
  105     gridLayout->addWidget(cbExistingWorksheets, 1, 1, 1, 1);
  106 
  107     QList<AspectType> list{AspectType::Folder, AspectType::Worksheet, AspectType::CartesianPlot};
  108     cbExistingPlots->setTopLevelClasses(list);
  109     list = {AspectType::CartesianPlot};
  110     m_plotsModel->setSelectableAspects(list);
  111     cbExistingPlots->setModel(m_plotsModel);
  112 
  113     //select the first available plot, if available
  114     auto plots = m_spreadsheet->project()->children<CartesianPlot>(AbstractAspect::ChildIndexFlag::Recursive);
  115     if (!plots.isEmpty()) {
  116         const auto* plot = plots.first();
  117         cbExistingPlots->setCurrentModelIndex(m_plotsModel->modelIndexOfAspect(plot));
  118     }
  119 
  120     list = {AspectType::Folder, AspectType::Worksheet};
  121     cbExistingWorksheets->setTopLevelClasses(list);
  122     list = {AspectType::Worksheet};
  123     m_worksheetsModel->setSelectableAspects(list);
  124     cbExistingWorksheets->setModel(m_worksheetsModel);
  125 
  126     //select the first available worksheet, if available
  127     auto worksheets = m_spreadsheet->project()->children<Worksheet>(AbstractAspect::ChildIndexFlag::Recursive);
  128     if (!worksheets.isEmpty()) {
  129         const auto* worksheet = worksheets.first();
  130         cbExistingWorksheets->setCurrentModelIndex(m_worksheetsModel->modelIndexOfAspect(worksheet));
  131     }
  132 
  133     //in the grid layout of the scroll area we have on default one row for the x-column,
  134     //one row for the separating line and one line for the y-column.
  135     //set the height of this default content as the minimal size of the scroll area.
  136     gridLayout = dynamic_cast<QGridLayout*>(ui->scrollAreaColumns->widget()->layout());
  137     int height = 2*ui->cbXColumn->height() + ui->line->height()
  138                 + 2*gridLayout->verticalSpacing()
  139                 + gridLayout->contentsMargins().top()
  140                 + gridLayout->contentsMargins().bottom();
  141     ui->scrollAreaColumns->setMinimumSize(0, height);
  142 
  143     //hide the check box for creation of original data, only shown if analysis curves are to be created
  144     ui->spacer->changeSize(0, 0);
  145     ui->chkCreateDataCurve->hide();
  146 
  147     //SIGNALs/SLOTs
  148     connect(buttonBox, &QDialogButtonBox::accepted, this, [=]() { hide();  plot(); });
  149     connect(buttonBox, &QDialogButtonBox::rejected, this, &PlotDataDialog::reject);
  150     connect(buttonBox, &QDialogButtonBox::accepted, this, &PlotDataDialog::accept);
  151     connect(ui->rbCurvePlacement1, &QRadioButton::toggled, this, &PlotDataDialog::curvePlacementChanged);
  152     connect(ui->rbCurvePlacement2, &QRadioButton::toggled, this, &PlotDataDialog::curvePlacementChanged);
  153     connect(ui->rbPlotPlacement1, &QRadioButton::toggled, this, &PlotDataDialog::plotPlacementChanged);
  154     connect(ui->rbPlotPlacement2, &QRadioButton::toggled, this, &PlotDataDialog::plotPlacementChanged);
  155     connect(ui->rbPlotPlacement3, &QRadioButton::toggled, this, &PlotDataDialog::plotPlacementChanged);
  156     connect(cbExistingPlots, &TreeViewComboBox::currentModelIndexChanged, this, &PlotDataDialog::checkOkButton);
  157     connect(cbExistingWorksheets, &TreeViewComboBox::currentModelIndexChanged, this, &PlotDataDialog::checkOkButton);
  158 
  159     //restore saved settings if available
  160     create(); // ensure there's a window created
  161     KConfigGroup conf(KSharedConfig::openConfig(), "PlotDataDialog");
  162     if (conf.exists()) {
  163         int index = conf.readEntry("CurvePlacement", 0);
  164         if (index == 2) ui->rbCurvePlacement2->setChecked(true);
  165 
  166         index = conf.readEntry("PlotPlacement", 0);
  167         if (index == 2) ui->rbPlotPlacement2->setChecked(true);
  168         if (index == 3) ui->rbPlotPlacement3->setChecked(true);
  169 
  170         KWindowConfig::restoreWindowSize(windowHandle(), conf);
  171         resize(windowHandle()->size()); // workaround for QTBUG-40584
  172     } else
  173         resize(QSize(0, 0).expandedTo(minimumSize()));
  174 
  175     processColumns();
  176     plotPlacementChanged();
  177 }
  178 
  179 PlotDataDialog::~PlotDataDialog() {
  180     //save current settings
  181     KConfigGroup conf(KSharedConfig::openConfig(), "PlotDataDialog");
  182     int index = 0;
  183     if (ui->rbCurvePlacement1->isChecked()) index = 1;
  184     if (ui->rbCurvePlacement2->isChecked()) index = 2;
  185     conf.writeEntry("CurvePlacement", index);
  186 
  187     if (ui->rbPlotPlacement1->isChecked()) index = 1;
  188     if (ui->rbPlotPlacement2->isChecked()) index = 2;
  189     if (ui->rbPlotPlacement3->isChecked()) index = 3;
  190     conf.writeEntry("PlotPlacement", index);
  191 
  192     KWindowConfig::saveWindowSize(windowHandle(), conf);
  193 
  194     delete m_plotsModel;
  195     delete m_worksheetsModel;
  196 }
  197 
  198 void PlotDataDialog::setAnalysisAction(AnalysisAction action) {
  199     m_analysisAction = action;
  200     m_analysisMode = true;
  201     ui->spacer->changeSize(0, 40);
  202     ui->chkCreateDataCurve->show();
  203 }
  204 
  205 void PlotDataDialog::processColumns() {
  206     //columns to plot
  207     auto* view = reinterpret_cast<SpreadsheetView*>(m_spreadsheet->view());
  208     QVector<Column*> selectedColumns = view->selectedColumns(true);
  209 
  210     //use all spreadsheet columns if no columns are selected
  211     if (selectedColumns.isEmpty())
  212         selectedColumns = m_spreadsheet->children<Column>();
  213 
  214     //skip error and non-plottable columns
  215     for (Column* col : selectedColumns) {
  216         if ((col->plotDesignation() == AbstractColumn::PlotDesignation::X || col->plotDesignation() == AbstractColumn::PlotDesignation::Y
  217             || col->plotDesignation() == AbstractColumn::PlotDesignation::NoDesignation) && col->isPlottable())
  218             m_columns << col;
  219     }
  220 
  221     //disable everything if the spreadsheet doesn't have any columns to plot
  222     if (m_columns.isEmpty()) {
  223         ui->gbCurvePlacement->setEnabled(false);
  224         ui->gbPlotPlacement->setEnabled(false);
  225         return;
  226     }
  227 
  228     //determine the column names
  229     //and the name of the first column having "X" as the plot designation (relevant for xy-curves only)
  230     QStringList columnNames;
  231     QString xColumnName;
  232     for (const Column* column : m_columns) {
  233         columnNames << column->name();
  234         if (m_plotType == PlotType::XYCurve && xColumnName.isEmpty() && column->plotDesignation() == AbstractColumn::PlotDesignation::X)
  235             xColumnName = column->name();
  236     }
  237 
  238     if (m_plotType == PlotType::XYCurve && xColumnName.isEmpty()) {
  239         //no X-column was selected -> look for the first non-selected X-column left to the first selected column
  240         const int index = m_spreadsheet->indexOfChild<Column>(selectedColumns.first()) - 1;
  241         if (index >= 0) {
  242             for (int i = index; i >= 0; --i) {
  243                 Column* column = m_spreadsheet->column(i);
  244                 if (column->plotDesignation() == AbstractColumn::PlotDesignation::X && column->isPlottable()) {
  245                     xColumnName = column->name();
  246                     m_columns.prepend(column);
  247                     columnNames.prepend(xColumnName);
  248                     break;
  249                 }
  250             }
  251         }
  252     }
  253 
  254     switch (m_plotType) {
  255     case PlotType::XYCurve:
  256         processColumnsForXYCurve(columnNames, xColumnName);
  257         break;
  258     case PlotType::Histogram:
  259         processColumnsForHistogram(columnNames);
  260         break;
  261     }
  262 
  263     //resize the scroll area to show five ComboBoxes at maximum without showing the scroll bars
  264     int size = m_columnComboBoxes.size() >= 5 ? 5 : m_columnComboBoxes.size();
  265     int height = size * ui->cbXColumn->height();
  266     auto* layout = dynamic_cast<QGridLayout*>(ui->scrollAreaColumns->widget()->layout());
  267     if (layout) {
  268         height += (size + 1)*layout->verticalSpacing();
  269         if (m_plotType == PlotType::XYCurve)
  270             height += layout->verticalSpacing(); //one more spacing for the separating line
  271     }
  272     ui->scrollAreaColumns->setMinimumSize(ui->scrollAreaColumns->width(), height);
  273 }
  274 
  275 void PlotDataDialog::processColumnsForXYCurve(const QStringList& columnNames, const QString& xColumnName) {
  276     m_columnComboBoxes << ui->cbXColumn;
  277     m_columnComboBoxes << ui->cbYColumn;
  278 
  279     //ui-widget only has one combobox for the y-data -> add additional comboboxes dynamically if required
  280     if (m_columns.size() > 2) {
  281         auto* gridLayout = dynamic_cast<QGridLayout*>(ui->scrollAreaColumns->widget()->layout());
  282         for (int i = 2; i < m_columns.size(); ++i) {
  283             QLabel* label = new QLabel(i18n("Y-data"));
  284             auto* comboBox = new QComboBox();
  285             gridLayout->addWidget(label, i+1, 0, 1, 1);
  286             gridLayout->addWidget(comboBox, i+1, 2, 1, 1);
  287             m_columnComboBoxes << comboBox;
  288         }
  289     } else {
  290         //two columns provided, only one curve is possible -> hide the curve placement options
  291         ui->rbCurvePlacement1->setChecked(true);
  292         ui->gbCurvePlacement->hide();
  293         ui->gbPlotPlacement->setTitle(i18n("Add Curve to"));
  294     }
  295 
  296     //show all selected/available column names in the data comboboxes
  297     for (QComboBox* const comboBox : m_columnComboBoxes)
  298         comboBox->addItems(columnNames);
  299 
  300     if (!xColumnName.isEmpty()) {
  301         //show in the X-data combobox the first column having X as the plot designation
  302         ui->cbXColumn->setCurrentIndex(ui->cbXColumn->findText(xColumnName));
  303 
  304         //for the remaining columns, show the names in the comboboxes for the Y-data
  305         //TODO: handle columns with error-designations
  306         int yColumnIndex = 1; //the index of the first Y-data comboBox in m_columnComboBoxes
  307         for (const QString& name : columnNames) {
  308             if (name != xColumnName) {
  309                 QComboBox* comboBox = m_columnComboBoxes[yColumnIndex];
  310                 comboBox->setCurrentIndex(comboBox->findText(name));
  311                 yColumnIndex++;
  312             }
  313         }
  314     } else {
  315         //no column with "x plot designation" is selected, simply show all columns in the order they were selected.
  316         //first selected column will serve as the x-column.
  317         int yColumnIndex = 0;
  318         for (const QString& name : columnNames) {
  319             QComboBox* comboBox = m_columnComboBoxes[yColumnIndex];
  320             comboBox->setCurrentIndex(comboBox->findText(name));
  321             yColumnIndex++;
  322         }
  323     }
  324 }
  325 
  326 void PlotDataDialog::processColumnsForHistogram(const QStringList& columnNames) {
  327     ui->gbData->setTitle(i18n("Histogram Data"));
  328     ui->line->hide();
  329     ui->spacer->changeSize(0, 0);
  330     ui->chkCreateDataCurve->hide();
  331 
  332     //use the already available cbXColumn combo box
  333     ui->lXColumn->setText(i18n("Data"));
  334     m_columnComboBoxes << ui->cbXColumn;
  335     ui->cbXColumn->addItems(columnNames);
  336     ui->cbXColumn->setCurrentIndex(0);
  337 
  338     if (m_columns.size() == 1) {
  339         //one column provided, only one histogram is possible
  340         //-> hide the curve placement options and the scroll areas for further columns
  341         ui->lYColumn->hide();
  342         ui->cbYColumn->hide();
  343         ui->rbCurvePlacement1->setChecked(true);
  344         ui->gbCurvePlacement->hide();
  345         ui->gbPlotPlacement->setTitle(i18n("Add Histogram to"));
  346         ui->scrollAreaColumns->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  347     } else {
  348         ui->gbCurvePlacement->setTitle(i18n("Histogram Placement"));
  349         ui->rbCurvePlacement1->setText(i18n("All histograms in one plot"));
  350         ui->rbCurvePlacement2->setText(i18n("One plot per histogram"));
  351         ui->gbPlotPlacement->setTitle(i18n("Add Histograms to"));
  352         ui->scrollAreaColumns->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
  353 
  354         //use the already available cbYColumn combo box
  355         ui->lYColumn->setText(i18n("Data"));
  356         m_columnComboBoxes << ui->cbYColumn;
  357         ui->cbYColumn->addItems(columnNames);
  358         ui->cbYColumn->setCurrentIndex(1);
  359 
  360         //add a ComboBox for every further column to be plotted
  361         auto* gridLayout = dynamic_cast<QGridLayout*>(ui->scrollAreaColumns->widget()->layout());
  362         for (int i = 2; i < m_columns.size(); ++i) {
  363             auto* label = new QLabel(i18n("Data"));
  364             auto* comboBox = new QComboBox();
  365             gridLayout->addWidget(label, i+1, 0, 1, 1);
  366             gridLayout->addWidget(comboBox, i+1, 2, 1, 1);
  367             comboBox->addItems(columnNames);
  368             comboBox->setCurrentIndex(i);
  369             m_columnComboBoxes << comboBox;
  370         }
  371     }
  372 }
  373 
  374 void PlotDataDialog::plot() {
  375     WAIT_CURSOR;
  376     m_spreadsheet->project()->setSuppressAspectAddedSignal(true);
  377     m_lastAddedCurve = nullptr;
  378 
  379     if (ui->rbPlotPlacement1->isChecked()) {
  380         //add curves to an existing plot
  381         auto* aspect = static_cast<AbstractAspect*>(cbExistingPlots->currentModelIndex().internalPointer());
  382         auto* plot = static_cast<CartesianPlot*>(aspect);
  383         plot->beginMacro( i18n("Plot - %1", m_spreadsheet->name()) );
  384         addCurvesToPlot(plot);
  385         plot->endMacro();
  386     } else if (ui->rbPlotPlacement2->isChecked()) {
  387         //add curves to a new plot in an existing worksheet
  388         auto* aspect = static_cast<AbstractAspect*>(cbExistingWorksheets->currentModelIndex().internalPointer());
  389         auto* worksheet = static_cast<Worksheet*>(aspect);
  390         worksheet->beginMacro( i18n("Worksheet - %1", m_spreadsheet->name()) );
  391 
  392         if (ui->rbCurvePlacement1->isChecked()) {
  393             //all curves in one plot
  394             CartesianPlot* plot = new CartesianPlot( i18n("Plot - %1", m_spreadsheet->name()) );
  395             plot->setType(CartesianPlot::Type::FourAxes);
  396 
  397             //set the axis titles before we add the plot to the worksheet
  398             //set the x-axis names
  399             const QString& xColumnName = ui->cbXColumn->currentText();
  400             for (auto* axis : plot->children<Axis>()) {
  401                 if (axis->orientation() == Axis::Orientation::Horizontal) {
  402                     axis->title()->setText(xColumnName);
  403                     break;
  404                 }
  405             }
  406 
  407             //if we only have one single y-column to plot, we can set the title of the y-axes
  408             if (m_columnComboBoxes.size() == 2) {
  409                 const QString& yColumnName = m_columnComboBoxes[1]->currentText();
  410                 for (auto* axis : plot->children<Axis>()) {
  411                     if (axis->orientation() == Axis::Orientation::Vertical) {
  412                         axis->title()->setText(yColumnName);
  413                         break;
  414                     }
  415                 }
  416             }
  417 
  418             worksheet->addChild(plot);
  419             addCurvesToPlot(plot);
  420         } else {
  421             //one plot per curve
  422             addCurvesToPlots(worksheet);
  423         }
  424         worksheet->endMacro();
  425     } else {
  426         //add curves to a new plot(s) in a new worksheet
  427         AbstractAspect* parent = m_spreadsheet->parentAspect();
  428         if (parent->type() == AspectType::DatapickerCurve)
  429             parent = parent->parentAspect()->parentAspect();
  430         else if (parent->type() == AspectType::Workbook)
  431             parent = parent->parentAspect();
  432 #ifdef HAVE_MQTT
  433         else if (dynamic_cast<MQTTTopic*>(m_spreadsheet))
  434             parent = m_spreadsheet->project();
  435 #endif
  436         parent->beginMacro( i18n("Plot data from %1", m_spreadsheet->name()) );
  437         Worksheet* worksheet = new Worksheet(i18n("Worksheet - %1", m_spreadsheet->name()));
  438         parent->addChild(worksheet);
  439 
  440         if (ui->rbCurvePlacement1->isChecked()) {
  441             //all curves in one plot
  442             CartesianPlot* plot = new CartesianPlot( i18n("Plot - %1", m_spreadsheet->name()) );
  443             plot->setType(CartesianPlot::Type::FourAxes);
  444 
  445             //set the axis titles before we add the plot to the worksheet
  446             //set the x-axis names
  447             const QString& xColumnName = ui->cbXColumn->currentText();
  448             for (auto* axis : plot->children<Axis>()) {
  449                 if (axis->orientation() == Axis::Orientation::Horizontal) {
  450                     axis->title()->setText(xColumnName);
  451                     break;
  452                 }
  453             }
  454 
  455             //if we only have one single y-column to plot, we can set the title of the y-axes
  456             if (m_columnComboBoxes.size() == 2) {
  457                 const QString& yColumnName = m_columnComboBoxes[1]->currentText();
  458                 for (auto* axis : plot->children<Axis>()) {
  459                     if (axis->orientation() == Axis::Orientation::Vertical) {
  460                         axis->title()->setText(yColumnName);
  461                         break;
  462                     }
  463                 }
  464             }
  465 
  466             worksheet->addChild(plot);
  467             addCurvesToPlot(plot);
  468         } else {
  469             //one plot per curve
  470             addCurvesToPlots(worksheet);
  471         }
  472 
  473         //when creating a new worksheet with many plots it can happen the place available for the data in the plot
  474         //is small and the result created here doesn't look nice. To avoid this and as a quick a dirty workaround,
  475         //we increase the size of the worksheet so the plots have a certain minimal size.
  476         //TODO:
  477         //A more sophisticated and better logic would require further adjustments for properties like plot area
  478         //paddings, the font size of axis ticks and title labels, etc. Also, this logic should be applied maybe if
  479         //we add plots to an already created worksheet.
  480 
  481         //adjust the sizes
  482         const auto layout = worksheet->layout();
  483         const auto plots = worksheet->children<CartesianPlot>();
  484         const int count = plots.size();
  485         const double minSize = 4.0;
  486         switch (layout) {
  487         case Worksheet::Layout::NoLayout:
  488         case Worksheet::Layout::VerticalLayout: {
  489             if (layout == Worksheet::Layout::NoLayout)
  490                 worksheet->setLayout(Worksheet::Layout::VerticalLayout);
  491 
  492             const auto plot = plots.constFirst();
  493             double height = Worksheet::convertFromSceneUnits(plot->rect().height(), Worksheet::Unit::Centimeter);
  494             if (height < 4.) {
  495                 double newHeight = worksheet->layoutTopMargin() + worksheet->layoutBottomMargin()
  496                             + (count - 1) * worksheet->layoutHorizontalSpacing()
  497                             + count * Worksheet::convertToSceneUnits(minSize, Worksheet::Unit::Centimeter);
  498                 QRectF newRect = worksheet->pageRect();
  499                 newRect.setHeight(round(newHeight));
  500                 worksheet->setPageRect(newRect);
  501             }
  502             break;
  503         }
  504         case Worksheet::Layout::HorizontalLayout: {
  505             const auto plot = plots.constFirst();
  506             double width = Worksheet::convertFromSceneUnits(plot->rect().width(), Worksheet::Unit::Centimeter);
  507             if (width < 4.) {
  508                 double newWidth = worksheet->layoutLeftMargin() + worksheet->layoutRightMargin()
  509                             + (count - 1) * worksheet->layoutVerticalSpacing()
  510                             + count * Worksheet::convertToSceneUnits(minSize, Worksheet::Unit::Centimeter);
  511                 QRectF newRect = worksheet->pageRect();
  512                 newRect.setWidth(round(newWidth));
  513                 worksheet->setPageRect(newRect);
  514             }
  515             break;
  516         }
  517         case Worksheet::Layout::GridLayout: {
  518             const auto plot = plots.constFirst();
  519             double width = Worksheet::convertFromSceneUnits(plot->rect().width(), Worksheet::Unit::Centimeter);
  520             double height = Worksheet::convertFromSceneUnits(plot->rect().height(), Worksheet::Unit::Centimeter);
  521             if (width < 4. || height < 4.) {
  522                 QRectF newRect = worksheet->pageRect();
  523                 if (height < 4.) {
  524                     double newHeight = worksheet->layoutTopMargin() + worksheet->layoutBottomMargin()
  525                                     + (count - 1) * worksheet->layoutHorizontalSpacing()
  526                                     + count * Worksheet::convertToSceneUnits(minSize, Worksheet::Unit::Centimeter);
  527                     newRect.setHeight(round(newHeight));
  528                 } else {
  529                     double newWidth = worksheet->layoutLeftMargin() + worksheet->layoutRightMargin()
  530                                 + (count - 1) * worksheet->layoutVerticalSpacing()
  531                                 + count * Worksheet::convertToSceneUnits(minSize, Worksheet::Unit::Centimeter);
  532                     newRect.setWidth(round(newWidth));
  533                 }
  534                 worksheet->setPageRect(newRect);
  535             }
  536             break;
  537         }
  538         }
  539 
  540         parent->endMacro();
  541     }
  542 
  543     //if more than two curves are produced, select the parent plot of the last added curve in the project explorer.
  544     //if a custom fit is being done, select the last created fit curve independent of the number of created curves.
  545     m_spreadsheet->project()->setSuppressAspectAddedSignal(false);
  546     if (m_lastAddedCurve) {
  547         QString path;
  548         if (!m_analysisMode) {
  549             if (m_columns.size() > 2)
  550                 path = m_lastAddedCurve->parentAspect()->path();
  551             else
  552                 path = m_lastAddedCurve->path();
  553         } else {
  554             if (m_analysisAction == AnalysisAction::FitCustom)
  555                 path = m_lastAddedCurve->path();
  556             else {
  557                 if (m_columns.size() > 2)
  558                     path = m_lastAddedCurve->parentAspect()->path();
  559                 else
  560                     path = m_lastAddedCurve->path();
  561             }
  562         }
  563 
  564         emit m_spreadsheet->project()->requestNavigateTo(path);
  565     }
  566     RESET_CURSOR;
  567 }
  568 
  569 Column* PlotDataDialog::columnFromName(const QString& name) const {
  570     for (auto* column : m_columns) {
  571         if (column->name() == name)
  572             return column;
  573     }
  574     return nullptr;
  575 }
  576 
  577 /*!
  578  * * for the selected columns in this dialog, creates a curve in the already existing plot \c plot.
  579  */
  580 void PlotDataDialog::addCurvesToPlot(CartesianPlot* plot) {
  581     QApplication::processEvents(QEventLoop::AllEvents, 100);
  582     switch (m_plotType) {
  583     case PlotType::XYCurve: {
  584         Column* xColumn = columnFromName(ui->cbXColumn->currentText());
  585         for (auto* comboBox : m_columnComboBoxes) {
  586             const QString& name = comboBox->currentText();
  587             Column* yColumn = columnFromName(name);
  588 
  589             //if only one column was selected, allow to use this column for x and for y.
  590             //otherwise, don't assign xColumn to y
  591             if (m_columns.size() > 1 && yColumn == xColumn)
  592                 continue;
  593 
  594             addCurve(name, xColumn, yColumn, plot);
  595         }
  596         break;
  597     }
  598     case PlotType::Histogram: {
  599         for (auto* comboBox : m_columnComboBoxes) {
  600             const QString& name = comboBox->currentText();
  601             Column* column = columnFromName(name);
  602             addHistogram(name, column, plot);
  603         }
  604         break;
  605     }
  606     }
  607 
  608     plot->dataChanged();
  609 }
  610 
  611 /*!
  612  * for the selected columns in this dialog, creates a plot and a curve in the already existing worksheet \c worksheet.
  613  */
  614 void PlotDataDialog::addCurvesToPlots(Worksheet* worksheet) {
  615     QApplication::processEvents(QEventLoop::AllEvents, 100);
  616     worksheet->setSuppressLayoutUpdate(true);
  617 
  618     switch (m_plotType) {
  619     case PlotType::XYCurve: {
  620         const QString& xColumnName = ui->cbXColumn->currentText();
  621         Column* xColumn = columnFromName(xColumnName);
  622         for (auto* comboBox : m_columnComboBoxes) {
  623             const QString& name = comboBox->currentText();
  624             Column* yColumn = columnFromName(name);
  625             if (yColumn == xColumn)
  626                 continue;
  627 
  628             CartesianPlot* plot = new CartesianPlot(i18n("Plot %1", name));
  629             plot->setType(CartesianPlot::Type::FourAxes);
  630 
  631             //set the axis names in the new plot
  632             bool xSet = false;
  633             bool ySet = false;
  634             for (auto* axis : plot->children<Axis>()) {
  635                 if (axis->orientation() == Axis::Orientation::Horizontal && !xSet) {
  636                     axis->title()->setText(xColumnName);
  637                     xSet = true;
  638                 } else if (axis->orientation() == Axis::Orientation::Vertical && !ySet) {
  639                     axis->title()->setText(name);
  640                     ySet = true;
  641                 }
  642             }
  643 
  644             worksheet->addChild(plot);
  645             addCurve(name, xColumn, yColumn, plot);
  646             plot->scaleAuto();
  647         }
  648         break;
  649     }
  650     case PlotType::Histogram: {
  651         for (auto* comboBox : m_columnComboBoxes) {
  652             const QString& name = comboBox->currentText();
  653             Column* column = columnFromName(name);
  654 
  655             CartesianPlot* plot = new CartesianPlot(i18n("Plot %1", name));
  656             plot->setType(CartesianPlot::Type::FourAxes);
  657 
  658             //set the axis names in the new plot
  659             bool xSet = false;
  660             for (auto* axis : plot->children<Axis>()) {
  661                 if (axis->orientation() == Axis::Orientation::Horizontal && !xSet) {
  662                     axis->title()->setText(name);
  663                     xSet = true;
  664                 }
  665             }
  666 
  667             worksheet->addChild(plot);
  668             addHistogram(name, column, plot);
  669             plot->scaleAuto();
  670         }
  671     }
  672     }
  673 
  674     worksheet->setSuppressLayoutUpdate(false);
  675     worksheet->updateLayout();
  676 }
  677 
  678 /*!
  679  * helper function that does the actual creation of the curve and adding it as child to the \c plot.
  680  */
  681 void PlotDataDialog::addCurve(const QString& name, Column* xColumn, Column* yColumn, CartesianPlot* plot) {
  682     if (!m_analysisMode) {
  683         auto* curve = new XYCurve(name);
  684         curve->suppressRetransform(true);
  685         curve->setXColumn(xColumn);
  686         curve->setYColumn(yColumn);
  687         curve->suppressRetransform(false);
  688         plot->addChild(curve);
  689         m_lastAddedCurve = curve;
  690     } else {
  691         bool createDataCurve = ui->chkCreateDataCurve->isChecked();
  692         XYCurve* curve = nullptr;
  693         if (createDataCurve) {
  694             curve = new XYCurve(name);
  695             curve->suppressRetransform(true);
  696             curve->setXColumn(xColumn);
  697             curve->setYColumn(yColumn);
  698             curve->suppressRetransform(false);
  699             plot->addChild(curve);
  700             m_lastAddedCurve = curve;
  701         }
  702 
  703         XYAnalysisCurve* analysisCurve = nullptr;
  704         switch (m_analysisAction) {
  705             case AnalysisAction::DataReduction:
  706                 analysisCurve = new XYDataReductionCurve(i18n("Reduction of '%1'", name));
  707                 break;
  708             case AnalysisAction::Differentiation:
  709                 analysisCurve = new XYDifferentiationCurve(i18n("Derivative of '%1'", name));
  710                 break;
  711             case AnalysisAction::Integration:
  712                 analysisCurve = new XYIntegrationCurve(i18n("Integral of '%1'", name));
  713                 break;
  714             case AnalysisAction::Interpolation:
  715                 analysisCurve = new XYInterpolationCurve(i18n("Interpolation of '%1'", name));
  716                 break;
  717             case AnalysisAction::Smoothing:
  718                 analysisCurve = new XYSmoothCurve(i18n("Smoothing of '%1'", name));
  719                 break;
  720             case AnalysisAction::FitLinear:
  721             case AnalysisAction::FitPower:
  722             case AnalysisAction::FitExp1:
  723             case AnalysisAction::FitExp2:
  724             case AnalysisAction::FitInvExp:
  725             case AnalysisAction::FitGauss:
  726             case AnalysisAction::FitCauchyLorentz:
  727             case AnalysisAction::FitTan:
  728             case AnalysisAction::FitTanh:
  729             case AnalysisAction::FitErrFunc:
  730             case AnalysisAction::FitCustom:
  731                 analysisCurve = new XYFitCurve(i18n("Fit to '%1'", name));
  732                 static_cast<XYFitCurve*>(analysisCurve)->initFitData(m_analysisAction);
  733                 static_cast<XYFitCurve*>(analysisCurve)->initStartValues(curve);
  734                 break;
  735             case AnalysisAction::FourierFilter:
  736                 analysisCurve = new XYFourierFilterCurve(i18n("Fourier Filter of '%1'", name));
  737                 break;
  738         }
  739 
  740         if (analysisCurve != nullptr) {
  741             analysisCurve->suppressRetransform(true);
  742             analysisCurve->setXDataColumn(xColumn);
  743             analysisCurve->setYDataColumn(yColumn);
  744             if (m_analysisAction != AnalysisAction::FitCustom) //no custom fit-model set yet, no need to recalculate
  745                 analysisCurve->recalculate();
  746             analysisCurve->suppressRetransform(false);
  747             plot->addChild(analysisCurve);
  748             m_lastAddedCurve = analysisCurve;
  749         }
  750     }
  751 }
  752 
  753 void PlotDataDialog::addHistogram(const QString& name, Column* column, CartesianPlot* plot) {
  754     auto* hist = new Histogram(name);
  755 //  hist->suppressRetransform(true);
  756     hist->setDataColumn(column);
  757 //  hist->suppressRetransform(false);
  758     plot->addChild(hist);
  759     m_lastAddedCurve = hist;
  760 }
  761 
  762 //################################################################
  763 //########################## Slots ###############################
  764 //################################################################
  765 void PlotDataDialog::curvePlacementChanged() {
  766     if (ui->rbCurvePlacement1->isChecked()) {
  767         ui->rbPlotPlacement1->setEnabled(true);
  768         ui->rbPlotPlacement2->setText(i18n("new plot in an existing worksheet"));
  769         ui->rbPlotPlacement3->setText(i18n("new plot in a new worksheet"));
  770     } else {
  771         ui->rbPlotPlacement1->setEnabled(false);
  772         if (ui->rbPlotPlacement1->isChecked())
  773             ui->rbPlotPlacement2->setChecked(true);
  774         ui->rbPlotPlacement2->setText(i18n("new plots in an existing worksheet"));
  775         ui->rbPlotPlacement3->setText(i18n("new plots in a new worksheet"));
  776     }
  777 }
  778 
  779 void PlotDataDialog::plotPlacementChanged() {
  780     if (ui->rbPlotPlacement1->isChecked()) {
  781         cbExistingPlots->setEnabled(true);
  782         cbExistingWorksheets->setEnabled(false);
  783     } else if (ui->rbPlotPlacement2->isChecked()) {
  784         cbExistingPlots->setEnabled(false);
  785         cbExistingWorksheets->setEnabled(true);
  786     } else {
  787         cbExistingPlots->setEnabled(false);
  788         cbExistingWorksheets->setEnabled(false);
  789     }
  790 
  791     checkOkButton();
  792 }
  793 
  794 void PlotDataDialog::checkOkButton() {
  795     bool enable = false;
  796     QString msg;
  797     if ( (m_plotType == PlotType::XYCurve && (ui->cbXColumn->currentIndex() == -1 || ui->cbYColumn->currentIndex() == -1)) ||
  798         (m_plotType == PlotType::Histogram && ui->cbXColumn->currentIndex() == -1) )
  799         msg = i18n("No data selected to plot.");
  800     else if (ui->rbPlotPlacement1->isChecked()) {
  801         AbstractAspect* aspect = static_cast<AbstractAspect*>(cbExistingPlots->currentModelIndex().internalPointer());
  802         enable = (aspect != nullptr);
  803         if (!enable)
  804             msg = i18n("An already existing plot has to be selected.");
  805     } else if (ui->rbPlotPlacement2->isChecked()) {
  806         AbstractAspect* aspect = static_cast<AbstractAspect*>(cbExistingWorksheets->currentModelIndex().internalPointer());
  807         enable = (aspect != nullptr);
  808         if (!enable)
  809             msg = i18n("An already existing worksheet has to be selected.");
  810     } else
  811         enable = true;
  812 
  813     m_okButton->setEnabled(enable);
  814     if (enable)
  815         m_okButton->setToolTip(i18n("Close the dialog and plot the data."));
  816     else
  817         m_okButton->setToolTip(msg);
  818 }