BornAgain  1.19.79
Simulate and fit neutron and x-ray scattering at grazing incidence
SessionItem.h
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/Model/BaseItem/SessionItem.h
6 //! @brief Defines class SessionItem
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 
15 #ifndef BORNAGAIN_GUI_MODEL_BASEITEM_SESSIONITEM_H
16 #define BORNAGAIN_GUI_MODEL_BASEITEM_SESSIONITEM_H
17 
18 #include "Base/Util/Assert.h"
19 #include "Fit/Param/RealLimits.h"
20 #include "GUI/Model/BaseItem/ModelMapper.h" // no forward declare, used too often
22 #include "Wrap/WinDllMacros.h"
23 #include <QStringList>
24 #include <memory>
25 
26 Q_DECLARE_METATYPE(RealLimits)
27 
28 class SessionItemData;
29 class SessionItemTags;
30 class QXmlStreamWriter;
31 class QXmlStreamReader;
32 class GroupInfo;
33 
34 //! Base class for a GUI data item.
35 //!
36 //! SessionItem and SessionModel implement the base structures to store data in the GUI layer.
37 //!
38 //! SessionItem provides
39 //! * Keeping of the project data itself
40 //! * Storing of elements for UI representation, e.g. the label text and the tooltip
41 //! * Generic serialization to XML
42 //! * Hierarchical tree management
43 //! * Method to signal value changes
44 //! * Generic way to change values via QVariant
45 //!
46 //! For realizing the highly generic approach, access to properties, children, values, signals is
47 //! done by strings. Converting values is done by means provided by QVariant.
48 //!
49 //! A data structure is realized at runtime, not at compile time (therefore e.g. properties are not
50 //! just data members, but children of type SessionItem). The tree structure, properties, UI etc.
51 //! are usually created by initialization routines.
52 //!
53 //! By this, the data holding is completely generic and also completely volatile - it can be changed
54 //! at any time, properties or children can be added or removed to represent different behavior, or
55 //! even different objects. This realizes kind of a dynamic polymorphy, which can be examined by
56 //! strings. E.g. to find out whether a child is a property with a value, or a grouping element with
57 //! more children, the child's modelType() has to be checked against "Property", "GroupProperty",
58 //! and so on. The model type therefore is comparable to the "class" of the SessionItem.
59 //!
60 //! The hierarchy of a SessionItem and its children is used for
61 //! * representation on the UI (trees)
62 //! * creating the XML hierarchy
63 //! * accessing
64 //!
65 //! **Example for creating properties (properties are a replacement for data members):**
66 //! \code
67 //! CylinderItem::CylinderItem()
68 //! {
69 //! addProperty("Radius", 8.0);
70 //! addProperty("Height", 16.0);
71 //! }
72 //! \endcode
73 //!
74 //! In this example, the text "Radius" will be used
75 //! * on the UI to represent the value (label),
76 //! * for storing the value to the project file (XML tag)
77 //! * for accessing the property (e.g. read/write the value).
78 //! * for identifying the item when signaling a value change
79 //!
80 //! The object has nothing like a \code double m_radius; \endcode
81 //!
82 //! **Relation to core elements**
83 //!
84 //! The GUI component does not use core component elements to store data.
85 //!
86 //! As an example, here the representation of simulation options in the core layer:
87 //! \code
88 //! class SimulationOptions {
89 //! public:
90 //! // access methods omitted for simplicity
91 //! private:
92 //! bool m_mc_integration;
93 //! bool m_include_specular;
94 //! bool m_use_avg_materials;
95 //! size_t m_mc_points;
96 //! ThreadInfo m_thread_info;
97 //! };
98 //! \endcode
99 //!
100 //! And following the corresponding object in the UI layer.
101 //! \code
102 //! class BA_CORE_API_ SimulationOptionsItem : public SessionItem {
103 //! private:
104 //! // Strings for accessing the properties
105 //! static constexpr auto P_RUN_POLICY{"..."};
106 //! static constexpr auto P_NTHREADS{"..."};
107 //! static constexpr auto P_COMPUTATION_METHOD{"..."};
108 //! static constexpr auto P_MC_POINTS{"..."};
109 //! static constexpr auto P_FRESNEL_MATERIAL_METHOD{"..."};
110 //! static constexpr auto P_INCLUDE_SPECULAR_PEAK{"..."};
111 //!
112 //! public:
113 //! // access methods omitted for simplicity
114 //! private:
115 //! // no members at all!
116 //! };
117 //! \endcode
118 //!
119 //! As one can see, no structure is visible at all in the class definition itself.
120 //! The structure is created in the constructor:
121 //!
122 //! \code
123 //! SimulationOptionsItem::SimulationOptionsItem() : SessionItem(M_TYPE)
124 //! {
125 //! ComboProperty policy;
126 //! policy << getRunPolicyNames();
127 //! policy.setToolTips(getRunPolicyTooltips());
128 //! addProperty(P_RUN_POLICY, policy.variant())->setToolTip(tooltip_runpolicy);
129 //!
130 //! ComboProperty nthreads;
131 //! nthreads << getCPUUsageOptions();
132 //! addProperty(P_NTHREADS, nthreads.variant())->setToolTip(tooltip_nthreads);
133 //!
134 //! ComboProperty computationMethod;
135 //! computationMethod << "Analytical"
136 //! << "Monte-Carlo Integration";
137 //! addProperty(P_COMPUTATION_METHOD,
138 //! computationMethod.variant())->setToolTip(tooltip_computation);
139 //!
140 //! // [Code omitted for simplicity]
141 //!
142 //! mapper()->setOnPropertyChange([this](const QString& name) {
143 //! if (name == P_COMPUTATION_METHOD && isTag(P_MC_POINTS)) {
144 //! ComboProperty combo = getItemValue(P_COMPUTATION_METHOD).value<ComboProperty>();
145 //!
146 //! if (combo.getValue() == "Analytical") {
147 //! getItem(P_MC_POINTS)->setEnabled(false);
148 //!
149 //! } else {
150 //! getItem(P_MC_POINTS)->setEnabled(true);
151 //! }
152 //! } else if (name == P_NTHREADS) {
153 //! updateComboItem(P_NTHREADS, getCPUUsageOptions());
154 //! } else if (name == P_RUN_POLICY) {
155 //! updateComboItem(P_RUN_POLICY, getRunPolicyNames());
156 //! }
157 //! });
158 //! }
159 //! \endcode
160 //!
161 //! The onPropertyChange part in the above example shows also the string based access to
162 //! values as well as types:
163 //! \code
164 //! mapper()->setOnPropertyChange([this](const QString& name) {
165 //! if (name == P_COMPUTATION_METHOD && isTag(P_MC_POINTS))
166 //! // ...
167 //! getItem(P_MC_POINTS)->setEnabled(true);
168 //! else if (name == P_NTHREADS)
169 //! updateComboItem(P_NTHREADS, getCPUUsageOptions());
170 //! else if (name == P_RUN_POLICY)
171 //! updateComboItem(P_RUN_POLICY, getRunPolicyNames());
172 //! });
173 //! \endcode
174 //!
175 //! Since UI and core are using completely unrelated objects, a conversion between the two
176 //! components is necessary. This is done with the functions in namespace TransformFromCore
177 //! and TransformToCore.
178 //!
179 //! For IParticle this looks like this:
180 //! \code
181 //! std::unique_ptr<IParticle> GUI::Transform::ToCore::createIParticle(const SessionItem&
182 //! item)
183 //! {
184 //! std::unique_ptr<IParticle> P_particle;
185 //! if (item.modelType() == "Particle") {
186 //! auto& particle_item = static_cast<const ParticleItem&>(item);
187 //! P_particle = particle_item.createParticle();
188 //! } else if (item.modelType() == "ParticleCoreShell") {
189 //! auto& particle_coreshell_item = static_cast<const ParticleCoreShellItem&>(item);
190 //! P_particle = particle_coreshell_item.createParticleCoreShell();
191 //! } else if (item.modelType() == "ParticleComposition") {
192 //! auto& particle_composition_item = static_cast<const ParticleCompositionItem&>(item);
193 //! P_particle = particle_composition_item.createParticleComposition();
194 //! } else if (item.modelType() == "MesoCrystal") {
195 //! auto& mesocrystal_item = static_cast<const MesoCrystalItem&>(item);
196 //! P_particle = mesocrystal_item.createMesoCrystal();
197 //! }
198 //! return P_particle;
199 //! }
200 //! \endcode
201 //!
202 //! This code part again shows the (highly error-prone) string-based type checking.
203 
204 class BA_CORE_API_ SessionItem {
205  friend class SessionModel;
206 
207 private:
208  static constexpr auto P_NAME{"Name"};
209 
210 public:
211  explicit SessionItem(const QString& modelType);
212  virtual ~SessionItem();
213  SessionModel* model() const;
214  SessionItem* parentItem() const;
215 
216  // these functions work without tags and operate on all children
217  QModelIndex index() const;
218  bool hasChildren() const;
219  int numberOfChildren() const;
220  QVector<SessionItem*> children() const;
221  SessionItem* childAt(int row) const;
222  int rowOfChild(SessionItem* child) const;
223  SessionItem* getChildOfType(const QString& type) const;
224  QVector<SessionItem*> childrenOfType(const QString& model_type) const;
225  template <typename T>
226  QVector<T*> childrenOfType() const;
227  template <typename T>
228  T* firstChildOfType() const;
229  SessionItem* takeRow(int row);
230  int parentRow();
231 
232  // manage and check tags
233  bool registerTag(const QString& name, int min = 0, int max = -1, QStringList modelTypes = {});
234  bool isTag(const QString& name) const;
235  const SessionItemTags* sessionItemTags() const;
236  QString tagFromItem(const SessionItem* item) const;
237  bool acceptsAsDefaultItem(const QString& item_name) const;
238  QVector<QString> acceptableDefaultItemTypes() const;
239 
240  // access tagged items
241  SessionItem* getItem(const QString& tag = "", int row = 0) const;
242  template <typename T>
243  T* item(const QString& tag) const;
244  QVector<SessionItem*> getItems(const QString& tag = "") const;
245  template <typename T>
246  QVector<T*> items(const QString& tag = "") const;
247  void insertChild(int row, SessionItem* item, const QString& tag = "");
248  SessionItem* takeItem(int row, const QString& tag);
249 
250  // convenience functions for properties
251  //! Add new property item and register new tag.
252  //! \a name is the tag name and the display name. The property's value will be set
253  //! to \a variant
254  SessionItem* addProperty(const QString& name, const QVariant& variant);
255 
256  // #migration: templated addProperty will eventually replace addGroupProperty
257  template <typename T>
258  T* addProperty(const QString& name);
259 
260  QVariant getItemValue(const QString& tag) const;
261  void setItemValue(const QString& tag, const QVariant& variant) const;
262 
263  // convenience functions for groups
264  SessionItem* addGroupProperty(const QString& groupTag, const GroupInfo& groupInfo);
265  SessionItem* setGroupProperty(const QString& groupTag, const QString& modelType) const;
266  template <typename T>
267  T* setGroupPropertyType(const QString& groupTag);
268  SessionItem* getGroupItem(const QString& groupName) const;
269  template <typename T>
270  T& groupItem(const QString& groupName) const;
271 
272  // access data stored in roles
273  QVariant roleProperty(int role) const;
274 
275  //! Set the contained role property to the given value.
276  //! See also setTranslatorForRolePropertySetter
277  bool setRoleProperty(int role, const QVariant& value);
278  QVector<int> getRoles() const;
279  void emitDataChanged(int role = Qt::DisplayRole);
280 
281  // custom data types
282  QString modelType() const;
283  template <typename T>
284  bool hasModelType() const;
285 
286  QVariant value() const;
287  bool setValue(QVariant value);
288 
289  QString defaultTag() const;
290  void setDefaultTag(const QString& tag);
291 
292  QString displayName() const;
293  void setDisplayName(const QString& display_name);
294 
295  QString itemName() const;
296  void setItemName(const QString& name);
297  static bool isItemNamePropertyName(const QString& name);
298 
299  void setEnabled(bool enabled);
300  void setEditable(bool enabled);
301  bool isEnabled() const;
302  bool isEditable() const;
303 
304  RealLimits limits() const;
305  SessionItem& setLimits(const RealLimits& value);
306 
307  int decimals() const;
308  SessionItem& setDecimals(int n);
309 
310  QString toolTip() const;
311  SessionItem& setToolTip(const QString& tooltip);
312 
313  ModelMapper* mapper();
314 
315  virtual QByteArray serializeBinaryData() const;
316  virtual void deserializeBinaryData(const QByteArray& data);
317 
318  virtual void writeNonSessionItems(QXmlStreamWriter* writer) const;
319  virtual void readNonSessionItems(QXmlStreamReader* reader);
320  virtual bool allowWritingChildToXml(SessionItem* child) const;
321 
322 private:
323  void childDeleted(SessionItem* child);
324  void setParentAndModel(SessionItem* parent, SessionModel* model);
325  void setModel(SessionModel* model);
326  int flags() const;
327  void changeFlags(bool enabled, int flag);
328  int getCopyNumberOfChild(const SessionItem* item) const;
329 
332  QVector<SessionItem*> m_children;
333  std::unique_ptr<SessionItemData> m_properties;
334  std::unique_ptr<SessionItemTags> m_tags;
335  std::unique_ptr<ModelMapper> m_mapper;
336 };
337 
338 // Extends namespace defined in GUI/Model/XML/Serialize.h
339 namespace Serialize {
340 
341 //! Only for migration
342 // #bamigration Remove once the SessionItem migration is done
343 template <typename type>
344 void rwSessionItem(Streamer& s, const QString& tag, SessionItem* property);
345 
346 } // namespace Serialize
347 
348 // ************************************************************************************************
349 // Templates implementation
350 // ************************************************************************************************
351 
352 template <typename T>
353 T* SessionItem::item(const QString& tag) const
354 {
355  auto* t = dynamic_cast<T*>(getItem(tag));
356  ASSERT(t);
357  return t;
358 }
359 
360 template <typename T>
361 QVector<T*> SessionItem::items(const QString& tag) const
362 {
363  QVector<T*> result;
364 
365  for (SessionItem* item : getItems(tag))
366  if (auto* titem = dynamic_cast<T*>(item))
367  result.push_back(titem);
368  return result;
369 }
370 
371 template <typename T>
372 QVector<T*> SessionItem::childrenOfType() const
373 {
374  QVector<T*> result;
375  for (auto* child : m_children)
376  if (child->modelType() == T::M_TYPE)
377  result.append(dynamic_cast<T*>(child));
378 
379  return result;
380 }
381 
382 
383 template <typename T>
385 {
386  for (auto* child : m_children)
387  if (child->modelType() == T::M_TYPE)
388  return dynamic_cast<T*>(child);
389 
390  return nullptr;
391 }
392 
393 template <typename T>
394 T* SessionItem::addProperty(const QString& tagname)
395 {
396  auto property = new T;
397  property->setDisplayName(tagname);
398  registerTag(tagname, 1, 1, QStringList() << property->modelType());
399  insertChild(0, property, tagname);
400  return property;
401 }
402 
403 template <typename T>
404 T* SessionItem::setGroupPropertyType(const QString& groupTag)
405 {
406  SessionItem* item = getGroupItem(groupTag);
407  if (!item->hasModelType<T>())
408  return dynamic_cast<T*>(setGroupProperty(groupTag, T::M_TYPE));
409  return dynamic_cast<T*>(item);
410 }
411 
412 template <typename T>
413 T& SessionItem::groupItem(const QString& groupName) const
414 {
415  auto* t = dynamic_cast<T*>(getGroupItem(groupName));
416  ASSERT(t);
417  return *t;
418 }
419 
420 template <typename T>
422 {
423  return modelType() == T::M_TYPE;
424 }
425 
426 
427 template <typename type>
428 void Serialize::rwSessionItem(Streamer& s, const QString& tag, SessionItem* property)
429 {
430  if (QXmlStreamWriter* w = s.xmlWriter()) {
431  auto v = property->value().value<type>();
432  Serialize::rwValue(s, tag, v);
433  } else if (QXmlStreamReader* r = s.xmlReader()) {
434  type v;
435  Serialize::rwValue(s, tag, v);
436  property->setValue(v);
437  }
438 }
439 
440 #endif // BORNAGAIN_GUI_MODEL_BASEITEM_SESSIONITEM_H
Defines class ModelMapper.
Defines class Streamer.
Defines info for GroupProperty, i.e. collection of model types, their labels and the name of default ...
Definition: GroupInfo.h:25
Handles all data roles for SessionItem.
Holds all tag info for SessionItem.
Base class for a GUI data item.
Definition: SessionItem.h:204
T & groupItem(const QString &groupName) const
Definition: SessionItem.h:413
SessionItem * addProperty(const QString &name, const QVariant &variant)
Add new property item and register new tag. name is the tag name and the display name....
SessionItem * getGroupItem(const QString &groupName) const
Access subitem of group item.
bool registerTag(const QString &name, int min=0, int max=-1, QStringList modelTypes={})
Add new tag to this item with given name, min, max and types. max = -1 -> unlimited,...
QVector< SessionItem * > getItems(const QString &tag="") const
Returns vector of all items of given tag.
std::unique_ptr< SessionItemData > m_properties
Definition: SessionItem.h:333
void insertChild(int row, SessionItem *item, const QString &tag="")
Insert item into given tag into given row.
bool hasModelType() const
Definition: SessionItem.h:421
QVector< SessionItem * > m_children
Definition: SessionItem.h:332
void setDisplayName(const QString &display_name)
Set display name.
SessionModel * m_model
Definition: SessionItem.h:331
T * firstChildOfType() const
Definition: SessionItem.h:384
T * item(const QString &tag) const
Definition: SessionItem.h:353
QVector< T * > items(const QString &tag="") const
Definition: SessionItem.h:361
SessionItem * setGroupProperty(const QString &groupTag, const QString &modelType) const
Set the current type of group item.
std::unique_ptr< SessionItemTags > m_tags
Definition: SessionItem.h:334
QVector< T * > childrenOfType() const
Definition: SessionItem.h:372
QString modelType() const
Get model type.
SessionItem * m_parent
Definition: SessionItem.h:330
std::unique_ptr< ModelMapper > m_mapper
Definition: SessionItem.h:335
T * setGroupPropertyType(const QString &groupTag)
Definition: SessionItem.h:404
SessionItem * getItem(const QString &tag="", int row=0) const
Returns item in given row of given tag.
Base class for a GUI data collection. A collection is e.g. all real data (RealDataModel)....
Definition: SessionModel.h:42
QVariant data(const QModelIndex &index, int role) const override
QModelIndex parent(const QModelIndex &child) const override
Qt::ItemFlags flags(const QModelIndex &index) const override
QModelIndex index(int row, int column, const QModelIndex &parent) const override
friend class SessionItem
Definition: SessionModel.h:44
QVector< QString > acceptableDefaultItemTypes(const QModelIndex &parent) const
Supports serialization to or deserialization from QXmlStream.
Definition: Streamer.h:36
QXmlStreamWriter * xmlWriter()
Returns stream writer or nullptr.
Definition: Streamer.h:47
QXmlStreamReader * xmlReader()
Returns stream reader or nullptr.
Definition: Streamer.h:48
QString const & name(EShape k)
Definition: particles.cpp:20
Functions to serialize various data types.
Definition: SessionItem.h:339
void rwSessionItem(Streamer &s, const QString &tag, SessionItem *property)
Only for migration.
Definition: SessionItem.h:428
void rwValue(Streamer &s, const QString &tag, bool &val)
Definition: Serialize.cpp:19