File indexing completed on 2025-01-26 03:34:13
0001 /* 0002 File : QQPlot.cpp 0003 Project : LabPlot 0004 Description : QQPlot 0005 -------------------------------------------------------------------- 0006 SPDX-FileCopyrightText: 2023 Alexander Semke <alexander.semke@web.de> 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 /*! 0011 \class QQPlot 0012 \brief 0013 0014 \ingroup worksheet 0015 */ 0016 #include "QQPlot.h" 0017 #include "QQPlotPrivate.h" 0018 #include "backend/core/column/Column.h" 0019 #include "backend/lib/XmlStreamReader.h" 0020 #include "backend/lib/commandtemplates.h" 0021 #include "backend/lib/macrosCurve.h" 0022 #include "backend/lib/trace.h" 0023 #include "backend/worksheet/Background.h" 0024 #include "backend/worksheet/Line.h" 0025 #include "backend/worksheet/plots/cartesian/Symbol.h" 0026 0027 #include <gsl/gsl_cdf.h> 0028 #include <gsl/gsl_statistics.h> 0029 0030 #include <QMenu> 0031 #include <QPainter> 0032 0033 #include <KConfig> 0034 #include <KConfigGroup> 0035 #include <KLocalizedString> 0036 #include <KSharedConfig> 0037 0038 CURVE_COLUMN_CONNECT(QQPlot, Data, data, recalc) 0039 0040 QQPlot::QQPlot(const QString& name) 0041 : Plot(name, new QQPlotPrivate(this), AspectType::QQPlot) { 0042 init(); 0043 } 0044 0045 QQPlot::QQPlot(const QString& name, QQPlotPrivate* dd) 0046 : Plot(name, dd, AspectType::QQPlot) { 0047 init(); 0048 } 0049 0050 // no need to delete the d-pointer here - it inherits from QGraphicsItem 0051 // and is deleted during the cleanup in QGraphicsScene 0052 QQPlot::~QQPlot() = default; 0053 0054 void QQPlot::init() { 0055 Q_D(QQPlot); 0056 0057 // setUndoAware(false); 0058 0059 KConfig config; 0060 KConfigGroup group = config.group(QStringLiteral("QQPlot")); 0061 // reference curve - line conneting two central quantiles Q1 and Q3 0062 d->referenceCurve = new XYCurve(QString()); 0063 d->referenceCurve->setName(name(), AbstractAspect::NameHandling::UniqueNotRequired); 0064 d->referenceCurve->setHidden(true); 0065 d->referenceCurve->graphicsItem()->setParentItem(d); 0066 d->referenceCurve->line()->init(group); 0067 d->referenceCurve->line()->setStyle(Qt::SolidLine); 0068 d->referenceCurve->symbol()->setStyle(Symbol::Style::NoSymbols); 0069 d->referenceCurve->background()->setPosition(Background::Position::No); 0070 0071 // columns holding the data for the reference curve 0072 d->xReferenceColumn = new Column(QStringLiteral("xReference")); 0073 d->xReferenceColumn->setHidden(true); 0074 d->xReferenceColumn->setUndoAware(false); 0075 addChildFast(d->xReferenceColumn); 0076 d->referenceCurve->setXColumn(d->xReferenceColumn); 0077 0078 d->yReferenceColumn = new Column(QStringLiteral("yReference")); 0079 d->yReferenceColumn->setHidden(true); 0080 d->yReferenceColumn->setUndoAware(false); 0081 addChildFast(d->yReferenceColumn); 0082 d->referenceCurve->setYColumn(d->yReferenceColumn); 0083 0084 // percentiles curve 0085 d->percentilesCurve = new XYCurve(QString()); 0086 d->percentilesCurve->setName(name(), AbstractAspect::NameHandling::UniqueNotRequired); 0087 d->percentilesCurve->setHidden(true); 0088 d->percentilesCurve->graphicsItem()->setParentItem(d); 0089 d->percentilesCurve->symbol()->init(group); 0090 d->percentilesCurve->symbol()->setStyle(Symbol::Style::Circle); 0091 d->percentilesCurve->line()->setStyle(Qt::NoPen); 0092 d->percentilesCurve->background()->setPosition(Background::Position::No); 0093 0094 // columns holding the data for the percentiles curve 0095 d->xPercentilesColumn = new Column(QStringLiteral("xPercentiles")); 0096 d->xPercentilesColumn->setHidden(true); 0097 d->xPercentilesColumn->setUndoAware(false); 0098 addChildFast(d->xPercentilesColumn); 0099 d->percentilesCurve->setXColumn(d->xPercentilesColumn); 0100 0101 d->yPercentilesColumn = new Column(QStringLiteral("yPercentiles")); 0102 d->yPercentilesColumn->setHidden(true); 0103 d->yPercentilesColumn->setUndoAware(false); 0104 addChildFast(d->yPercentilesColumn); 0105 d->percentilesCurve->setYColumn(d->yPercentilesColumn); 0106 0107 d->updateDistribution(); 0108 0109 // synchronize the names of the internal XYCurves with the name of the current q-q plot 0110 // so we have the same name shown on the undo stack 0111 connect(this, &AbstractAspect::aspectDescriptionChanged, [this] { 0112 Q_D(QQPlot); 0113 d->referenceCurve->setName(name(), AbstractAspect::NameHandling::UniqueNotRequired); 0114 d->percentilesCurve->setName(name(), AbstractAspect::NameHandling::UniqueNotRequired); 0115 }); 0116 } 0117 0118 void QQPlot::finalizeAdd() { 0119 Q_D(QQPlot); 0120 WorksheetElement::finalizeAdd(); 0121 addChildFast(d->referenceCurve); 0122 addChildFast(d->percentilesCurve); 0123 } 0124 0125 /*! 0126 Returns an icon to be used in the project explorer. 0127 */ 0128 QIcon QQPlot::icon() const { 0129 return QIcon::fromTheme(QStringLiteral("view-object-histogram-linear")); 0130 } 0131 0132 void QQPlot::handleResize(double /*horizontalRatio*/, double /*verticalRatio*/, bool /*pageResize*/) { 0133 // TODO 0134 } 0135 0136 void QQPlot::setVisible(bool on) { 0137 Q_D(QQPlot); 0138 beginMacro(on ? i18n("%1: set visible", name()) : i18n("%1: set invisible", name())); 0139 d->referenceCurve->setVisible(on); 0140 d->percentilesCurve->setVisible(on); 0141 WorksheetElement::setVisible(on); 0142 endMacro(); 0143 } 0144 0145 // ############################################################################## 0146 // ########################## getter methods ################################## 0147 // ############################################################################## 0148 // general 0149 BASIC_SHARED_D_READER_IMPL(QQPlot, const AbstractColumn*, dataColumn, dataColumn) 0150 BASIC_SHARED_D_READER_IMPL(QQPlot, QString, dataColumnPath, dataColumnPath) 0151 BASIC_SHARED_D_READER_IMPL(QQPlot, nsl_sf_stats_distribution, distribution, distribution) 0152 0153 // line 0154 Line* QQPlot::line() const { 0155 Q_D(const QQPlot); 0156 return d->referenceCurve->line(); 0157 } 0158 0159 // symbols 0160 Symbol* QQPlot::symbol() const { 0161 Q_D(const QQPlot); 0162 return d->percentilesCurve->symbol(); 0163 } 0164 0165 bool QQPlot::minMax(const Dimension dim, const Range<int>& indexRange, Range<double>& r, bool /* includeErrorBars */) const { 0166 Q_D(const QQPlot); 0167 0168 switch (dim) { 0169 case Dimension::X: 0170 return d->referenceCurve->minMax(dim, indexRange, r, false); 0171 case Dimension::Y: { 0172 Range referenceRange(r); 0173 Range percentilesRange(r); 0174 bool rc = true; 0175 rc = d->referenceCurve->minMax(dim, indexRange, referenceRange, false); 0176 if (!rc) 0177 return false; 0178 0179 rc = d->percentilesCurve->minMax(dim, indexRange, percentilesRange, false); 0180 if (!rc) 0181 return false; 0182 0183 r.setStart(std::min(referenceRange.start(), percentilesRange.start())); 0184 r.setEnd(std::max(referenceRange.end(), percentilesRange.end())); 0185 return true; 0186 } 0187 } 0188 return false; 0189 } 0190 0191 double QQPlot::minimum(const Dimension dim) const { 0192 Q_D(const QQPlot); 0193 switch (dim) { 0194 case Dimension::X: 0195 return d->referenceCurve->minimum(dim); 0196 case Dimension::Y: 0197 return std::min(d->referenceCurve->minimum(dim), d->percentilesCurve->minimum(dim)); 0198 } 0199 return NAN; 0200 } 0201 0202 double QQPlot::maximum(const Dimension dim) const { 0203 Q_D(const QQPlot); 0204 switch (dim) { 0205 case Dimension::X: 0206 return d->referenceCurve->maximum(dim); 0207 case Dimension::Y: 0208 return std::max(d->referenceCurve->maximum(dim), d->percentilesCurve->maximum(dim)); 0209 } 0210 return NAN; 0211 } 0212 0213 bool QQPlot::hasData() const { 0214 Q_D(const QQPlot); 0215 return (d->dataColumn != nullptr); 0216 } 0217 0218 bool QQPlot::usingColumn(const Column* column) const { 0219 Q_D(const QQPlot); 0220 return (d->dataColumn == column); 0221 } 0222 0223 void QQPlot::updateColumnDependencies(const AbstractColumn* column) { 0224 Q_D(QQPlot); 0225 const QString& columnPath = column->path(); 0226 0227 if (d->dataColumn == column) // the column is the same and was just renamed -> update the column path 0228 d->dataColumnPath = columnPath; 0229 else if (d->dataColumnPath == columnPath) { // another column was renamed to the current path -> set and connect to the new column 0230 setUndoAware(false); 0231 setDataColumn(column); 0232 setUndoAware(true); 0233 } 0234 } 0235 0236 QColor QQPlot::color() const { 0237 Q_D(const QQPlot); 0238 return d->percentilesCurve->color(); 0239 } 0240 0241 // ############################################################################## 0242 // ################# setter methods and undo commands ########################## 0243 // ############################################################################## 0244 0245 // General 0246 CURVE_COLUMN_SETTER_CMD_IMPL_F_S(QQPlot, Data, data, recalc) 0247 void QQPlot::setDataColumn(const AbstractColumn* column) { 0248 Q_D(QQPlot); 0249 if (column != d->dataColumn) 0250 exec(new QQPlotSetDataColumnCmd(d, column, ki18n("%1: set data column"))); 0251 } 0252 0253 void QQPlot::setDataColumnPath(const QString& path) { 0254 Q_D(QQPlot); 0255 d->dataColumnPath = path; 0256 } 0257 0258 STD_SETTER_CMD_IMPL_F_S(QQPlot, SetDistribution, nsl_sf_stats_distribution, distribution, updateDistribution) 0259 void QQPlot::setDistribution(nsl_sf_stats_distribution distribution) { 0260 Q_D(QQPlot); 0261 if (distribution != d->distribution) 0262 exec(new QQPlotSetDistributionCmd(d, distribution, ki18n("%1: set distribution"))); 0263 } 0264 0265 // ############################################################################## 0266 // ################################# SLOTS #################################### 0267 // ############################################################################## 0268 void QQPlot::retransform() { 0269 D(QQPlot); 0270 d->retransform(); 0271 } 0272 0273 void QQPlot::recalc() { 0274 D(QQPlot); 0275 d->recalc(); 0276 } 0277 0278 void QQPlot::dataColumnAboutToBeRemoved(const AbstractAspect* aspect) { 0279 Q_D(QQPlot); 0280 if (aspect == d->dataColumn) { 0281 d->dataColumn = nullptr; 0282 d->retransform(); 0283 } 0284 } 0285 0286 // ############################################################################## 0287 // ######################### Private implementation ############################# 0288 // ############################################################################## 0289 QQPlotPrivate::QQPlotPrivate(QQPlot* owner) 0290 : PlotPrivate(owner) 0291 , q(owner) { 0292 setFlag(QGraphicsItem::ItemIsSelectable, true); 0293 setAcceptHoverEvents(false); 0294 } 0295 0296 QQPlotPrivate::~QQPlotPrivate() { 0297 } 0298 0299 /*! 0300 called when the size of the plot or its data ranges (manual changes, zooming, etc.) were changed. 0301 recalculates the position of the scene points to be drawn. 0302 triggers the update of lines, drop lines, symbols etc. 0303 */ 0304 void QQPlotPrivate::retransform() { 0305 const bool suppressed = suppressRetransform || q->isLoading(); 0306 if (suppressed) 0307 return; 0308 0309 if (!isVisible()) 0310 return; 0311 0312 PERFTRACE(name() + QLatin1String(Q_FUNC_INFO)); 0313 referenceCurve->retransform(); 0314 percentilesCurve->retransform(); 0315 recalcShapeAndBoundingRect(); 0316 } 0317 0318 /*! 0319 * called when the source data was changed, recalculates the plot. 0320 */ 0321 void QQPlotPrivate::recalc() { 0322 PERFTRACE(name() + QLatin1String(Q_FUNC_INFO)); 0323 0324 if (!dataColumn) { 0325 yPercentilesColumn->clear(); 0326 Q_EMIT q->dataChanged(); 0327 return; 0328 } 0329 0330 // copy the non-nan and not masked values into a new vector 0331 QVector<double> rawData; 0332 copyValidData(rawData); 0333 size_t n = rawData.count(); 0334 0335 // sort the data to calculate the percentiles 0336 std::sort(rawData.begin(), rawData.end()); 0337 0338 // calculate y-values - the percentiles for the column data 0339 QVector<double> yData; 0340 for (int i = 1; i < 100; ++i) 0341 yData << gsl_stats_quantile_from_sorted_data(rawData.data(), 1, n, double(i) / 100.); 0342 0343 yPercentilesColumn->replaceValues(0, yData); 0344 0345 double y1 = gsl_stats_quantile_from_sorted_data(rawData.data(), 1, n, 0.01); 0346 double y2 = gsl_stats_quantile_from_sorted_data(rawData.data(), 1, n, 0.99); 0347 yReferenceColumn->setValueAt(0, y1); 0348 yReferenceColumn->setValueAt(1, y2); 0349 0350 // Q_EMIT dataChanged() in order to retransform everything with the new size/shape of the plot 0351 Q_EMIT q->dataChanged(); 0352 } 0353 0354 /*! 0355 * called when the distribution was changed, recalculates everything that depends on 0356 * the distribution only and doesn't dependent on the source data 0357 */ 0358 void QQPlotPrivate::updateDistribution() { 0359 QVector<double> xData; 0360 double x1 = 0.; 0361 double x2 = 0.; 0362 0363 // handle all distributions where the inverse of the CDF is available in gsl_cdf.h 0364 switch (distribution) { 0365 case nsl_sf_stats_gaussian: { 0366 x1 = gsl_cdf_gaussian_Pinv(0.01, 1.0); 0367 x2 = gsl_cdf_gaussian_Pinv(0.99, 1.0); 0368 for (int i = 1; i < 100; ++i) 0369 xData << gsl_cdf_gaussian_Pinv(double(i) / 100., 1.0); 0370 break; 0371 } 0372 case nsl_sf_stats_exponential: { 0373 x1 = gsl_cdf_exponential_Pinv(0.01, 1.0); 0374 x2 = gsl_cdf_exponential_Pinv(0.99, 1.0); 0375 for (int i = 1; i < 100; ++i) 0376 xData << gsl_cdf_exponential_Pinv(double(i) / 100., 1.0); 0377 break; 0378 } 0379 case nsl_sf_stats_laplace: { 0380 x1 = gsl_cdf_laplace_Pinv(0.01, 1.0); 0381 x2 = gsl_cdf_laplace_Pinv(0.99, 1.0); 0382 for (int i = 1; i < 100; ++i) 0383 xData << gsl_cdf_laplace_Pinv(double(i) / 100., 1.0); 0384 break; 0385 } 0386 case nsl_sf_stats_cauchy_lorentz: { 0387 x1 = gsl_cdf_cauchy_Pinv(0.01, 1.0); 0388 x2 = gsl_cdf_cauchy_Pinv(0.99, 1.0); 0389 for (int i = 1; i < 100; ++i) 0390 xData << gsl_cdf_cauchy_Pinv(double(i) / 100., 1.0); 0391 break; 0392 } 0393 case nsl_sf_stats_rayleigh: { 0394 x1 = gsl_cdf_rayleigh_Pinv(0.01, 1.0); 0395 x2 = gsl_cdf_rayleigh_Pinv(0.99, 1.0); 0396 for (int i = 1; i < 100; ++i) 0397 xData << gsl_cdf_rayleigh_Pinv(double(i) / 100., 1.0); 0398 break; 0399 } 0400 case nsl_sf_stats_gamma: { 0401 x1 = gsl_cdf_gamma_Pinv(0.01, 1.0, 1.0); 0402 x2 = gsl_cdf_gamma_Pinv(0.99, 1.0, 1.0); 0403 for (int i = 1; i < 100; ++i) 0404 xData << gsl_cdf_gamma_Pinv(double(i) / 100., 1.0, 1.0); 0405 break; 0406 } 0407 case nsl_sf_stats_flat: { 0408 x1 = gsl_cdf_flat_Pinv(0.01, 0.0, 1.0); 0409 x2 = gsl_cdf_flat_Pinv(0.99, 0.0, 1.0); 0410 for (int i = 1; i < 100; ++i) 0411 xData << gsl_cdf_flat_Pinv(double(i) / 100., 0.0, 1.0); 0412 break; 0413 } 0414 case nsl_sf_stats_lognormal: { 0415 x1 = gsl_cdf_lognormal_Pinv(0.01, 1.0, 1.0); 0416 x2 = gsl_cdf_lognormal_Pinv(0.99, 1.0, 1.0); 0417 for (int i = 1; i < 100; ++i) 0418 xData << gsl_cdf_lognormal_Pinv(double(i) / 100., 1.0, 1.0); 0419 break; 0420 } 0421 case nsl_sf_stats_chi_squared: { 0422 x1 = gsl_cdf_chisq_Pinv(0.01, 1.0); 0423 x2 = gsl_cdf_chisq_Pinv(0.99, 1.0); 0424 for (int i = 1; i < 100; ++i) 0425 xData << gsl_cdf_chisq_Pinv(double(i) / 100., 1.0); 0426 break; 0427 } 0428 case nsl_sf_stats_fdist: { 0429 x1 = gsl_cdf_fdist_Pinv(0.01, 1.0, 1.0); 0430 x2 = gsl_cdf_fdist_Pinv(0.99, 1.0, 1.0); 0431 for (int i = 1; i < 100; ++i) 0432 xData << gsl_cdf_fdist_Pinv(double(i) / 100., 1.0, 1.0); 0433 break; 0434 } 0435 case nsl_sf_stats_tdist: { 0436 x1 = gsl_cdf_tdist_Pinv(0.01, 1.0); 0437 x2 = gsl_cdf_tdist_Pinv(0.99, 1.0); 0438 for (int i = 1; i < 100; ++i) 0439 xData << gsl_cdf_tdist_Pinv(double(i) / 100., 1.0); 0440 break; 0441 } 0442 case nsl_sf_stats_beta: { 0443 x1 = gsl_cdf_beta_Pinv(0.01, 1.0, 1.0); 0444 x2 = gsl_cdf_beta_Pinv(0.99, 1.0, 1.0); 0445 for (int i = 1; i < 100; ++i) 0446 xData << gsl_cdf_beta_Pinv(double(i) / 100., 1.0, 1.0); 0447 break; 0448 } 0449 case nsl_sf_stats_logistic: { 0450 x1 = gsl_cdf_logistic_Pinv(0.01, 1.0); 0451 x2 = gsl_cdf_logistic_Pinv(0.99, 1.0); 0452 for (int i = 1; i < 100; ++i) 0453 xData << gsl_cdf_logistic_Pinv(double(i) / 100., 1.0); 0454 break; 0455 } 0456 case nsl_sf_stats_pareto: { 0457 x1 = gsl_cdf_pareto_Pinv(0.01, 1.0, 1.0); 0458 x2 = gsl_cdf_pareto_Pinv(0.99, 1.0, 1.0); 0459 for (int i = 1; i < 100; ++i) 0460 xData << gsl_cdf_pareto_Pinv(double(i) / 100., 1.0, 1.0); 0461 break; 0462 } 0463 case nsl_sf_stats_weibull: { 0464 x1 = gsl_cdf_weibull_Pinv(0.01, 1.0, 1.0); 0465 x2 = gsl_cdf_weibull_Pinv(0.99, 1.0, 1.0); 0466 for (int i = 1; i < 100; ++i) 0467 xData << gsl_cdf_weibull_Pinv(double(i) / 100., 1.0, 1.0); 0468 break; 0469 } 0470 case nsl_sf_stats_gumbel1: { 0471 x1 = gsl_cdf_gumbel1_Pinv(0.01, 1.0, 1.0); 0472 x2 = gsl_cdf_gumbel1_Pinv(0.99, 1.0, 1.0); 0473 for (int i = 1; i < 100; ++i) 0474 xData << gsl_cdf_gumbel1_Pinv(double(i) / 100., 1.0, 1.0); 0475 break; 0476 } 0477 case nsl_sf_stats_gumbel2: { 0478 x1 = gsl_cdf_gumbel2_Pinv(0.01, 1.0, 1.0); 0479 x2 = gsl_cdf_gumbel2_Pinv(0.99, 1.0, 1.0); 0480 for (int i = 1; i < 100; ++i) 0481 xData << gsl_cdf_gumbel2_Pinv(double(i) / 100., 1.0, 1.0); 0482 break; 0483 } 0484 case nsl_sf_stats_gaussian_tail: 0485 case nsl_sf_stats_exponential_power: 0486 case nsl_sf_stats_rayleigh_tail: 0487 case nsl_sf_stats_landau: 0488 case nsl_sf_stats_levy_alpha_stable: 0489 case nsl_sf_stats_levy_skew_alpha_stable: 0490 case nsl_sf_stats_poisson: 0491 case nsl_sf_stats_bernoulli: 0492 case nsl_sf_stats_binomial: 0493 case nsl_sf_stats_negative_binomial: 0494 case nsl_sf_stats_pascal: 0495 case nsl_sf_stats_geometric: 0496 case nsl_sf_stats_hypergeometric: 0497 case nsl_sf_stats_logarithmic: 0498 case nsl_sf_stats_maxwell_boltzmann: 0499 case nsl_sf_stats_sech: 0500 case nsl_sf_stats_levy: 0501 case nsl_sf_stats_frechet: 0502 break; 0503 } 0504 0505 xReferenceColumn->setValueAt(0, x1); 0506 xReferenceColumn->setValueAt(1, x2); 0507 0508 xPercentilesColumn->replaceValues(0, xData); 0509 0510 // Q_EMIT dataChanged() in order to retransform everything with the new size/shape of the plot 0511 Q_EMIT q->dataChanged(); 0512 } 0513 0514 /*! 0515 * copy the non-nan and not masked values of the current column 0516 * into the vector \c data. 0517 */ 0518 void QQPlotPrivate::copyValidData(QVector<double>& data) const { 0519 const int rowCount = dataColumn->rowCount(); 0520 data.reserve(rowCount); 0521 double val; 0522 if (dataColumn->columnMode() == AbstractColumn::ColumnMode::Double) { 0523 auto* rowValues = reinterpret_cast<QVector<double>*>(reinterpret_cast<const Column*>(dataColumn)->data()); 0524 for (int row = 0; row < rowCount; ++row) { 0525 val = rowValues->value(row); 0526 if (std::isnan(val) || dataColumn->isMasked(row)) 0527 continue; 0528 0529 data.push_back(val); 0530 } 0531 } else if (dataColumn->columnMode() == AbstractColumn::ColumnMode::Integer) { 0532 auto* rowValues = reinterpret_cast<QVector<int>*>(reinterpret_cast<const Column*>(dataColumn)->data()); 0533 for (int row = 0; row < rowCount; ++row) { 0534 val = rowValues->value(row); 0535 if (std::isnan(val) || dataColumn->isMasked(row)) 0536 continue; 0537 0538 data.push_back(val); 0539 } 0540 } else if (dataColumn->columnMode() == AbstractColumn::ColumnMode::BigInt) { 0541 auto* rowValues = reinterpret_cast<QVector<qint64>*>(reinterpret_cast<const Column*>(dataColumn)->data()); 0542 for (int row = 0; row < rowCount; ++row) { 0543 val = rowValues->value(row); 0544 if (std::isnan(val) || dataColumn->isMasked(row)) 0545 continue; 0546 0547 data.push_back(val); 0548 } 0549 } 0550 0551 if (data.size() < rowCount) 0552 data.squeeze(); 0553 } 0554 0555 /*! 0556 recalculates the outer bounds and the shape of the curve. 0557 */ 0558 void QQPlotPrivate::recalcShapeAndBoundingRect() { 0559 if (suppressRecalc) 0560 return; 0561 0562 prepareGeometryChange(); 0563 m_shape = QPainterPath(); 0564 m_shape.addPath(referenceCurve->graphicsItem()->shape()); 0565 m_shape.addPath(percentilesCurve->graphicsItem()->shape()); 0566 0567 m_boundingRectangle = m_shape.boundingRect(); 0568 } 0569 0570 // ############################################################################## 0571 // ################## Serialization/Deserialization ########################### 0572 // ############################################################################## 0573 //! Save as XML 0574 void QQPlot::save(QXmlStreamWriter* writer) const { 0575 Q_D(const QQPlot); 0576 0577 writer->writeStartElement(QStringLiteral("QQPlot")); 0578 writeBasicAttributes(writer); 0579 writeCommentElement(writer); 0580 0581 // general 0582 writer->writeStartElement(QStringLiteral("general")); 0583 WRITE_COLUMN(d->dataColumn, dataColumn); 0584 WRITE_COLUMN(d->xReferenceColumn, xReferenceColumn); 0585 WRITE_COLUMN(d->yReferenceColumn, yReferenceColumn); 0586 WRITE_COLUMN(d->xPercentilesColumn, xPercentilesColumn); 0587 WRITE_COLUMN(d->yPercentilesColumn, yPercentilesColumn); 0588 writer->writeAttribute(QStringLiteral("distribution"), QString::number(static_cast<int>(d->distribution))); 0589 writer->writeAttribute(QStringLiteral("visible"), QString::number(d->isVisible())); 0590 writer->writeAttribute(QStringLiteral("legendVisible"), QString::number(d->legendVisible)); 0591 writer->writeEndElement(); 0592 0593 // save the internal columns, above only the references to them were saved 0594 d->xReferenceColumn->save(writer); 0595 d->yReferenceColumn->save(writer); 0596 d->xPercentilesColumn->save(writer); 0597 d->yPercentilesColumn->save(writer); 0598 0599 // save the internal curves 0600 d->referenceCurve->save(writer); 0601 d->percentilesCurve->save(writer); 0602 0603 writer->writeEndElement(); // close "QQPlot" section 0604 } 0605 0606 //! Load from XML 0607 bool QQPlot::load(XmlStreamReader* reader, bool preview) { 0608 Q_D(QQPlot); 0609 0610 if (!readBasicAttributes(reader)) 0611 return false; 0612 0613 QXmlStreamAttributes attribs; 0614 QString str; 0615 0616 while (!reader->atEnd()) { 0617 reader->readNext(); 0618 if (reader->isEndElement() && reader->name() == QLatin1String("QQPlot")) 0619 break; 0620 0621 if (!reader->isStartElement()) 0622 continue; 0623 0624 if (reader->name() == QLatin1String("comment")) { 0625 if (!readCommentElement(reader)) 0626 return false; 0627 } else if (!preview && reader->name() == QLatin1String("general")) { 0628 attribs = reader->attributes(); 0629 READ_COLUMN(dataColumn); 0630 READ_COLUMN(xReferenceColumn); 0631 READ_COLUMN(yReferenceColumn); 0632 READ_COLUMN(xPercentilesColumn); 0633 READ_COLUMN(yPercentilesColumn); 0634 READ_INT_VALUE("distribution", distribution, nsl_sf_stats_distribution); 0635 READ_INT_VALUE("legendVisible", legendVisible, bool); 0636 0637 str = attribs.value(QStringLiteral("visible")).toString(); 0638 if (str.isEmpty()) 0639 reader->raiseMissingAttributeWarning(QStringLiteral("visible")); 0640 else 0641 d->setVisible(str.toInt()); 0642 } else if (reader->name() == QLatin1String("column")) { 0643 attribs = reader->attributes(); 0644 bool rc = true; 0645 const auto& name = attribs.value(QStringLiteral("name")); 0646 if (name == QLatin1String("xReference")) 0647 rc = d->xReferenceColumn->load(reader, preview); 0648 else if (name == QLatin1String("yReference")) 0649 rc = d->yReferenceColumn->load(reader, preview); 0650 else if (name == QLatin1String("xPercentiles")) 0651 rc = d->xPercentilesColumn->load(reader, preview); 0652 else if (name == QLatin1String("yPercentiles")) 0653 rc = d->yPercentilesColumn->load(reader, preview); 0654 0655 if (!rc) 0656 return false; 0657 } else if (reader->name() == QLatin1String("xyCurve")) { 0658 attribs = reader->attributes(); 0659 bool rc = true; 0660 if (attribs.value(QStringLiteral("name")) == QLatin1String("reference")) 0661 rc = d->referenceCurve->load(reader, preview); 0662 else 0663 rc = d->percentilesCurve->load(reader, preview); 0664 0665 if (!rc) 0666 return false; 0667 } else { // unknown element 0668 reader->raiseUnknownElementWarning(); 0669 if (!reader->skipToEndElement()) 0670 return false; 0671 } 0672 } 0673 return true; 0674 } 0675 0676 // ############################################################################## 0677 // ######################### Theme management ################################## 0678 // ############################################################################## 0679 void QQPlot::loadThemeConfig(const KConfig& config) { 0680 KConfigGroup group; 0681 if (config.hasGroup(QStringLiteral("Theme"))) 0682 group = config.group(QStringLiteral("XYCurve")); // when loading from the theme config, use the same properties as for XYCurve 0683 else 0684 group = config.group(QStringLiteral("QQPlot")); 0685 0686 const auto* plot = static_cast<const CartesianPlot*>(parentAspect()); 0687 int index = plot->curveChildIndex(this); 0688 const QColor themeColor = plot->themeColorPalette(index); 0689 0690 Q_D(QQPlot); 0691 d->suppressRecalc = true; 0692 0693 d->referenceCurve->line()->loadThemeConfig(group, themeColor); 0694 d->percentilesCurve->line()->setStyle(Qt::NoPen); 0695 d->percentilesCurve->symbol()->loadThemeConfig(group, themeColor); 0696 0697 d->suppressRecalc = false; 0698 d->recalcShapeAndBoundingRect(); 0699 } 0700 0701 void QQPlot::saveThemeConfig(const KConfig& config) { 0702 Q_D(const QQPlot); 0703 KConfigGroup group = config.group(QStringLiteral("QQPlot")); 0704 d->referenceCurve->line()->saveThemeConfig(group); 0705 d->percentilesCurve->symbol()->saveThemeConfig(group); 0706 }