File indexing completed on 2024-05-12 15:26:41

0001 /***************************************************************************
0002     File                 : DatapickerImage.cpp
0003     Project              : LabPlot
0004     Description          : Worksheet for Datapicker
0005     --------------------------------------------------------------------
0006     Copyright            : (C) 2015 by Ankit Wagadre (wagadre.ankit@gmail.com)
0007     Copyright            : (C) 2015-2019 by Alexander Semke (alexander.semke@web.de)
0008 
0009  ***************************************************************************/
0010 /***************************************************************************
0011  *                                                                         *
0012  *  This program is free software; you can redistribute it and/or modify   *
0013  *  it under the terms of the GNU General Public License as published by   *
0014  *  the Free Software Foundation; either version 2 of the License, or      *
0015  *  (at your option) any later version.                                    *
0016  *                                                                         *
0017  *  This program is distributed in the hope that it will be useful,        *
0018  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
0019  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
0020  *  GNU General Public License for more details.                           *
0021  *                                                                         *
0022  *   You should have received a copy of the GNU General Public License     *
0023  *   along with this program; if not, write to the Free Software           *
0024  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
0025  *   Boston, MA  02110-1301  USA                                           *
0026  *                                                                         *
0027  ***************************************************************************/
0028 
0029 #include "DatapickerImage.h"
0030 #include "DatapickerImagePrivate.h"
0031 #include "backend/datapicker/ImageEditor.h"
0032 #include "backend/lib/commandtemplates.h"
0033 #include "backend/lib/XmlStreamReader.h"
0034 #include "backend/datapicker/DatapickerPoint.h"
0035 #include "backend/datapicker/Segments.h"
0036 #include "backend/worksheet/Worksheet.h"
0037 #include "commonfrontend/datapicker/DatapickerImageView.h"
0038 #include "kdefrontend/worksheet/ExportWorksheetDialog.h"
0039 #include "backend/lib/trace.h"
0040 
0041 #include <QDesktopWidget>
0042 #include <QGraphicsScene>
0043 #include <QMenu>
0044 #include <QPrinter>
0045 #include <QPrintDialog>
0046 #include <QPrintPreviewDialog>
0047 
0048 #include <KConfig>
0049 #include <KConfigGroup>
0050 #include <KLocalizedString>
0051 
0052 /**
0053  * \class DatapickerImage
0054  * \brief container to open image/plot.
0055  *
0056  * Top-level container for DatapickerPoint.
0057  *
0058  * * \ingroup datapicker
0059  */
0060 DatapickerImage::DatapickerImage(const QString& name, bool loading) :
0061     AbstractPart(name, AspectType::DatapickerImage),
0062     foregroundBins( new int[ImageEditor::colorAttributeMax(ColorAttributes::Foreground) + 1]),
0063     hueBins( new int[ImageEditor::colorAttributeMax(ColorAttributes::Hue) + 1]),
0064     saturationBins( new int[ImageEditor::colorAttributeMax(ColorAttributes::Saturation) + 1]),
0065     valueBins( new int[ImageEditor::colorAttributeMax(ColorAttributes::Value) + 1]),
0066     intensityBins( new int[ImageEditor::colorAttributeMax(ColorAttributes::Intensity) + 1]),
0067     d(new DatapickerImagePrivate(this)),
0068     m_segments(new Segments(this)) {
0069 
0070     if (!loading)
0071         init();
0072 }
0073 
0074 DatapickerImage::~DatapickerImage() {
0075     delete [] hueBins;
0076     delete [] saturationBins;
0077     delete [] valueBins;
0078     delete [] intensityBins;
0079     delete [] foregroundBins;
0080     delete m_segments;
0081     delete d;
0082 }
0083 
0084 void DatapickerImage::init() {
0085     KConfig config;
0086     KConfigGroup group = config.group( "DatapickerImage" );
0087 
0088     //general properties
0089     d->fileName = group.readEntry("FileName", QString());
0090     d->rotationAngle = group.readEntry("RotationAngle", 0.0);
0091     d->minSegmentLength = group.readEntry("MinSegmentLength", 30);
0092     d->pointSeparation = group.readEntry("PointSeparation", 30);
0093     d->axisPoints.type = (DatapickerImage::GraphType) group.readEntry("GraphType", static_cast<int>(DatapickerImage::GraphType::Cartesian));
0094     d->axisPoints.ternaryScale = group.readEntry("TernaryScale", 1);
0095 
0096     //edit image settings
0097     d->plotImageType = DatapickerImage::PlotImageType::OriginalImage;
0098     d->settings.foregroundThresholdHigh = group.readEntry("ForegroundThresholdHigh", 90);
0099     d->settings.foregroundThresholdLow = group.readEntry("ForegroundThresholdLow", 30);
0100     d->settings.hueThresholdHigh = group.readEntry("HueThresholdHigh", 360);
0101     d->settings.hueThresholdLow = group.readEntry("HueThresholdLow", 0);
0102     d->settings.intensityThresholdHigh = group.readEntry("IntensityThresholdHigh", 100);
0103     d->settings.intensityThresholdLow = group.readEntry("IntensityThresholdLow", 20);
0104     d->settings.saturationThresholdHigh = group.readEntry("SaturationThresholdHigh", 100);
0105     d->settings.saturationThresholdLow = group.readEntry("SaturationThresholdLow", 30);
0106     d->settings.valueThresholdHigh = group.readEntry("ValueThresholdHigh", 90);
0107     d->settings.valueThresholdLow = group.readEntry("ValueThresholdLow", 30);
0108 
0109     // reference point symbol properties
0110     d->pointStyle = (Symbol::Style)group.readEntry("PointStyle", (int)Symbol::Style::Cross);
0111     d->pointSize = group.readEntry("Size", Worksheet::convertToSceneUnits(7, Worksheet::Unit::Point));
0112     d->pointRotationAngle = group.readEntry("Rotation", 0.0);
0113     d->pointOpacity = group.readEntry("Opacity", 1.0);
0114     d->pointBrush.setStyle( (Qt::BrushStyle)group.readEntry("FillingStyle", (int)Qt::NoBrush) );
0115     d->pointBrush.setColor( group.readEntry("FillingColor", QColor(Qt::black)) );
0116     d->pointPen.setStyle( (Qt::PenStyle)group.readEntry("BorderStyle", (int)Qt::SolidLine) );
0117     d->pointPen.setColor( group.readEntry("BorderColor", QColor(Qt::red)) );
0118     d->pointPen.setWidthF( group.readEntry("BorderWidth", Worksheet::convertToSceneUnits(1, Worksheet::Unit::Point)) );
0119     d->pointVisibility = group.readEntry("PointVisibility", true);
0120 }
0121 
0122 /*!
0123     Returns an icon to be used in the project explorer.
0124 */
0125 QIcon DatapickerImage::icon() const {
0126     return QIcon::fromTheme("image-x-generic");
0127 }
0128 
0129 /*!
0130     Return a new context menu
0131 */
0132 QMenu* DatapickerImage::createContextMenu() {
0133     QMenu* menu = new QMenu(nullptr);
0134     emit requestProjectContextMenu(menu);
0135     return menu;
0136 }
0137 
0138 void DatapickerImage::createContextMenu(QMenu* menu) {
0139     emit requestProjectContextMenu(menu);
0140 }
0141 
0142 //! Construct a primary view on me.
0143 /**
0144  * This method may be called multiple times during the life time of an Aspect, or it might not get
0145  * called at all. Aspects must not depend on the existence of a view for their operation.
0146  */
0147 QWidget* DatapickerImage::view() const {
0148     if (!m_partView) {
0149         m_view = new DatapickerImageView(const_cast<DatapickerImage *>(this));
0150         m_partView = m_view;
0151         connect(m_view, &DatapickerImageView::statusInfo, this, &DatapickerImage::statusInfo);
0152     }
0153     return m_partView;
0154 }
0155 
0156 bool DatapickerImage::exportView() const {
0157     auto* dlg = new ExportWorksheetDialog(m_view);
0158     dlg->setFileName(name());
0159     bool ret;
0160     if ( (ret = (dlg->exec() == QDialog::Accepted)) ) {
0161         const QString path = dlg->path();
0162         const WorksheetView::ExportFormat format = dlg->exportFormat();
0163         const int resolution = dlg->exportResolution();
0164 
0165         WAIT_CURSOR;
0166         m_view->exportToFile(path, format, resolution);
0167         RESET_CURSOR;
0168     }
0169     delete dlg;
0170     return ret;
0171 }
0172 
0173 bool DatapickerImage::printView() {
0174     QPrinter printer;
0175     auto* dlg = new QPrintDialog(&printer, m_view);
0176     bool ret;
0177     dlg->setWindowTitle(i18nc("@title:window", "Print Datapicker Image"));
0178     if ( (ret = (dlg->exec() == QDialog::Accepted)) )
0179         m_view->print(&printer);
0180 
0181     delete dlg;
0182     return ret;
0183 }
0184 
0185 bool DatapickerImage::printPreview() const {
0186     auto* dlg = new QPrintPreviewDialog(m_view);
0187     connect(dlg, &QPrintPreviewDialog::paintRequested, m_view, &DatapickerImageView::print);
0188     return dlg->exec();
0189 }
0190 
0191 /*!
0192     Selects or deselects the Datapicker/DatapickerImage in the project explorer.
0193     This function is called in \c DatapickerImageView.
0194     The DatapickerImage gets deselected if there are selected items in the view,
0195     and selected if there are no selected items in the view.
0196 */
0197 void DatapickerImage::setSelectedInView(const bool b) {
0198     if (b)
0199         emit childAspectSelectedInView(this);
0200     else
0201         emit childAspectDeselectedInView(this);
0202 }
0203 
0204 void DatapickerImage::setSegmentsHoverEvent(const bool on) {
0205     m_segments->setAcceptHoverEvents(on);
0206 }
0207 
0208 QGraphicsScene* DatapickerImage::scene() const {
0209     return d->m_scene;
0210 }
0211 
0212 QRectF DatapickerImage::pageRect() const {
0213     return d->m_scene->sceneRect();
0214 }
0215 
0216 void DatapickerImage::setPlotImageType(const DatapickerImage::PlotImageType type) {
0217     d->plotImageType = type;
0218     if (d->plotImageType == DatapickerImage::PlotImageType::ProcessedImage)
0219         d->discretize();
0220 
0221     emit requestUpdate();
0222 }
0223 
0224 DatapickerImage::PlotImageType DatapickerImage::plotImageType() {
0225     return d->plotImageType;
0226 }
0227 
0228 /* =============================== getter methods for background options ================================= */
0229 CLASS_D_READER_IMPL(DatapickerImage, QString, fileName, fileName)
0230 CLASS_D_READER_IMPL(DatapickerImage, DatapickerImage::ReferencePoints, axisPoints, axisPoints)
0231 CLASS_D_READER_IMPL(DatapickerImage, DatapickerImage::EditorSettings, settings, settings)
0232 BASIC_D_READER_IMPL(DatapickerImage, float, rotationAngle, rotationAngle)
0233 BASIC_D_READER_IMPL(DatapickerImage, DatapickerImage::PointsType, plotPointsType, plotPointsType)
0234 BASIC_D_READER_IMPL(DatapickerImage, int, pointSeparation, pointSeparation)
0235 BASIC_D_READER_IMPL(DatapickerImage, int, minSegmentLength, minSegmentLength)
0236 BASIC_D_READER_IMPL(DatapickerImage, Symbol::Style, pointStyle, pointStyle)
0237 BASIC_D_READER_IMPL(DatapickerImage, qreal, pointOpacity, pointOpacity)
0238 BASIC_D_READER_IMPL(DatapickerImage, qreal, pointRotationAngle, pointRotationAngle)
0239 BASIC_D_READER_IMPL(DatapickerImage, qreal, pointSize, pointSize)
0240 CLASS_D_READER_IMPL(DatapickerImage, QBrush, pointBrush, pointBrush)
0241 CLASS_D_READER_IMPL(DatapickerImage, QPen, pointPen, pointPen)
0242 BASIC_D_READER_IMPL(DatapickerImage, bool, pointVisibility, pointVisibility)
0243 /* ============================ setter methods and undo commands  for background options  ================= */
0244 STD_SETTER_CMD_IMPL_F_S(DatapickerImage, SetFileName, QString, fileName, updateFileName)
0245 void DatapickerImage::setFileName(const QString& fileName) {
0246     if (fileName!= d->fileName) {
0247         beginMacro(i18n("%1: upload new image", name()));
0248         exec(new DatapickerImageSetFileNameCmd(d, fileName, ki18n("%1: upload image")));
0249         endMacro();
0250     }
0251 }
0252 
0253 STD_SETTER_CMD_IMPL_S(DatapickerImage, SetRotationAngle, float, rotationAngle)
0254 void DatapickerImage::setRotationAngle(float angle) {
0255     if (angle != d->rotationAngle)
0256         exec(new DatapickerImageSetRotationAngleCmd(d, angle, ki18n("%1: set rotation angle")));
0257 }
0258 
0259 STD_SETTER_CMD_IMPL_S(DatapickerImage, SetAxisPoints, DatapickerImage::ReferencePoints, axisPoints)
0260 void DatapickerImage::setAxisPoints(const DatapickerImage::ReferencePoints& points) {
0261     if (memcmp(&points, &d->axisPoints, sizeof(points)) != 0)
0262         exec(new DatapickerImageSetAxisPointsCmd(d, points, ki18n("%1: set Axis points")));
0263 }
0264 
0265 STD_SETTER_CMD_IMPL_F_S(DatapickerImage, SetSettings, DatapickerImage::EditorSettings, settings, discretize)
0266 void DatapickerImage::setSettings(const DatapickerImage::EditorSettings& editorSettings) {
0267     if (memcmp(&editorSettings, &d->settings, sizeof(editorSettings)) != 0)
0268         exec(new DatapickerImageSetSettingsCmd(d, editorSettings, ki18n("%1: set editor settings")));
0269 }
0270 
0271 STD_SETTER_CMD_IMPL_F_S(DatapickerImage, SetMinSegmentLength, int, minSegmentLength, makeSegments)
0272 void DatapickerImage::setminSegmentLength(const int value) {
0273     if (d->minSegmentLength != value)
0274         exec(new DatapickerImageSetMinSegmentLengthCmd(d, value, ki18n("%1: set minimum segment length")));        ;
0275 }
0276 
0277 STD_SETTER_CMD_IMPL_F_S(DatapickerImage, SetPointStyle, Symbol::Style, pointStyle, retransform)
0278 void DatapickerImage::setPointStyle(Symbol::Style newStyle) {
0279     if (newStyle != d->pointStyle)
0280         exec(new DatapickerImageSetPointStyleCmd(d, newStyle, ki18n("%1: set point's style")));
0281 }
0282 
0283 STD_SETTER_CMD_IMPL_F_S(DatapickerImage, SetPointSize, qreal, pointSize, retransform)
0284 void DatapickerImage::setPointSize(qreal value) {
0285     if (!qFuzzyCompare(1 + value, 1 + d->pointSize))
0286         exec(new DatapickerImageSetPointSizeCmd(d, value, ki18n("%1: set point's size")));
0287 }
0288 
0289 STD_SETTER_CMD_IMPL_F_S(DatapickerImage, SetPointRotationAngle, qreal, pointRotationAngle, retransform)
0290 void DatapickerImage::setPointRotationAngle(qreal angle) {
0291     if (!qFuzzyCompare(1 + angle, 1 + d->pointRotationAngle))
0292         exec(new DatapickerImageSetPointRotationAngleCmd(d, angle, ki18n("%1: rotate point")));
0293 }
0294 
0295 STD_SETTER_CMD_IMPL_F_S(DatapickerImage, SetPointBrush, QBrush, pointBrush, retransform)
0296 void DatapickerImage::setPointBrush(const QBrush& newBrush) {
0297     if (newBrush != d->pointBrush)
0298         exec(new DatapickerImageSetPointBrushCmd(d, newBrush, ki18n("%1: set point's filling")));
0299 }
0300 
0301 STD_SETTER_CMD_IMPL_F_S(DatapickerImage, SetPointPen, QPen, pointPen, retransform)
0302 void DatapickerImage::setPointPen(const QPen &newPen) {
0303     if (newPen != d->pointPen)
0304         exec(new DatapickerImageSetPointPenCmd(d, newPen, ki18n("%1: set outline style")));
0305 }
0306 
0307 STD_SETTER_CMD_IMPL_F_S(DatapickerImage, SetPointOpacity, qreal, pointOpacity, retransform)
0308 void DatapickerImage::setPointOpacity(qreal newOpacity) {
0309     if (newOpacity != d->pointOpacity)
0310         exec(new DatapickerImageSetPointOpacityCmd(d, newOpacity, ki18n("%1: set point's opacity")));
0311 }
0312 
0313 STD_SETTER_CMD_IMPL_F_S(DatapickerImage, SetPointVisibility, bool, pointVisibility, retransform)
0314 void DatapickerImage::setPointVisibility(const bool on) {
0315     if (on != d->pointVisibility)
0316         exec(new DatapickerImageSetPointVisibilityCmd(d, on, on ? ki18n("%1: set visible") : ki18n("%1: set invisible")));
0317 }
0318 
0319 void DatapickerImage::setPrinting(bool on) const {
0320     auto points = parentAspect()->children<DatapickerPoint>(ChildIndexFlag::Recursive | ChildIndexFlag::IncludeHidden);
0321     for (auto* point : points)
0322         point->setPrinting(on);
0323 }
0324 
0325 void DatapickerImage::setPlotPointsType(const PointsType pointsType) {
0326     if (d->plotPointsType == pointsType)
0327         return;
0328 
0329     d->plotPointsType = pointsType;
0330 
0331     if (pointsType == DatapickerImage::PointsType::AxisPoints) {
0332         //clear image
0333         auto points = children<DatapickerPoint>(ChildIndexFlag::IncludeHidden);
0334         if (!points.isEmpty()) {
0335             beginMacro(i18n("%1: remove all axis points", name()));
0336 
0337             for (auto* point : points)
0338                 point->remove();
0339             endMacro();
0340         }
0341         m_segments->setSegmentsVisible(false);
0342     } else if (pointsType == DatapickerImage::PointsType::CurvePoints)
0343         m_segments->setSegmentsVisible(false);
0344     else if (pointsType == DatapickerImage::PointsType::SegmentPoints) {
0345         d->makeSegments();
0346         m_segments->setSegmentsVisible(true);
0347     }
0348 }
0349 
0350 void DatapickerImage::setPointSeparation(const int value) {
0351     d->pointSeparation = value;
0352 }
0353 
0354 //##############################################################################
0355 //######################  Private implementation ###############################
0356 //##############################################################################
0357 DatapickerImagePrivate::DatapickerImagePrivate(DatapickerImage *owner) : q(owner),
0358     pageRect(0, 0, 1000, 1000),
0359     m_scene(new QGraphicsScene(pageRect)) {
0360 }
0361 
0362 QString DatapickerImagePrivate::name() const {
0363     return q->name();
0364 }
0365 
0366 void DatapickerImagePrivate::retransform() {
0367     auto points = q->children<DatapickerPoint>(AbstractAspect::ChildIndexFlag::IncludeHidden);
0368     for (auto* point : points)
0369         point->retransform();
0370 }
0371 
0372 bool DatapickerImagePrivate::uploadImage(const QString& address) {
0373     bool rc = q->originalPlotImage.load(address);
0374     if (rc) {
0375         //convert the image to 32bit-format if this is not the case yet
0376         QImage::Format format = q->originalPlotImage.format();
0377         if (format != QImage::Format_RGB32 && format != QImage::Format_ARGB32 && format != QImage::Format_ARGB32_Premultiplied)
0378             q->originalPlotImage = q->originalPlotImage.convertToFormat(QImage::Format_RGB32);
0379 
0380         q->processedPlotImage = q->originalPlotImage;
0381         q->background = ImageEditor::findBackgroundColor(&q->originalPlotImage);
0382         //upload Histogram
0383         ImageEditor::uploadHistogram(q->intensityBins, &q->originalPlotImage, q->background, DatapickerImage::ColorAttributes::Intensity);
0384         ImageEditor::uploadHistogram(q->foregroundBins, &q->originalPlotImage, q->background, DatapickerImage::ColorAttributes::Foreground);
0385         ImageEditor::uploadHistogram(q->hueBins, &q->originalPlotImage, q->background, DatapickerImage::ColorAttributes::Hue);
0386         ImageEditor::uploadHistogram(q->saturationBins, &q->originalPlotImage, q->background, DatapickerImage::ColorAttributes::Saturation);
0387         ImageEditor::uploadHistogram(q->valueBins, &q->originalPlotImage, q->background, DatapickerImage::ColorAttributes::Value);
0388         discretize();
0389 
0390         //resize the screen
0391         double w = Worksheet::convertToSceneUnits(q->originalPlotImage.width(), Worksheet::Unit::Inch)/QApplication::desktop()->physicalDpiX();
0392         double h = Worksheet::convertToSceneUnits(q->originalPlotImage.height(), Worksheet::Unit::Inch)/QApplication::desktop()->physicalDpiX();
0393         m_scene->setSceneRect(0, 0, w, h);
0394         q->isLoaded = true;
0395     }
0396     return rc;
0397 }
0398 
0399 void DatapickerImagePrivate::discretize() {
0400     PERFTRACE("DatapickerImagePrivate::discretize()");
0401     if (plotImageType != DatapickerImage::PlotImageType::ProcessedImage)
0402         return;
0403 
0404     ImageEditor::discretize(&q->processedPlotImage, &q->originalPlotImage, settings, q->background);
0405 
0406     if (plotPointsType != DatapickerImage::PointsType::SegmentPoints)
0407         emit q->requestUpdate();
0408     else
0409         makeSegments();
0410 }
0411 
0412 void DatapickerImagePrivate::makeSegments() {
0413     if (plotPointsType != DatapickerImage::PointsType::SegmentPoints)
0414         return;
0415 
0416     PERFTRACE("DatapickerImagePrivate::makeSegments()");
0417     q->m_segments->makeSegments(q->processedPlotImage);
0418     q->m_segments->setSegmentsVisible(true);
0419     emit q->requestUpdate();
0420 }
0421 
0422 DatapickerImagePrivate::~DatapickerImagePrivate() {
0423     delete m_scene;
0424 }
0425 
0426 void DatapickerImagePrivate::updateFileName() {
0427     WAIT_CURSOR;
0428     q->isLoaded = false;
0429     const QString& address = fileName.trimmed();
0430 
0431     if (!address.isEmpty()) {
0432         if (uploadImage(address))
0433             fileName = address;
0434     } else {
0435         //hide segments if they are visible
0436         q->m_segments->setSegmentsVisible(false);
0437     }
0438 
0439     auto points = q->parentAspect()->children<DatapickerPoint>(AbstractAspect::ChildIndexFlag::Recursive | AbstractAspect::ChildIndexFlag::IncludeHidden);
0440     if (!points.isEmpty()) {
0441         for (auto* point : points)
0442             point->remove();
0443     }
0444 
0445     emit q->requestUpdate();
0446     emit q->requestUpdateActions();
0447     RESET_CURSOR;
0448 }
0449 
0450 //##############################################################################
0451 //##################  Serialization/Deserialization  ###########################
0452 //##############################################################################
0453 
0454 //! Save as XML
0455 void DatapickerImage::save(QXmlStreamWriter* writer) const {
0456     writer->writeStartElement( "datapickerImage" );
0457     writeBasicAttributes(writer);
0458 
0459     //general properties
0460     writer->writeStartElement( "general" );
0461     writer->writeAttribute( "fileName", d->fileName );
0462     writer->writeAttribute( "plotPointsType", QString::number(static_cast<int>(d->plotPointsType)) );
0463     writer->writeEndElement();
0464 
0465     writer->writeStartElement( "axisPoint" );
0466     writer->writeAttribute( "graphType", QString::number(static_cast<int>(d->axisPoints.type)) );
0467     writer->writeAttribute( "ternaryScale", QString::number(d->axisPoints.ternaryScale) );
0468     writer->writeAttribute( "axisPointLogicalX1", QString::number(d->axisPoints.logicalPos[0].x()) );
0469     writer->writeAttribute( "axisPointLogicalY1", QString::number(d->axisPoints.logicalPos[0].y()) );
0470     writer->writeAttribute( "axisPointLogicalX2", QString::number(d->axisPoints.logicalPos[1].x()) );
0471     writer->writeAttribute( "axisPointLogicalY2", QString::number(d->axisPoints.logicalPos[1].y()) );
0472     writer->writeAttribute( "axisPointLogicalX3", QString::number(d->axisPoints.logicalPos[2].x()) );
0473     writer->writeAttribute( "axisPointLogicalY3", QString::number(d->axisPoints.logicalPos[2].y()) );
0474     writer->writeAttribute( "axisPointLogicalZ1", QString::number(d->axisPoints.logicalPos[0].z()) );
0475     writer->writeAttribute( "axisPointLogicalZ2", QString::number(d->axisPoints.logicalPos[1].z()) );
0476     writer->writeAttribute( "axisPointLogicalZ3", QString::number(d->axisPoints.logicalPos[2].z()) );
0477     writer->writeAttribute( "axisPointSceneX1", QString::number(d->axisPoints.scenePos[0].x()) );
0478     writer->writeAttribute( "axisPointSceneY1", QString::number(d->axisPoints.scenePos[0].y()) );
0479     writer->writeAttribute( "axisPointSceneX2", QString::number(d->axisPoints.scenePos[1].x()) );
0480     writer->writeAttribute( "axisPointSceneY2", QString::number(d->axisPoints.scenePos[1].y()) );
0481     writer->writeAttribute( "axisPointSceneX3", QString::number(d->axisPoints.scenePos[2].x()) );
0482     writer->writeAttribute( "axisPointSceneY3", QString::number(d->axisPoints.scenePos[2].y()) );
0483     writer->writeEndElement();
0484 
0485     //editor and segment settings
0486     writer->writeStartElement( "editorSettings" );
0487     writer->writeAttribute( "plotImageType", QString::number(static_cast<int>(d->plotImageType)) );
0488     writer->writeAttribute( "rotationAngle", QString::number(d->rotationAngle) );
0489     writer->writeAttribute( "minSegmentLength", QString::number(d->minSegmentLength) );
0490     writer->writeAttribute( "pointSeparation", QString::number(d->pointSeparation) );
0491     writer->writeAttribute( "foregroundThresholdHigh", QString::number(d->settings.foregroundThresholdHigh) );
0492     writer->writeAttribute( "foregroundThresholdLow", QString::number(d->settings.foregroundThresholdLow) );
0493     writer->writeAttribute( "hueThresholdHigh", QString::number(d->settings.hueThresholdHigh) );
0494     writer->writeAttribute( "hueThresholdLow", QString::number(d->settings.hueThresholdLow) );
0495     writer->writeAttribute( "intensityThresholdHigh", QString::number(d->settings.intensityThresholdHigh) );
0496     writer->writeAttribute( "intensityThresholdLow", QString::number(d->settings.intensityThresholdLow) );
0497     writer->writeAttribute( "saturationThresholdHigh", QString::number(d->settings.saturationThresholdHigh) );
0498     writer->writeAttribute( "saturationThresholdLow", QString::number(d->settings.saturationThresholdLow) );
0499     writer->writeAttribute( "valueThresholdHigh", QString::number(d->settings.valueThresholdHigh) );
0500     writer->writeAttribute( "valueThresholdLow", QString::number(d->settings.valueThresholdLow) );
0501     writer->writeEndElement();
0502 
0503     //symbol properties
0504     writer->writeStartElement( "symbolProperties" );
0505     writer->writeAttribute( "pointRotationAngle", QString::number(d->pointRotationAngle) );
0506     writer->writeAttribute( "pointOpacity", QString::number(d->pointOpacity) );
0507     writer->writeAttribute( "pointSize", QString::number(d->pointSize) );
0508     writer->writeAttribute( "pointStyle", QString::number(static_cast<int>(d->pointStyle)) );
0509     writer->writeAttribute( "pointVisibility", QString::number(d->pointVisibility) );
0510     WRITE_QBRUSH(d->pointBrush);
0511     WRITE_QPEN(d->pointPen);
0512     writer->writeEndElement();
0513 
0514     //serialize all children
0515     for (auto* child : children<AbstractAspect>(ChildIndexFlag::IncludeHidden))
0516         child->save(writer);
0517 
0518     writer->writeEndElement();
0519 }
0520 
0521 //! Load from XML
0522 bool DatapickerImage::load(XmlStreamReader* reader, bool preview) {
0523     if (!readBasicAttributes(reader))
0524         return false;
0525 
0526     KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used");
0527     QXmlStreamAttributes attribs;
0528     QString str;
0529 
0530     while (!reader->atEnd()) {
0531         reader->readNext();
0532         if (reader->isEndElement() && reader->name() == "datapickerImage")
0533             break;
0534 
0535         if (!reader->isStartElement())
0536             continue;
0537 
0538         if (!preview && reader->name() == "general") {
0539             attribs = reader->attributes();
0540 
0541             str = attribs.value("fileName").toString();
0542             d->fileName = str;
0543 
0544             READ_INT_VALUE("plotPointsType", plotPointsType, DatapickerImage::PointsType);
0545         } else if (!preview && reader->name() == "axisPoint") {
0546             attribs = reader->attributes();
0547 
0548             READ_INT_VALUE("graphType", axisPoints.type, DatapickerImage::GraphType);
0549             READ_INT_VALUE("ternaryScale", axisPoints.ternaryScale, int);
0550 
0551             str = attribs.value("axisPointLogicalX1").toString();
0552             if (str.isEmpty())
0553                 reader->raiseWarning(attributeWarning.subs("axisPointLogicalX1").toString());
0554             else
0555                 d->axisPoints.logicalPos[0].setX(str.toDouble());
0556 
0557             str = attribs.value("axisPointLogicalY1").toString();
0558             if (str.isEmpty())
0559                 reader->raiseWarning(attributeWarning.subs("axisPointLogicalY1").toString());
0560             else
0561                 d->axisPoints.logicalPos[0].setY(str.toDouble());
0562 
0563             str = attribs.value("axisPointLogicalZ1").toString();
0564             if (str.isEmpty())
0565                 reader->raiseWarning(attributeWarning.subs("axisPointLogicalZ1").toString());
0566             else
0567                 d->axisPoints.logicalPos[0].setZ(str.toDouble());
0568 
0569             str = attribs.value("axisPointLogicalX2").toString();
0570             if (str.isEmpty())
0571                 reader->raiseWarning(attributeWarning.subs("axisPointLogicalX2").toString());
0572             else
0573                 d->axisPoints.logicalPos[1].setX(str.toDouble());
0574 
0575             str = attribs.value("axisPointLogicalY2").toString();
0576             if (str.isEmpty())
0577                 reader->raiseWarning(attributeWarning.subs("axisPointLogicalY2").toString());
0578             else
0579                 d->axisPoints.logicalPos[1].setY(str.toDouble());
0580 
0581             str = attribs.value("axisPointLogicalZ2").toString();
0582             if (str.isEmpty())
0583                 reader->raiseWarning(attributeWarning.subs("axisPointLogicalZ2").toString());
0584             else
0585                 d->axisPoints.logicalPos[1].setZ(str.toDouble());
0586 
0587             str = attribs.value("axisPointLogicalX3").toString();
0588             if (str.isEmpty())
0589                 reader->raiseWarning(attributeWarning.subs("axisPointLogicalX3").toString());
0590             else
0591                 d->axisPoints.logicalPos[2].setX(str.toDouble());
0592 
0593             str = attribs.value("axisPointLogicalY3").toString();
0594             if (str.isEmpty())
0595                 reader->raiseWarning(attributeWarning.subs("axisPointLogicalY3").toString());
0596             else
0597                 d->axisPoints.logicalPos[2].setY(str.toDouble());
0598 
0599             str = attribs.value("axisPointLogicalZ3").toString();
0600             if (str.isEmpty())
0601                 reader->raiseWarning(attributeWarning.subs("axisPointLogicalZ3").toString());
0602             else
0603                 d->axisPoints.logicalPos[2].setZ(str.toDouble());
0604 
0605             str = attribs.value("axisPointSceneX1").toString();
0606             if (str.isEmpty())
0607                 reader->raiseWarning(attributeWarning.subs("axisPointSceneX1").toString());
0608             else
0609                 d->axisPoints.scenePos[0].setX(str.toDouble());
0610 
0611             str = attribs.value("axisPointSceneY1").toString();
0612             if (str.isEmpty())
0613                 reader->raiseWarning(attributeWarning.subs("axisPointSceneY1").toString());
0614             else
0615                 d->axisPoints.scenePos[0].setY(str.toDouble());
0616 
0617             str = attribs.value("axisPointSceneX2").toString();
0618             if (str.isEmpty())
0619                 reader->raiseWarning(attributeWarning.subs("axisPointSceneX2").toString());
0620             else
0621                 d->axisPoints.scenePos[1].setX(str.toDouble());
0622 
0623             str = attribs.value("axisPointSceneY2").toString();
0624             if (str.isEmpty())
0625                 reader->raiseWarning(attributeWarning.subs("axisPointSceneY2").toString());
0626             else
0627                 d->axisPoints.scenePos[1].setY(str.toDouble());
0628 
0629             str = attribs.value("axisPointSceneX3").toString();
0630             if (str.isEmpty())
0631                 reader->raiseWarning(attributeWarning.subs("axisPointSceneX3").toString());
0632             else
0633                 d->axisPoints.scenePos[2].setX(str.toDouble());
0634 
0635             str = attribs.value("axisPointSceneY3").toString();
0636             if (str.isEmpty())
0637                 reader->raiseWarning(attributeWarning.subs("axisPointSceneY3").toString());
0638             else
0639                 d->axisPoints.scenePos[2].setY(str.toDouble());
0640 
0641         } else if (!preview && reader->name() == "editorSettings") {
0642             attribs = reader->attributes();
0643 
0644             READ_INT_VALUE("plotImageType", plotImageType, DatapickerImage::PlotImageType);
0645             READ_DOUBLE_VALUE("rotationAngle", rotationAngle);
0646             READ_INT_VALUE("minSegmentLength", minSegmentLength, int);
0647             READ_INT_VALUE("pointSeparation", pointSeparation, int);
0648             READ_INT_VALUE("foregroundThresholdHigh", settings.foregroundThresholdHigh, int);
0649             READ_INT_VALUE("foregroundThresholdLow", settings.foregroundThresholdLow, int);
0650             READ_INT_VALUE("hueThresholdHigh", settings.hueThresholdHigh, int);
0651             READ_INT_VALUE("hueThresholdLow", settings.hueThresholdLow, int);
0652             READ_INT_VALUE("intensityThresholdHigh", settings.intensityThresholdHigh, int);
0653             READ_INT_VALUE("intensityThresholdLow", settings.intensityThresholdLow, int);
0654             READ_INT_VALUE("saturationThresholdHigh", settings.saturationThresholdHigh, int);
0655             READ_INT_VALUE("saturationThresholdLow", settings.saturationThresholdLow, int);
0656             READ_INT_VALUE("valueThresholdHigh", settings.valueThresholdHigh, int);
0657             READ_INT_VALUE("valueThresholdLow", settings.valueThresholdLow, int);
0658         } else if (!preview && reader->name() == "symbolProperties") {
0659             attribs = reader->attributes();
0660 
0661             READ_DOUBLE_VALUE("pointRotationAngle", pointRotationAngle);
0662             READ_DOUBLE_VALUE("pointOpacity", pointOpacity);
0663             READ_DOUBLE_VALUE("pointSize", pointSize);
0664             READ_INT_VALUE("pointStyle", pointStyle, Symbol::Style);
0665             READ_INT_VALUE("pointVisibility", pointVisibility, bool);
0666             READ_QBRUSH(d->pointBrush);
0667             READ_QPEN(d->pointPen);
0668         } else if (reader->name() == "datapickerPoint") {
0669             auto* datapickerPoint = new DatapickerPoint(QString());
0670             datapickerPoint->setHidden(true);
0671             if (!datapickerPoint->load(reader, preview)) {
0672                 delete datapickerPoint;
0673                 return false;
0674             } else
0675                 addChild(datapickerPoint);
0676         } else { // unknown element
0677             reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString()));
0678             if (!reader->skipToEndElement()) return false;
0679         }
0680     }
0681 
0682     d->uploadImage(d->fileName);
0683     d->retransform();
0684     return true;
0685 }