labplot  2.8.2
About: LabPlot is an application for plotting and analysis of 2D and 3D functions and data. It is a complete rewrite of LabPlot1 and lacks in the first release a lot of features available in the predecessor. On the other hand, the GUI and the usability is more superior.
  Fossies Dox: labplot-2.8.2.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

Project.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  File : Project.cpp
3  Project : LabPlot
4  Description : Represents a LabPlot project.
5  --------------------------------------------------------------------
6  Copyright : (C) 2011-2020 Alexander Semke (alexander.semke@web.de)
7  Copyright : (C) 2007-2008 Tilman Benkert (thzs@gmx.net)
8  Copyright : (C) 2007 Knut Franke (knut.franke@gmx.de)
9  ***************************************************************************/
10 
11 /***************************************************************************
12  * *
13  * This program is free software; you can redistribute it and/or modify *
14  * it under the terms of the GNU General Public License as published by *
15  * the Free Software Foundation; either version 2 of the License, or *
16  * (at your option) any later version. *
17  * *
18  * This program is distributed in the hope that it will be useful, *
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
21  * GNU General Public License for more details. *
22  * *
23  * You should have received a copy of the GNU General Public License *
24  * along with this program; if not, write to the Free Software *
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
26  * Boston, MA 02110-1301 USA *
27  * *
28  ***************************************************************************/
29 #include "backend/core/Project.h"
40 #ifdef HAVE_MQTT
42 #endif
43 
44 #include <QDateTime>
45 #include <QFile>
46 #include <QMenu>
47 #include <QMimeData>
48 #include <QThreadPool>
49 #include <QUndoStack>
50 #include <QBuffer>
51 
52 #include <KConfig>
53 #include <KConfigGroup>
54 #include <KFilterDev>
55 #include <KLocalizedString>
56 #include <KMessageBox>
57 
58 /**
59  * \class Project
60  * \ingroup core
61  * \brief Represents a project.
62  *
63  * Project represents the root node of all objects created during the runtime of the program.
64  * Manages also the undo stack.
65  */
66 
67 /**
68  * \enum Project::MdiWindowVisibility
69  * \brief MDI subwindow visibility setting
70  */
71 /**
72  * \var Project::folderOnly
73  * \brief only show MDI windows corresponding to Parts in the current folder
74  */
75 /**
76  * \var Project::foldAndSubfolders
77  * \brief show MDI windows corresponding to Parts in the current folder and its subfolders
78  */
79 /**
80  * \var Project::allMdiWindows
81  * \brief show MDI windows for all Parts in the project simultaneously
82  */
83 
85 public:
86  Private(Project* owner) :
87  version(LVERSION),
88  author(QString(qgetenv("USER"))),
89  modificationTime(QDateTime::currentDateTime()),
90  q(owner) {
91  }
92  QString name() const {
93  return q->name();
94  }
95 
96  QUndoStack undo_stack;
98  QString fileName;
99  QString version;
100  QString author;
101  QDateTime modificationTime;
102  bool changed{false};
104  Project* const q;
105 };
106 
107 Project::Project() : Folder(i18n("Project"), AspectType::Project), d(new Private(this)) {
108  //load default values for name, comment and author from config
109  KConfig config;
110  KConfigGroup group = config.group("Project");
111 
112  d->author = group.readEntry("Author", QString());
113 
114  //we don't have direct access to the members name and comment
115  //->temporary disable the undo stack and call the setters
116  setUndoAware(false);
117  setIsLoading(true);
118  setName(group.readEntry("Name", i18n("Project")));
119  setComment(group.readEntry("Comment", QString()));
120  setUndoAware(true);
121  setIsLoading(false);
122  d->changed = false;
123 
125  connect(this, &Project::aspectAdded,this, &Project::aspectAddedSlot);
126 }
127 
129  //if the project is being closed and the live data sources still continue reading the data,
130  //the dependent objects (columns, etc.), which are already deleted maybe here, are still being notified about the changes.
131  //->stop reading the live data sources prior to deleting all objects.
132  for (auto* lds : children<LiveDataSource>())
133  lds->pauseReading();
134 
135 #ifdef HAVE_MQTT
136  for (auto* client : children<MQTTClient>())
137  client->pauseReading();
138 #endif
139 
140  //if the project is being closed, in Worksheet the scene items are being removed and the selection in the view can change.
141  //don't react on these changes since this can lead crashes (worksheet object is already in the destructor).
142  //->notify all worksheets about the project being closed.
143  for (auto* w : children<Worksheet>(ChildIndexFlag::Recursive))
144  w->setIsClosing();
145 
146  d->undo_stack.clear();
147  delete d;
148 }
149 
150 QUndoStack* Project::undoStack() const {
151  return &d->undo_stack;
152 }
153 
155  QMenu* menu = AbstractAspect::createContextMenu();
156 
157  //add close action
158  menu->addSeparator();
159  menu->addAction(QIcon::fromTheme(QLatin1String("document-close")), i18n("Close"), this, SIGNAL(closeRequested()));
160 
161  //add the actions from MainWin
162  emit requestProjectContextMenu(menu);
163 
164  return menu;
165 }
166 
168  QMenu* menu = const_cast<Folder*>(folder)->AbstractAspect::createContextMenu();
169  emit requestFolderContextMenu(folder, menu);
170  return menu;
171 }
172 
174  d->mdiWindowVisibility = visibility;
176 }
177 
179  return d->mdiWindowVisibility;
180 }
181 
182 CLASS_D_ACCESSOR_IMPL(Project, QString, fileName, FileName, fileName)
183 BASIC_D_ACCESSOR_IMPL(Project, QString, version, Version, version)
184 CLASS_D_READER_IMPL(Project, QString, author, author)
185 CLASS_D_ACCESSOR_IMPL(Project, QDateTime, modificationTime, ModificationTime, modificationTime)
186 
187 STD_SETTER_CMD_IMPL_S(Project, SetAuthor, QString, author)
188 void Project::setAuthor(const QString& author) {
189  if (author != d->author)
190  exec(new ProjectSetAuthorCmd(d, author, ki18n("%1: set author")));
191 }
192 
193 void Project::setChanged(const bool value) {
194  if (isLoading())
195  return;
196 
197  d->changed = value;
198 
199  if (value)
200  emit changed();
201 }
202 
205 }
206 
209 }
210 
211 bool Project::hasChanged() const {
212  return d->changed;
213 }
214 
215 /*!
216  * \brief Project::descriptionChanged
217  * This function is called, when an object changes its name. When a column changed its name and wasn't connected before to the curve/column(formula) then
218  * this is done in this function
219  * \param aspect
220  */
222  if (isLoading())
223  return;
224 
225  //when the name of a column is being changed, it can match again the names being used in the curves, etc.
226  //and we need to update the dependencies
227  const auto* column = dynamic_cast<const AbstractColumn*>(aspect);
228  if (column) {
229  const auto& curves = children<XYCurve>(ChildIndexFlag::Recursive);
230  updateCurveColumnDependencies(curves, column);
231 
232  const auto& histograms = children<Histogram>(ChildIndexFlag::Recursive);
233  updateHistogramColumnDependencies(histograms, column);
234  }
235 
236  d->changed = true;
237  emit changed();
238 }
239 
240 /*!
241  * \brief Project::aspectAddedSlot
242  * When adding new columns, these should be connected to the corresponding curves
243  * \param aspect
244  */
246  //check whether new columns were added and if yes,
247  //update the dependencies in the project
249  const auto* column = dynamic_cast<const AbstractColumn*>(aspect);
250  if (column)
251  columns.append(column);
252  else {
253  for (auto* child : aspect->children<Column>(ChildIndexFlag::Recursive))
254  columns.append(static_cast<const AbstractColumn*>(child));
255  }
256 
257  if (columns.isEmpty())
258  return;
259 
260  //if a new column was addded, check whether the column names match the missing
261  //names in the curves, etc. and update the dependencies
262  const auto& curves = children<XYCurve>(ChildIndexFlag::Recursive);
263  for (auto column : columns)
264  updateCurveColumnDependencies(curves, column);
265 
266  const auto& histograms = children<Histogram>(ChildIndexFlag::Recursive);
267  for (auto column : columns)
268  updateHistogramColumnDependencies(histograms, column);
269 }
270 
272  const QString& columnPath = column->path();
273 
274  // setXColumnPath must not be set, because if curve->column matches column, there already exist a
275  // signal/slot connection between the curve and the column to update this. If they are not same,
276  // xColumnPath is set in setXColumn. Same for the yColumn.
277  for (auto* curve : curves) {
278  curve->setUndoAware(false);
279  auto* analysisCurve = dynamic_cast<XYAnalysisCurve*>(curve);
280  if (analysisCurve) {
281  if (analysisCurve->xDataColumnPath() == columnPath)
282  analysisCurve->setXDataColumn(column);
283  if (analysisCurve->yDataColumnPath() == columnPath)
284  analysisCurve->setYDataColumn(column);
285  if (analysisCurve->y2DataColumnPath() == columnPath)
286  analysisCurve->setY2DataColumn(column);
287 
288  auto* fitCurve = dynamic_cast<XYFitCurve*>(curve);
289  if (fitCurve) {
290  if (fitCurve->xErrorColumnPath() == columnPath)
291  fitCurve->setXErrorColumn(column);
292  if (fitCurve->yErrorColumnPath() == columnPath)
293  fitCurve->setYErrorColumn(column);
294  }
295  } else {
296  if (curve->xColumnPath() == columnPath)
297  curve->setXColumn(column);
298  if (curve->yColumnPath() == columnPath)
299  curve->setYColumn(column);
300  if (curve->valuesColumnPath() == columnPath)
301  curve->setValuesColumn(column);
302  if (curve->xErrorPlusColumnPath() == columnPath)
303  curve->setXErrorPlusColumn(column);
304  if (curve->xErrorMinusColumnPath() == columnPath)
305  curve->setXErrorMinusColumn(column);
306  if (curve->yErrorPlusColumnPath() == columnPath)
307  curve->setYErrorPlusColumn(column);
308  if (curve->yErrorMinusColumnPath() == columnPath)
309  curve->setYErrorMinusColumn(column);
310  }
311 
312  if (curve->valuesColumnPath() == columnPath)
313  curve->setValuesColumn(column);
314 
315  curve->setUndoAware(true);
316  }
317 
318  const QVector<Column*>& columns = children<Column>(ChildIndexFlag::Recursive);
319  for (auto* tempColumn : columns) {
320  const QStringList& paths = tempColumn->formulaVariableColumnPaths();
321  for (int i = 0; i < paths.count(); i++) {
322  if (paths.at(i) == columnPath)
323  tempColumn->setformulVariableColumn(i, const_cast<Column*>(static_cast<const Column*>(column)));
324  }
325  }
326 }
327 
329  const QString& columnPath = column->path();
330  for (auto* histogram : histograms) {
331  if (histogram->dataColumnPath() == columnPath) {
332  histogram->setUndoAware(false);
333  histogram->setDataColumn(column);
334  histogram->setUndoAware(true);
335  }
336 
337  if (histogram->valuesColumnPath() == columnPath) {
338  histogram->setUndoAware(false);
339  histogram->setValuesColumn(column);
340  histogram->setUndoAware(true);
341  }
342  }
343 }
344 void Project::navigateTo(const QString& path) {
345  emit requestNavigateTo(path);
346 }
347 
348 bool Project::isLabPlotProject(const QString& fileName) {
349  return fileName.endsWith(QStringLiteral(".lml"), Qt::CaseInsensitive)
350  || fileName.endsWith(QStringLiteral(".lml.gz"), Qt::CaseInsensitive)
351  || fileName.endsWith(QStringLiteral(".lml.bz2"), Qt::CaseInsensitive)
352  || fileName.endsWith(QStringLiteral(".lml.xz"), Qt::CaseInsensitive);
353 }
354 
356  static const QString extensions = "*.lml *.lml.gz *.lml.bz2 *.lml.xz *.LML *.LML.GZ *.LML.BZ2 *.LML.XZ";
357  return extensions;
358 }
359 
360 QVector<quintptr> Project::droppedAspects(const QMimeData* mimeData) {
361  QByteArray data = mimeData->data(QLatin1String("labplot-dnd"));
362  QDataStream stream(&data, QIODevice::ReadOnly);
363 
364  //read the project pointer first
365  quintptr project = 0;
366  stream >> project;
367 
368  //read the pointers of the dragged aspects
369  QVector<quintptr> vec;
370  stream >> vec;
371 
372  return vec;
373 }
374 
375 //##############################################################################
376 //################## Serialization/Deserialization ###########################
377 //##############################################################################
378 
379 void Project::save(const QPixmap& thumbnail, QXmlStreamWriter* writer) const {
380  //set the version and the modification time to the current values
381  d->version = LVERSION;
382  d->modificationTime = QDateTime::currentDateTime();
383 
384  writer->setAutoFormatting(true);
385  writer->writeStartDocument();
386  writer->writeDTD("<!DOCTYPE LabPlotXML>");
387 
388  writer->writeStartElement("project");
389  writer->writeAttribute("version", version());
390  writer->writeAttribute("fileName", fileName());
391  writer->writeAttribute("modificationTime", modificationTime().toString("yyyy-dd-MM hh:mm:ss:zzz"));
392  writer->writeAttribute("author", author());
393 
394  QByteArray bArray;
395  QBuffer buffer(&bArray);
396  buffer.open(QIODevice::WriteOnly);
397  QPixmap scaledThumbnail = thumbnail.scaled(512,512, Qt::KeepAspectRatio);
398  scaledThumbnail.save(&buffer, "JPEG");
399  QString image = QString::fromLatin1(bArray.toBase64().data());
400  writer->writeAttribute("thumbnail", image);
401 
402  writeBasicAttributes(writer);
403 
404  writeCommentElement(writer);
405 
406  save(writer);
407 }
408 
409 /**
410  * \brief Save as XML
411  */
412 void Project::save(QXmlStreamWriter* writer) const {
413  //save all children
414  for (auto* child : children<AbstractAspect>(ChildIndexFlag::IncludeHidden)) {
415  writer->writeStartElement("child_aspect");
416  child->save(writer);
417  writer->writeEndElement();
418  }
419 
420  //save the state of the views (visible, maximized/minimized/geometry)
421  //and the state of the project explorer (expanded items, currently selected item)
422  emit requestSaveState(writer);
423 
424  writer->writeEndElement();
425  writer->writeEndDocument();
426 }
427 
428 bool Project::load(const QString& filename, bool preview) {
429  QIODevice* file;
430  // first try gzip compression, because projects can be gzipped and end with .lml
431  if (filename.endsWith(QLatin1String(".lml"), Qt::CaseInsensitive))
432  file = new KCompressionDevice(filename,KFilterDev::compressionTypeForMimeType("application/x-gzip"));
433  else // opens filename using file ending
434  file = new KFilterDev(filename);
435 
436  if (!file)
437  file = new QFile(filename);
438 
439  if (!file->open(QIODevice::ReadOnly)) {
440  KMessageBox::error(nullptr, i18n("Sorry. Could not open file for reading."));
441  return false;
442  }
443 
444  char c;
445  bool rc = file->getChar(&c);
446  if (!rc) {
447  KMessageBox::error(nullptr, i18n("The project file is empty."), i18n("Error opening project"));
448  file->close();
449  delete file;
450  return false;
451  }
452  file->seek(0);
453 
454  //parse XML
455  XmlStreamReader reader(file);
456  setIsLoading(true);
457  rc = this->load(&reader, preview);
458  setIsLoading(false);
459  if (rc == false) {
460  RESET_CURSOR;
461  QString msg = reader.errorString();
462  if (msg.isEmpty())
463  msg = i18n("Unknown error when opening the project %1.", filename);
464  KMessageBox::error(nullptr, msg, i18n("Error when opening the project"));
465  file->close();
466  delete file;
467  return false;
468  }
469 
470  if (reader.hasWarnings()) {
471  qWarning("The following problems occurred when loading the project file:");
472  const QStringList& warnings = reader.warningStrings();
473  for (const auto& str : warnings)
474  qWarning() << qUtf8Printable(str);
475 
476 //TODO: show warnings in a kind of "log window" but not in message box
477 // KMessageBox::error(this, msg, i18n("Project loading partly failed"));
478  }
479 
480  if (reader.hasMissingCASWarnings()) {
481  RESET_CURSOR;
482 
483  const QString& msg = i18n("The project has content written with %1. "
484  "Your installation of LabPlot lacks the support for it.\n\n "
485  "You won't be able to see this part of the project. "
486  "If you modify and save the project, the CAS content will be lost.\n\n"
487  "Do you want to continue?", reader.missingCASWarning());
488  auto rc = KMessageBox::warningYesNo(nullptr, msg, i18n("Missing Support for CAS"));
489  if (rc == KMessageBox::ButtonCode::No) {
490  file->close();
491  delete file;
492  return false;
493  }
494  }
495 
496  file->close();
497  delete file;
498 
499  return true;
500 }
501 
502 /**
503  * \brief Load from XML
504  */
505 bool Project::load(XmlStreamReader* reader, bool preview) {
506  while (!(reader->isStartDocument() || reader->atEnd()))
507  reader->readNext();
508 
509  if (!(reader->atEnd())) {
510  if (!reader->skipToNextTag())
511  return false;
512 
513  if (reader->name() == "project") {
514  QString version = reader->attributes().value("version").toString();
515  if (version.isEmpty())
516  reader->raiseWarning(i18n("Attribute 'version' is missing."));
517  else
518  d->version = version;
519 
520  if (!readBasicAttributes(reader)) return false;
521  if (!readProjectAttributes(reader)) return false;
522 
523  while (!reader->atEnd()) {
524  reader->readNext();
525 
526  if (reader->isEndElement()) break;
527 
528  if (reader->isStartElement()) {
529  if (reader->name() == "comment") {
530  if (!readCommentElement(reader))
531  return false;
532  } else if (reader->name() == "child_aspect") {
533  if (!readChildAspectElement(reader, preview))
534  return false;
535  } else if (reader->name() == "state") {
536  //load the state of the views (visible, maximized/minimized/geometry)
537  //and the state of the project explorer (expanded items, currently selected item)
538  emit requestLoadState(reader);
539  } else {
540  reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString()));
541  if (!reader->skipToEndElement()) return false;
542  }
543  }
544  }
545  } else // no project element
546  reader->raiseError(i18n("no project element found"));
547  } else // no start document
548  reader->raiseError(i18n("no valid XML document found"));
549 
550  if (!preview) {
551  //wait until all columns are decoded from base64-encoded data
552  QThreadPool::globalInstance()->waitForDone();
553 
554  //LiveDataSource:
555  //call finalizeLoad() to replace relative with absolute paths if required
556  //and to create columns during the initial read
557  auto sources = children<LiveDataSource>(ChildIndexFlag::Recursive);
558  for (auto* source : sources) {
559  if (!source) continue;
560  source->finalizeLoad();
561  }
562 
563  //everything is read now.
564  //restore the pointer to the data sets (columns) in xy-curves etc.
565  auto columns = children<Column>(ChildIndexFlag::Recursive);
566 
567  //xy-curves
568  // cannot be removed by the column observer, because it does not react
569  // on curve changes
570  auto curves = children<XYCurve>(ChildIndexFlag::Recursive);
571  for (auto* curve : curves) {
572  if (!curve) continue;
573  curve->suppressRetransform(true);
574 
575  auto* equationCurve = dynamic_cast<XYEquationCurve*>(curve);
576  auto* analysisCurve = dynamic_cast<XYAnalysisCurve*>(curve);
577  if (equationCurve) {
578  //curves defined by a mathematical equations recalculate their own columns on load again.
579  if (!preview)
580  equationCurve->recalculate();
581  } else if (analysisCurve) {
582  RESTORE_COLUMN_POINTER(analysisCurve, xDataColumn, XDataColumn);
583  RESTORE_COLUMN_POINTER(analysisCurve, yDataColumn, YDataColumn);
584  RESTORE_COLUMN_POINTER(analysisCurve, y2DataColumn, Y2DataColumn);
585  auto* fitCurve = dynamic_cast<XYFitCurve*>(curve);
586  if (fitCurve) {
587  RESTORE_COLUMN_POINTER(fitCurve, xErrorColumn, XErrorColumn);
588  RESTORE_COLUMN_POINTER(fitCurve, yErrorColumn, YErrorColumn);
589  }
590  } else {
591  RESTORE_COLUMN_POINTER(curve, xColumn, XColumn);
592  RESTORE_COLUMN_POINTER(curve, yColumn, YColumn);
593  RESTORE_COLUMN_POINTER(curve, valuesColumn, ValuesColumn);
594  RESTORE_COLUMN_POINTER(curve, xErrorPlusColumn, XErrorPlusColumn);
595  RESTORE_COLUMN_POINTER(curve, xErrorMinusColumn, XErrorMinusColumn);
596  RESTORE_COLUMN_POINTER(curve, yErrorPlusColumn, YErrorPlusColumn);
597  RESTORE_COLUMN_POINTER(curve, yErrorMinusColumn, YErrorMinusColumn);
598  }
599  if (dynamic_cast<XYAnalysisCurve*>(curve))
600  RESTORE_POINTER(dynamic_cast<XYAnalysisCurve*>(curve), dataSourceCurve, DataSourceCurve, XYCurve, curves);
601 
602  curve->suppressRetransform(false);
603  }
604 
605  //axes
606  auto axes = children<Axis>(ChildIndexFlag::Recursive);
607  for (auto* axis : axes) {
608  if (!axis) continue;
609  RESTORE_COLUMN_POINTER(axis, majorTicksColumn, MajorTicksColumn);
610  RESTORE_COLUMN_POINTER(axis, minorTicksColumn, MinorTicksColumn);
611  }
612 
613  //histograms
614  auto hists = children<Histogram>(ChildIndexFlag::Recursive);
615  for (auto* hist : hists) {
616  if (!hist) continue;
617  RESTORE_COLUMN_POINTER(hist, dataColumn, DataColumn);
618  RESTORE_COLUMN_POINTER(hist, valuesColumn, ValuesColumn);
619  }
620 
621  //data picker curves
622  auto dataPickerCurves = children<DatapickerCurve>(ChildIndexFlag::Recursive);
623  for (auto* dataPickerCurve : dataPickerCurves) {
624  if (!dataPickerCurve) continue;
625  RESTORE_COLUMN_POINTER(dataPickerCurve, posXColumn, PosXColumn);
626  RESTORE_COLUMN_POINTER(dataPickerCurve, posYColumn, PosYColumn);
627  RESTORE_COLUMN_POINTER(dataPickerCurve, plusDeltaXColumn, PlusDeltaXColumn);
628  RESTORE_COLUMN_POINTER(dataPickerCurve, minusDeltaXColumn, MinusDeltaXColumn);
629  RESTORE_COLUMN_POINTER(dataPickerCurve, plusDeltaYColumn, PlusDeltaYColumn);
630  RESTORE_COLUMN_POINTER(dataPickerCurve, minusDeltaYColumn, MinusDeltaYColumn);
631  }
632 
633  //if a column was calculated via a formula, restore the pointers to the variable columns defining the formula
634  for (auto* col : columns) {
635  if (!col->formulaVariableColumnPaths().isEmpty()) {
636  auto& formulaVariableColumns = const_cast<QVector<Column*>&>(col->formulaVariableColumns());
637  formulaVariableColumns.resize(col->formulaVariableColumnPaths().length());
638 
639  for (int i = 0; i < col->formulaVariableColumnPaths().length(); i++) {
640  auto path = col->formulaVariableColumnPaths()[i];
641  for (Column* c : columns) {
642  if (!c) continue;
643  if (c->path() == path) {
644  formulaVariableColumns[i] = c;
645  col->finalizeLoad();
646  break;
647  }
648  }
649  }
650  }
651  }
652 
653  //all data was read in spreadsheets:
654  //call CartesianPlot::retransform() to retransform the plots
655  for (auto* plot : children<CartesianPlot>(ChildIndexFlag::Recursive)) {
656  plot->setIsLoading(false);
657  plot->retransform();
658  }
659 
660  //all data was read in live-data sources:
661  //call CartesianPlot::dataChanged() to notify affected plots about the new data.
662  //this needs to be done here since in LiveDataSource::finalizeImport() called above
663  //where the data is read the column pointers are not restored yes in curves.
665  for (auto* source : sources) {
666  for (int n = 0; n < source->columnCount(); ++n) {
667  Column* column = source->column(n);
668 
669  //determine the plots where the column is consumed
670  for (const auto* curve : curves) {
671  if (curve->xColumn() == column || curve->yColumn() == column) {
672  auto* plot = static_cast<CartesianPlot*>(curve->parentAspect());
673  if (plots.indexOf(plot) == -1) {
674  plots << plot;
675  plot->setSuppressDataChangedSignal(true);
676  }
677  }
678  }
679 
680  column->setChanged();
681  }
682  }
683 
684  //loop over all affected plots and retransform them
685  for (auto* plot : plots) {
686  plot->setSuppressDataChangedSignal(false);
687  plot->dataChanged();
688  }
689  }
690 
691  emit loaded();
692  return !reader->hasError();
693 }
694 
696  QXmlStreamAttributes attribs = reader->attributes();
697  QString str = attribs.value(reader->namespaceUri().toString(), "modificationTime").toString();
698  QDateTime modificationTime = QDateTime::fromString(str, "yyyy-dd-MM hh:mm:ss:zzz");
699  if (str.isEmpty() || !modificationTime.isValid()) {
700  reader->raiseWarning(i18n("Invalid project modification time. Using current time."));
701  d->modificationTime = QDateTime::currentDateTime();
702  } else
703  d->modificationTime = modificationTime;
704 
705  d->author = attribs.value(reader->namespaceUri().toString(), "author").toString();
706 
707  return true;
708 }
AspectType
Base class of all persistent objects in a Project.
void setIsLoading(bool)
void setUndoAware(bool)
void aspectAdded(const AbstractAspect *)
Emitted after a new Aspect has been added to the tree.
bool readCommentElement(XmlStreamReader *)
Load comment from an XML element.
Folder * folder()
Return the folder the Aspect is contained in or 0 if there is none.
@ Recursive
Recursively handle all descendents, not just immediate children.
@ IncludeHidden
Include aspects marked as "hidden" in numbering or listing children.
void aspectDescriptionChanged(const AbstractAspect *)
Emitted after the name, comment or caption spec have changed.
QString name() const
virtual QMenu * createContextMenu()
Return a new context menu.
void writeBasicAttributes(QXmlStreamWriter *) const
Save name and creation time to XML.
T * child(int index, ChildIndexFlags flags={}) const
bool setName(const QString &, bool autoUnique=true)
AbstractAspect::setName sets the name of the abstract aspect.
virtual QString path() const
Return the path that leads from the top-most Aspect (usually a Project) to me.
void exec(QUndoCommand *)
Execute the given command, pushing it on the undoStack() if available.
bool isLoading() const
QVector< AbstractAspect * > children(AspectType type, ChildIndexFlags flags={}) const
bool readBasicAttributes(XmlStreamReader *)
Load name and creation time from XML.
void writeCommentElement(QXmlStreamWriter *) const
Save the comment to XML.
void setComment(const QString &)
Interface definition for data with column logic.
A xy-plot.
Definition: CartesianPlot.h:58
Aspect that manages a column.
Definition: Column.h:42
void setChanged()
Definition: Column.cpp:998
Folder in a project.
Definition: Folder.h:35
bool readChildAspectElement(XmlStreamReader *, bool preview)
Read child aspect from XML.
Definition: Folder.cpp:171
Private(Project *owner)
Definition: Project.cpp:86
MdiWindowVisibility mdiWindowVisibility
Definition: Project.cpp:97
Project *const q
Definition: Project.cpp:104
QString name() const
Definition: Project.cpp:92
QDateTime modificationTime
Definition: Project.cpp:101
QString version
Definition: Project.cpp:99
QUndoStack undo_stack
Definition: Project.cpp:96
QString fileName
Definition: Project.cpp:98
QString author
Definition: Project.cpp:100
bool aspectAddedSignalSuppressed
Definition: Project.cpp:103
Represents a project.
Definition: Project.h:42
virtual const Project * project() const
Definition: Project.h:56
bool aspectAddedSignalSuppressed() const
Definition: Project.cpp:207
MdiWindowVisibility
MDI subwindow visibility setting.
Definition: Project.h:46
@ folderOnly
only show MDI windows corresponding to Parts in the current folder
void requestNavigateTo(const QString &path)
void updateCurveColumnDependencies(const QVector< XYCurve * > &, const AbstractColumn *) const
Definition: Project.cpp:271
void requestLoadState(XmlStreamReader *)
void setMdiWindowVisibility(MdiWindowVisibility visibility)
Definition: Project.cpp:173
Private * d
Definition: Project.h:110
QVector< quintptr > droppedAspects(const QMimeData *)
Definition: Project.cpp:360
void navigateTo(const QString &path)
Definition: Project.cpp:344
void changed()
void descriptionChanged(const AbstractAspect *)
Project::descriptionChanged This function is called, when an object changes its name....
Definition: Project.cpp:221
MdiWindowVisibility mdiWindowVisibility() const
Definition: Project.cpp:178
QUndoStack * undoStack() const override
Return the undo stack of the Project, or 0 if this Aspect is not part of a Project.
Definition: Project.cpp:150
static QString supportedExtensions()
Definition: Project.cpp:355
void mdiWindowVisibilityChanged()
bool hasChanged() const
Definition: Project.cpp:211
void requestFolderContextMenu(const Folder *, QMenu *)
QMenu * createContextMenu() override
Return a new context menu.
Definition: Project.cpp:154
void requestSaveState(QXmlStreamWriter *) const
Project()
Definition: Project.cpp:107
void loaded()
void setChanged(const bool value=true)
Definition: Project.cpp:193
bool load(XmlStreamReader *, bool preview) override
Load from XML.
Definition: Project.cpp:505
void closeRequested()
void setSuppressAspectAddedSignal(bool)
Definition: Project.cpp:203
void updateHistogramColumnDependencies(const QVector< Histogram * > &, const AbstractColumn *) const
Definition: Project.cpp:328
bool readProjectAttributes(XmlStreamReader *)
Definition: Project.cpp:695
QString path() const override
Return the path that leads from the top-most Aspect (usually a Project) to me.
Definition: Project.h:63
virtual QMenu * createFolderContextMenu(const Folder *)
Definition: Project.cpp:167
void save(const QPixmap &, QXmlStreamWriter *) const
Definition: Project.cpp:379
void aspectAddedSlot(const AbstractAspect *)
Project::aspectAddedSlot When adding new columns, these should be connected to the corresponding curv...
Definition: Project.cpp:245
static bool isLabPlotProject(const QString &fileName)
Definition: Project.cpp:348
~Project() override
Definition: Project.cpp:128
void requestProjectContextMenu(QMenu *)
Base class for all analysis curves.
virtual void recalculate()=0
A 2D-curve, provides an interface for editing many properties of the curve.
Definition: XYCurve.h:46
A xy-curve defined by a mathematical equation.
A xy-curve defined by a fit model.
Definition: XYFitCurve.h:43
XML stream parser that supports errors as well as warnings. This class also adds line and column numb...
const QStringList & warningStrings() const
bool hasWarnings() const
bool hasMissingCASWarnings() const
void raiseWarning(const QString &)
void raiseError(const QString &)
QString missingCASWarning() const
#define CLASS_D_READER_IMPL(classname, type, method, var)
Definition: macros.h:148
#define RESET_CURSOR
Definition: macros.h:64
#define STD_SETTER_CMD_IMPL_S(class_name, cmd_name, value_type, field_name)
Definition: macros.h:207
#define RESTORE_POINTER(obj, name, Name, Type, list)
Definition: macros.h:520
#define RESTORE_COLUMN_POINTER(obj, col, Col)
Definition: macros.h:490
#define BASIC_D_ACCESSOR_IMPL(classname, type, method, Method, var)
Definition: macros.h:111
#define CLASS_D_ACCESSOR_IMPL(classname, type, method, Method, var)
Definition: macros.h:138
short Version(char *&buffer, size_t &count)
Get version of ROOT object, obtain number of bytes in object.
Definition: ROOTFilter.cpp:573
#define i18n(m)
Definition: nsl_common.h:38