BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
RealDataTreeModel.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/coregui/Views/ImportDataWidgets/RealDataTreeModel.cpp
6 //! @brief Implements class RealDataTreeModel
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2021
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************************************
14 
18 #include <QApplication>
19 #include <QtCore>
20 #include <QtGui>
21 
23 {
24  m_model = model;
26 
27  // In the following connections: "queued" is important, because while
28  // the notification is sent, a new item is still not completely constructed
31  Qt::ConnectionType(Qt::QueuedConnection | Qt::UniqueConnection));
32 
33  connect(m_model, &RealDataModel::modelAboutToBeReset, this, &RealDataTreeModel::clear,
34  Qt::UniqueConnection);
35 }
36 
38 {
40  return;
41 
42  beginResetModel();
45  endResetModel();
47 }
48 
50 {
51  beginResetModel();
52  m_items1D.clear();
53  m_items2D.clear();
54  endResetModel();
56 }
57 
59 {
60  QModelIndex index = indexForItem(item);
61  if (!index.isValid())
62  return;
63 
64  if (item->dataLoader())
65  item->dataLoader()->disconnect(this);
66 
67  if (item->isSpecularData()) {
68  m_intermediate1DHeadline = m_items1D.size() == 1; // we need intermediate headline
69  // if last child gets deleted
70  const int rowOfItem = m_items1D.indexOf(item);
71  beginRemoveRows(create1DHeadlineIndex(), rowOfItem, rowOfItem);
72  m_items1D.removeAll(item);
73  m_model->removeItem(item);
74  endRemoveRows();
75 
76  if (m_items1D.isEmpty()) {
77  const int rowOfHeadline = 0;
78  beginRemoveRows(QModelIndex(), rowOfHeadline, rowOfHeadline);
80  endRemoveRows();
81  }
82  } else {
83  m_intermediate2DHeadline = m_items2D.size() == 1; // we need intermediate headline
84  // if last child gets deleted
85  const int rowOfItem = m_items2D.indexOf(item);
86  beginRemoveRows(create2DHeadlineIndex(), rowOfItem, rowOfItem);
87  m_items2D.removeAll(item);
88  m_model->removeItem(item);
89  endRemoveRows();
90 
91  if (m_items2D.isEmpty()) {
92  const int rowOfHeadline = m_items1D.isEmpty() ? 0 : 1;
93  beginRemoveRows(QModelIndex(), rowOfHeadline, rowOfHeadline);
95  endRemoveRows();
96  }
97  }
98 
100  m_intermediate2DHeadline = false;
101 }
102 
104 {
105  auto newItem = m_model->insertSpecularDataItem();
106  const int rowOfItem = m_model->realDataItems1D().indexOf(newItem);
107  if (m_items1D.isEmpty()) {
108  const int rowOfHeadline = 0;
109  beginInsertRows(QModelIndex(), rowOfHeadline, rowOfHeadline);
111  endInsertRows();
112  }
113 
114  beginInsertRows(create1DHeadlineIndex(), rowOfItem, rowOfItem);
116  endInsertRows();
118  m_intermediate1DHeadline = false; // reset intermediate state
119  return newItem;
120 }
121 
123 {
124  auto newItem = m_model->insertIntensityDataItem();
125  const int rowOfItem = m_model->realDataItems2D().indexOf(newItem);
126  if (m_items2D.isEmpty()) {
127  const int rowOfHeadline = m_items1D.isEmpty() ? 0 : 1;
128  beginInsertRows(QModelIndex(), rowOfHeadline, rowOfHeadline);
130  endInsertRows();
131  }
132 
133  beginInsertRows(create2DHeadlineIndex(), rowOfItem, rowOfItem);
135  endInsertRows();
137  m_intermediate2DHeadline = false; // reset intermediate state
138  return newItem;
139 }
140 
142 {
143  if (!m_items1D.isEmpty())
144  return m_items1D.first();
145  if (!m_items2D.isEmpty())
146  return m_items2D.first();
147  return nullptr;
148 }
149 
150 QModelIndex RealDataTreeModel::index(int row, int column, const QModelIndex& parent) const
151 {
152  if (!hasIndex(row, column, parent))
153  return QModelIndex();
154 
155  if (!parent.isValid())
156  return createIndex(row, column, nullptr);
157 
158  const bool parentIs1DHeadline = (parent.row() == 0) && !m_items1D.isEmpty();
159  if (parentIs1DHeadline)
160  return createIndex(row, column, m_items1D[row]);
161 
162  return createIndex(row, column, m_items2D[row]);
163 }
164 
165 QModelIndex RealDataTreeModel::parent(const QModelIndex& index) const
166 {
167  if (!index.isValid())
168  return QModelIndex();
169 
170  if (index.internalPointer() == nullptr) // index is headline => no parent
171  return QModelIndex();
172 
173  if (itemForIndex(index)->isSpecularData())
174  return create1DHeadlineIndex();
175 
176  return create2DHeadlineIndex();
177 }
178 
179 int RealDataTreeModel::columnCount(const QModelIndex& /*parent*/) const
180 {
181  return 1;
182 }
183 
184 int RealDataTreeModel::rowCount(const QModelIndex& parent) const
185 {
186  const bool has1D = !m_items1D.isEmpty();
187  const bool has2D = !m_items2D.isEmpty();
188 
189  if (!parent.isValid()) {
190  int headLines = 0;
191  if (has1D || m_intermediate1DHeadline)
192  headLines++;
193  if (has2D || m_intermediate2DHeadline)
194  headLines++;
195 
196  return headLines;
197  }
198 
199  if (parent.internalPointer() != nullptr)
200  return 0; // items do not have children
201 
202  // parent is a headline
203  if (parent.row() == 0 && has1D)
204  return m_items1D.size();
205 
206  return m_items2D.size();
207 }
208 
209 QVariant RealDataTreeModel::data(const QModelIndex& index, int role) const
210 {
211  const bool has1D = !m_items1D.isEmpty();
212 
213  if (isHeadline(index)) {
214  const QString title = (index.row() == 0 && has1D) ? "1D Data" : "2D Data";
215  switch (role) {
216  case Qt::DisplayRole:
217  return title;
218 
219  case Qt::FontRole: {
220  QFont f(QApplication::font());
221  f.setPointSize(f.pointSize() * 1.5);
222  f.setBold(true);
223  return f;
224  }
225 
226  case Qt::SizeHintRole: {
227  QFont f(QApplication::font());
228  f.setPointSize(f.pointSize() * 1.5);
229  f.setBold(true);
230  QSize s = QFontMetrics(f).boundingRect(title).size();
231  return QSize(s.width() * 2, s.height() * 2);
232  }
233 
234  case Qt::TextAlignmentRole:
235  return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
236 
237  case Qt::BackgroundRole:
238  return QBrush(QColor(250, 250, 250));
239 
240  case Qt::ForegroundRole:
241  return QBrush(Qt::black);
242 
243  default:
244  return QVariant();
245  }
246  }
247 
248  const auto item = itemForIndex(index);
249 
250  if (role == Qt::ToolTipRole)
251  return QString();
252 
253  if (role == Qt::DecorationRole) {
254  if (item->isSpecularData())
255  return item->hasImportErrors() ? QIcon(":/images/warning_16x16.png")
256  : QIcon(":/images/1D_OK.png");
257  else
258  return QIcon(":/images/2D_OK.png");
259  }
260 
261  if (role == Qt::DisplayRole) {
262  const auto numErrors =
263  (item->dataLoader() != nullptr) ? item->dataLoader()->numErrors() : 0;
264 
265  if (numErrors == 0)
266  return item->name();
267  else if (numErrors == 1)
268  return item->name() + " (1 parser warning)";
269  else
270  return item->name() + QString(" (%1 parser warnings)").arg(numErrors);
271  }
272 
273  if (role == Qt::EditRole)
274  return item->name();
275 
276  return m_model->data(m_model->indexOfItem(item), role);
277 }
278 
279 Qt::ItemFlags RealDataTreeModel::flags(const QModelIndex& index) const
280 {
281  if (isHeadline(index))
282  return Qt::NoItemFlags;
283 
285  if (index.column() == 0) // col 0 contains name of the data entry
286  f |= Qt::ItemIsEditable;
287 
288  return f;
289 }
290 
291 bool RealDataTreeModel::setData(const QModelIndex& index, const QVariant& value, int role)
292 {
293  if (!index.isValid())
294  return false;
295 
296  if (role == Qt::EditRole && index.column() == 0) {
297  itemForIndex(index)->setName(value.toString());
298  emit dataChanged(index, index);
299  return true;
300  }
301 
302  return false;
303 }
304 
305 RealDataItem* RealDataTreeModel::itemForIndex(const QModelIndex& index) const
306 {
307  if (!index.isValid())
308  return nullptr;
309 
310  return reinterpret_cast<RealDataItem*>(index.internalPointer());
311 }
312 
314 {
315  if (item == nullptr)
316  return QModelIndex();
317 
318  if (item->isSpecularData()) {
319  if (auto index = m_items1D.indexOf(item); index >= 0)
320  return createIndex(index, 0, item);
321 
322  return QModelIndex();
323  }
324 
325  if (auto index = m_items2D.indexOf(item); index >= 0)
326  return createIndex(index, 0, item);
327 
328  return QModelIndex();
329 }
330 
331 bool RealDataTreeModel::isHeadline(const QModelIndex& index) const
332 {
333  if (!index.isValid())
334  return false;
335 
336  return index.internalPointer() == nullptr;
337 }
338 
340 {
341  return createIndex(0, 0, nullptr);
342 }
343 
345 {
346  const bool has1D = !m_items1D.isEmpty();
347  return createIndex(has1D ? 1 : 0, 0, nullptr);
348 }
349 
351 {
352  for (auto item : m_items1D)
353  connect(item, &RealDataItem::importContentsProcessed, this,
354  std::bind(&RealDataTreeModel::onContentsProcessed, this, item),
355  Qt::UniqueConnection);
356 
357  for (auto item : m_items2D)
358  connect(item, &RealDataItem::importContentsProcessed, this,
359  std::bind(&RealDataTreeModel::onContentsProcessed, this, item),
360  Qt::UniqueConnection);
361 }
362 
364 {
365  QModelIndex index = indexForItem(item);
366  emit dataChanged(index, index);
367 }
Defines class RealDataItem.
Defines class RealDataModel.
Defines class RealDataTreeModel.
The RealDataItem class represents intensity data imported from file and intended for fitting.
Definition: RealDataItem.h:35
bool isSpecularData() const
AbstractDataLoader * dataLoader() const
void setName(const QString &name)
void importContentsProcessed()
The RealDataModel class is a model to store all imported RealDataItem's.
Definition: RealDataModel.h:26
QVector< RealDataItem * > realDataItems2D() const
RealDataItem * insertIntensityDataItem()
void realDataAddedOrRemoved()
QVector< RealDataItem * > realDataItems1D() const
RealDataItem * insertSpecularDataItem()
virtual int rowCount(const QModelIndex &parent=QModelIndex()) const override
QVector< RealDataItem * > m_items2D
virtual QModelIndex parent(const QModelIndex &index) const override
QModelIndex indexForItem(RealDataItem *item) const
virtual Qt::ItemFlags flags(const QModelIndex &index) const override
void setRealDataModel(RealDataModel *model)
virtual bool setData(const QModelIndex &index, const QVariant &value, int role) override
QModelIndex create2DHeadlineIndex() const
virtual QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
RealDataItem * insertIntensityDataItem()
bool isHeadline(const QModelIndex &index) const
RealDataModel * m_model
virtual int columnCount(const QModelIndex &parent=QModelIndex()) const override
RealDataItem * topMostItem() const
The topmost visible item. Can be null of course.
RealDataItem * insertSpecularDataItem()
QVector< RealDataItem * > m_items1D
RealDataItem * itemForIndex(const QModelIndex &index) const
virtual QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
void onContentsProcessed(RealDataItem *item)
void removeItem(RealDataItem *item)
QModelIndex create1DHeadlineIndex() const
virtual Qt::ItemFlags flags(const QModelIndex &index) const
void removeItem(SessionItem *item)
virtual QVariant data(const QModelIndex &index, int role) const
QModelIndex indexOfItem(SessionItem *item) const
QVariant DecorationRole(const SessionItem &item)
Returns tooltip for given item.
QVariant ToolTipRole(const SessionItem &item, int ncol=0)
Returns tooltip for given item.
QVariant ForegroundRole(const SessionItem &item)
Returns text color for given item.