BornAgain  1.19.79
Simulate and fit neutron and x-ray scattering at grazing incidence
ProjectDocument.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/Model/Project/ProjectDocument.cpp
6 //! @brief Implements class ProjectDocument
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2018
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************************************
14 
30 #include "GUI/Util/Error.h"
32 #include "GUI/Util/Path.h"
33 #include <QElapsedTimer>
34 #include <QStandardPaths>
35 #include <QXmlStreamReader>
36 #include <memory>
37 
38 namespace {
39 
40 const QString minimal_supported_version = "1.6.0";
41 const QString BornAgainTag("BornAgain");
42 const QString BornAgainVersionAttribute("Version");
43 const QString InfoTag("DocumentInfo");
44 const QString InfoNameAttribute("ProjectName");
45 const QString SimulationOptionsTag("SimulationOptions");
46 const QString SamplesTag("Samples");
47 const QString InstrumentsTag("Instruments");
48 const QString FormatVersionTag("FormatVersion");
49 
50 } // namespace
51 
52 
54  : m_modified(false)
55  , m_dataService(new DatafieldIOService(this))
56  , m_singleInstrumentMode(false)
57  , m_singleSampleMode(false)
58  , m_functionalities(All)
59  , m_instrumentEditController(&m_instruments)
60 {
62  &ProjectDocument::onModelChanged, Qt::UniqueConnection);
64  &ProjectDocument::onModelChanged, Qt::UniqueConnection);
65 
66 
67  m_linkManager = std::make_unique<LinkInstrumentManager>(this);
68  setObjectName("ProjectDocument");
69 
71  connectModels();
72 }
73 
75 {
76  return m_project_name;
77 }
78 
79 void ProjectDocument::setProjectName(const QString& text)
80 {
81  if (m_project_name != text) {
82  m_project_name = text;
83  emit modified();
84  }
85 }
86 
88 {
89  return m_project_dir;
90 }
91 
93 {
94  if (m_project_name.isEmpty())
95  return "";
96  return m_project_dir;
97 }
98 
99 void ProjectDocument::setProjectDir(const QString& text)
100 {
101  m_project_dir = text;
102 }
103 
104 //! Returns directory name suitable for saving plots.
105 
107 {
108  if (QString dir = validProjectDir(); !dir.isEmpty())
109  return dir;
110  return QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
111 }
112 
114 {
115  if (!projectName().isEmpty())
117  return "";
118 }
119 
120 void ProjectDocument::setProjectFileName(const QString& projectFileName)
121 {
124 }
125 
127 {
128  return const_cast<InstrumentCollection*>(&m_instruments);
129 }
130 
132 {
133  return &m_sampleItems;
134 }
135 
137 {
139 }
140 
142 {
143  return m_applicationModels.jobModel();
144 }
145 
147 {
148  return &m_simulationOptionsItem;
149 }
150 
152 {
153  return m_linkManager.get();
154 }
155 
157 {
159 }
160 
161 void ProjectDocument::save(const QString& project_file_name, bool autoSave)
162 {
163  saveProjectData(project_file_name);
164  saveProjectFile(project_file_name, autoSave);
165 }
166 
167 void ProjectDocument::saveProjectFile(const QString& project_file_name, bool autoSave)
168 {
169  QElapsedTimer timer;
170  timer.start();
171 
172  QFile file(project_file_name);
173  if (!file.open(QFile::ReadWrite | QIODevice::Truncate | QFile::Text))
174  throw Error("ProjectDocument::save_project_file() -> Error. Can't open "
175  "file '"
176  + project_file_name + "' for writing.");
177 
178  writeTo(&file);
179  file.close();
180 
181  if (!autoSave) {
182  setProjectFileName(project_file_name);
183  m_modified = false;
184  emit modified();
185  }
186 }
187 
188 void ProjectDocument::saveProjectData(const QString& project_file_name)
189 {
190  for (auto* d : realDataModel()->realDataItems())
191  d->updateNonXMLDataFileNames();
192 
193  QElapsedTimer timer;
194  timer.start();
195 
197 }
198 
200  MessageService& messageService)
201 {
202  setProjectFileName(project_file_name);
203 
204  QFile file(projectFileName());
205  if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
206  QString message = QString("Open file error '%1'").arg(file.errorString());
207  messageService.addError(this, message);
208  return ReadResult::error;
209  }
210 
211  try {
213  auto result = readProject(&file, messageService);
214  file.close();
215  if (result == ReadResult::error)
216  return result;
217 
218  m_dataService->loadDataFiles(projectDir(), &messageService);
219  connectModels();
220 
221  if (!messageService.warnings().empty())
222  result = ReadResult::warning;
223  return result;
224  } catch (const std::exception& ex) {
225  QString message = QString("Exception thrown '%1'").arg(QString(ex.what()));
226  messageService.addError(this, message);
227  return ReadResult::error;
228  }
229 }
230 
232 {
233  return (!m_project_name.isEmpty() && !m_project_dir.isEmpty());
234 }
235 
237 {
238  return m_modified;
239 }
240 
242 {
243  m_modified = true;
244  emit modified();
245 }
246 
248 {
249  m_modified = false;
250 }
251 
253 {
254  return !m_dataService->dataInterfaces().isEmpty();
255 }
256 
258 {
259  QString result(m_currentVersion);
260  if (result.isEmpty())
262  return result;
263 }
264 
266 {
267  return m_singleInstrumentMode;
268 }
269 
271 {
272  if (b != m_singleInstrumentMode) {
274  emit modified();
276  setModified();
277  }
278 }
279 
281 {
282  return m_singleSampleMode;
283 }
284 
286 {
287  if (b != m_singleSampleMode) {
288  m_singleSampleMode = b;
289  emit modified();
291  setModified();
292  }
293 }
294 
295 ProjectDocument::Functionalities ProjectDocument::functionalities() const
296 {
297  return m_functionalities;
298 }
299 
300 void ProjectDocument::setFunctionalities(const Functionalities& f)
301 {
302  if (m_functionalities != f) {
303  m_functionalities = f;
304  emit modified();
305  emit functionalitiesChanged();
306  setModified();
307  }
308 }
309 
311 {
312  m_modified = true;
313  emit modified();
314 }
315 
317  MessageService& messageService)
318 {
319  const int warningsBefore = messageService.warnings().size();
320 
321  QXmlStreamReader reader(device);
322  try {
323  while (!reader.atEnd()) {
324  reader.readNext();
325  if (reader.isStartElement()) {
326  if (reader.name() == BornAgainTag) {
327  Streamer(&reader).assertVersion(2);
329  reader.attributes().value(BornAgainVersionAttribute).toString();
330 
332  minimal_supported_version)) {
333  QString message = QString("Can't open document version '%1', "
334  "minimal supported version '%2'")
335  .arg(m_currentVersion)
336  .arg(minimal_supported_version);
337  messageService.addError(this, message);
338  return ReadResult::error;
339  }
340 
341  while (reader.readNextStartElement())
342  if (reader.name() == InfoTag)
343  reader.skipCurrentElement();
344  else if (reader.name() == SimulationOptionsTag) {
346  reader.skipCurrentElement();
347  GUI::Session::XML::assertExpectedTag(&reader, SimulationOptionsTag);
348  } else if (reader.name() == SamplesTag) {
349  Streamer s(&reader);
351  // cleanup
352  if (reader.name() != SamplesTag) {
353  if (!reader.isEndElement())
354  reader.skipCurrentElement();
355  reader.skipCurrentElement();
356  }
357  GUI::Session::XML::assertExpectedTag(&reader, SamplesTag);
358  ASSERT(reader.isEndElement());
359 
360  GUI::Session::XML::assertExpectedTag(&reader, SamplesTag);
361  } else if (reader.name() == InstrumentsTag) {
362  Streamer s(&reader);
364  // cleanup
365  if (reader.name() != InstrumentsTag) {
366  if (!reader.isEndElement())
367  reader.skipCurrentElement();
368  reader.skipCurrentElement();
369  }
370  GUI::Session::XML::assertExpectedTag(&reader, InstrumentsTag);
371  ASSERT(reader.isEndElement());
372 
373  GUI::Session::XML::assertExpectedTag(&reader, InstrumentsTag);
374  } else
375  m_applicationModels.readFrom(&reader, &messageService);
376  }
377  }
378  }
379 
380  } catch (DeserializationException& ex) {
381  reader.raiseError(ex.text());
382  }
383 
384  if (reader.hasError()) {
385  QString message = QString("Format error '%1'").arg(reader.errorString());
386  messageService.addError(this, message);
387  return ReadResult::error;
388  }
389 
390  // return "ReadResult::warning" only if warnings have been issued _in here_
391  return warningsBefore != messageService.warnings().size() ? ReadResult::warning
392  : ReadResult::ok;
393 }
394 
395 void ProjectDocument::writeTo(QIODevice* device)
396 {
397  QXmlStreamWriter writer(device);
398  writer.setAutoFormatting(true);
399  writer.writeStartDocument();
400  writer.writeStartElement(BornAgainTag);
401  QString version_string = GUI::Util::Path::getBornAgainVersionString();
402  writer.writeAttribute(BornAgainVersionAttribute, version_string);
403  Streamer(&writer).writeVersion(2);
404 
405  writer.writeStartElement(InfoTag);
406  writer.writeAttribute(InfoNameAttribute, projectName());
407  writer.writeEndElement(); // InfoTag
408 
409  writer.writeStartElement(SimulationOptionsTag);
411  writer.writeEndElement();
412 
413  writer.writeStartElement(SamplesTag);
414  Streamer s(&writer);
416  writer.writeEndElement();
417 
418  writer.writeStartElement(InstrumentsTag);
419  Streamer sInstruments(&writer);
420  m_instruments.serialize(sInstruments);
421  writer.writeEndElement();
422 
423  m_applicationModels.writeTo(&writer);
424 
425  writer.writeEndElement(); // BornAgain tag
426  writer.writeEndDocument();
427 }
428 
430 {
433 }
434 
436 {
438  &ProjectDocument::onModelChanged, Qt::UniqueConnection);
439 }
Defines class DeserializationException.
Defines error class.
Defines class DatafieldIOService.
Defines class InstrumentItem and all its children.
Defines abstract item with a material property.
Defines class JobModel.
Defines class LinkInstrumentManager.
Defines class MaterialItem.
Defines MessageService class.
Defines class MultiLayerItem.
Defines class Helpers functions.
Defines class ProjectDocument.
Defines namespace GUI::Project::Utils.
Defines class RealDataItem.
Defines class RealDataModel.
Defines class SimulationOptionsItem.
Defines class Streamer.
Defines.
JobModel * jobModel() const
void readFrom(class QXmlStreamReader *reader, MessageService *messageService)
RealDataModel * realDataModel() const
void writeTo(class QXmlStreamWriter *writer)
Writes all model in file one by one.
Provide read/write of heavy data files in a separate thread.
Definition: IOService.h:30
void setApplicationModels(ApplicationModels *models)
Definition: IOService.cpp:50
QVector< SaveLoadInterface * > dataInterfaces() const
Returns all non-XML items available for save/load.
Definition: IOService.cpp:109
void save(const QString &projectDir)
Definition: IOService.cpp:55
void loadDataFiles(const QString &projectDir, MessageService *messageService=nullptr)
Definition: IOService.cpp:77
void serialize(Streamer &s)
Class to modify the instruments list or a single instrument and provide the necessary signaling withi...
void instrumentAddedOrRemoved()
Signals a change in the list of instruments.
void instrumentChanged(const InstrumentItem *instrument)
Signals any change in the settings of the given instrument.
The LinkInstrumentManager class provides communication between InstrumentCollection and RealDataModel...
The service to collect messages from different senders.
QStringList warnings(bool includeSenders=false) const
void addError(QObject *sender, const QString &description)
Main model to hold sample items.
void serialize(Streamer &s)
void singleSampleModeChanged()
Emitted when single sample mode has changed.
JobModel * jobModel() const
ApplicationModels m_applicationModels
RealDataModel * realDataModel() const
void setSingleInstrumentMode(bool b)
InstrumentCollection m_instruments
QString projectFileName() const
void setProjectDir(const QString &text)
void saveProjectData(const QString &project_file_name)
SimulationOptionsItem m_simulationOptionsItem
void singleInstrumentModeChanged()
Emitted when single instrument mode has changed.
QString documentVersion() const
void functionalitiesChanged()
Emitted when functionality has changed.
void setSingleSampleMode(bool b)
void saveProjectFile(const QString &project_file_name, bool autoSave=false)
QString projectName() const
InstrumentCollection * collectedItems() const
void setFunctionalities(const Functionalities &f)
void setProjectFileName(const QString &projectFileName)
MultiLayerItems m_sampleItems
LinkInstrumentManager * linkInstrumentManager()
ReadResult readProject(QIODevice *device, MessageService &messageService)
void setProjectName(const QString &text)
QString validProjectDir() const
ReadResult loadProjectFile(const QString &project_file_name, MessageService &messageService)
void writeTo(QIODevice *device)
bool isModified() const
bool hasData() const
Functionalities functionalities() const
MultiLayerItems * sampleItems()
InstrumentsEditController m_instrumentEditController
bool singleInstrumentMode() const
std::unique_ptr< LinkInstrumentManager > m_linkManager
DatafieldIOService * m_dataService
QString projectDir() const
void modified()
Emitted for any modifications in the document.
QString userExportDir() const
Returns directory name suitable for saving plots.
Functionalities m_functionalities
SimulationOptionsItem * simulationOptionsItem()
void save(const QString &project_file_name, bool autoSave=false)
QString m_currentVersion
InstrumentsEditController * instrumentsEditController()
The edit controller for the instruments in this project document.
bool singleSampleMode() const
The RealDataModel class is a model to store all imported RealDataItem's.
Definition: RealDataModel.h:24
The SimulationOptionsItem class holds simulation status (run policy, number of threads,...
void writeContentTo(QXmlStreamWriter *writer) const
void readContentFrom(QXmlStreamReader *reader)
Supports serialization to or deserialization from QXmlStream.
Definition: Streamer.h:36
void writeVersion(unsigned version) const
As writer, serializes the given version number. As reader, does nothing.
Definition: Streamer.cpp:19
void assertVersion(unsigned expectedVersion) const
As reader, throws DeserializationException unless the expected version is read. As writer,...
Definition: Streamer.cpp:26
constexpr const char * projectFileExtension
Definition: ProjectUtils.h:24
QString projectDir(const QString &projectFileName)
Returns project directory deduced from project file name.
QString projectName(const QString &projectFileName)
Returns project name deduced from project file name.
void assertExpectedTag(QXmlStreamReader *reader, const QString &tag)
Definition: UtilXML.cpp:199
bool isVersionMatchMinimal(const QString &version, const QString &minimal_version)
Returns true if current BornAgain version match minimal required version.
Definition: Path.cpp:117
QString getBornAgainVersionString()
Definition: Path.cpp:60