BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
DesignerScene.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/coregui/Views/SampleDesigner/DesignerScene.cpp
6 //! @brief Implements class DesignerScene
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 
33 #include <QGraphicsSceneMouseEvent>
34 #include <QItemSelection>
35 #include <QPainter>
36 
38  : QGraphicsScene(parent)
39  , m_sampleModel(0)
40  , m_instrumentModel(0)
41  , m_materialModel(0)
42  , m_selectionModel(0)
43  , m_proxy(0)
44  , m_block_selection(false)
45  , m_aligner(new SampleViewAligner(this))
46 {
47  setSceneRect(QRectF(-1600, 0, 3200, 3200));
48  setBackgroundBrush(DesignerHelper::getSceneBackground());
49 
50  m_nodeEditor = new NodeEditor(parent);
51  m_nodeEditor->install(this);
56  connect(this, &DesignerScene::selectionChanged, this, &DesignerScene::onSceneSelectionChanged);
57 }
58 
60 {
61  delete m_aligner;
62 }
63 
65 {
66  ASSERT(sampleModel);
67 
68  if (sampleModel != m_sampleModel) {
69 
70  if (m_sampleModel) {
71  disconnect(m_sampleModel, &SampleModel::modelAboutToBeReset, this,
73  disconnect(m_sampleModel, &SampleModel::rowsInserted, this,
75  disconnect(m_sampleModel, &SampleModel::rowsAboutToBeRemoved, this,
77  disconnect(m_sampleModel, &SampleModel::rowsRemoved, this,
79  disconnect(m_sampleModel, &SampleModel::modelReset, this, &DesignerScene::updateScene);
80  }
81 
82  m_sampleModel = sampleModel;
83 
84  connect(m_sampleModel, &SampleModel::modelAboutToBeReset, this, &DesignerScene::resetScene);
85  connect(m_sampleModel, &SampleModel::rowsInserted, this, &DesignerScene::onRowsInserted);
86  connect(m_sampleModel, &SampleModel::rowsAboutToBeRemoved, this,
88  connect(m_sampleModel, &SampleModel::rowsRemoved, this, &DesignerScene::onRowsRemoved);
89  connect(m_sampleModel, &SampleModel::modelReset, this, &DesignerScene::updateScene);
90 
91  resetScene();
92  updateScene();
93  }
94 }
95 
97 {
98  m_instrumentModel = instrumentModel;
99 }
100 
102 {
103  m_materialModel = materialModel;
104 }
105 
106 void DesignerScene::setSelectionModel(QItemSelectionModel* model, FilterPropertyProxy* proxy)
107 {
108  ASSERT(model);
109 
110  if (model != m_selectionModel) {
111 
112  if (m_selectionModel) {
113  disconnect(m_selectionModel, &QItemSelectionModel::selectionChanged, this,
115  }
116 
117  m_selectionModel = model;
118  m_proxy = proxy;
119 
120  connect(m_selectionModel, &QItemSelectionModel::selectionChanged, this,
122  }
123 }
124 
126 {
127  auto it = m_ItemToView.find(item);
128  if (it != m_ItemToView.end()) {
129  return it.value();
130  }
131  return nullptr;
132 }
133 
135 {
136  clear();
137  m_ItemToView.clear();
139 }
140 
142 {
143  updateViews();
144  alignViews();
145 }
146 
147 void DesignerScene::onRowsInserted(const QModelIndex& /* parent */, int /* first */, int /* last */)
148 {
149  updateScene();
150 }
151 
152 void DesignerScene::onRowsRemoved(const QModelIndex& /* parent */, int /* first */, int /* last */)
153 {
154  updateScene();
155 }
156 
157 void DesignerScene::onRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last)
158 {
159  m_block_selection = true;
160  for (int irow = first; irow <= last; ++irow) {
161  QModelIndex itemIndex = m_sampleModel->index(irow, 0, parent);
162  deleteViews(itemIndex); // deleting all child items
163  }
164  m_block_selection = false;
165 }
166 
167 //! propagate selection from model to scene
168 void DesignerScene::onSessionSelectionChanged(const QItemSelection& /* selected */,
169  const QItemSelection& /* deselected */)
170 {
171  if (m_block_selection)
172  return;
173 
174  m_block_selection = true;
175 
176  for (QMap<SessionItem*, IView*>::iterator it = m_ItemToView.begin(); it != m_ItemToView.end();
177  ++it) {
178  QModelIndex index = m_proxy->mapFromSource(m_sampleModel->indexOfItem(it.key()));
179  if (index.isValid()) {
180  if (m_selectionModel->isSelected(index)) {
181  it.value()->setSelected(true);
182  } else {
183  it.value()->setSelected(false);
184  }
185  }
186  }
187 
188  m_block_selection = false;
189 }
190 
191 //! propagate selection from scene to model
193 {
194  if (m_block_selection)
195  return;
196 
197  m_block_selection = true;
198 
199  m_selectionModel->clearSelection();
200  QList<QGraphicsItem*> selected = selectedItems();
201  for (int i = 0; i < selected.size(); ++i) {
202  IView* view = dynamic_cast<IView*>(selected[i]);
203  if (view) {
204  SessionItem* sampleItem = view->getItem();
205  QModelIndex itemIndex = m_sampleModel->indexOfItem(sampleItem);
206  ASSERT(itemIndex.isValid());
207  if (!m_selectionModel->isSelected(m_proxy->mapFromSource(itemIndex)))
208  m_selectionModel->select(m_proxy->mapFromSource(itemIndex),
209  QItemSelectionModel::Select);
210  }
211  }
212 
213  m_block_selection = false;
214 }
215 
216 //! runs through all items recursively and updates corresponding views
217 void DesignerScene::updateViews(const QModelIndex& parentIndex, IView* parentView)
218 {
220 
221  IView* childView(0);
222  int childCount = 0;
223  for (int i_row = 0; i_row < m_sampleModel->rowCount(parentIndex); ++i_row) {
224  QModelIndex itemIndex = m_sampleModel->index(i_row, 0, parentIndex);
225 
226  if (SessionItem* item = m_sampleModel->itemForIndex(itemIndex)) {
227 
228  if (item && !SampleViewFactory::isValidType(item->modelType()))
229  continue;
230 
231  childView = addViewForItem(item);
232  if (childView) {
233  if (parentView)
234  parentView->addView(childView, childCount++);
235  }
236  }
237 
238  updateViews(itemIndex, childView);
239  }
240 }
241 
242 //! adds view for item, if it doesn't exists
244 {
245  ASSERT(item);
246 
247  IView* view = getViewForItem(item);
248 
249  if (!view) {
251  if (view) {
252  m_ItemToView[item] = view;
253  view->setParameterizedItem(item);
254  addItem(view);
255  return view;
256  }
257  } else {
258  // view for item exists
259  }
260  return view;
261 }
262 
263 //! aligns SampleView's on graphical canvas
265 {
266  m_aligner->alignSample(QModelIndex(), QPointF(200, 800));
267 }
268 
269 //! runs recursively through model's item and schedules view removal
270 void DesignerScene::deleteViews(const QModelIndex& viewIndex)
271 {
272  for (int i_row = 0; i_row < m_sampleModel->rowCount(viewIndex); ++i_row) {
273  QModelIndex itemIndex = m_sampleModel->index(i_row, 0, viewIndex);
274 
275  if (SessionItem* item = m_sampleModel->itemForIndex(itemIndex)) {
277 
278  } else {
279  // not a parameterized graphics item
280  }
281  deleteViews(itemIndex);
282  }
283  removeItemViewFromScene(m_sampleModel->itemForIndex(viewIndex)); // deleting view itself
284 }
285 
286 //! removes view from scene corresponding to given item
288 {
289  ASSERT(item);
290 
291  for (QMap<SessionItem*, IView*>::iterator it = m_ItemToView.begin(); it != m_ItemToView.end();
292  ++it) {
293  if (it.key() == item) {
294  IView* view = it.value();
295  view->setSelected(false);
296  m_ItemToView.erase(it);
297  emit view->aboutToBeDeleted();
298  view->deleteLater();
299  // delete view;
300  update();
301  break;
302  }
303  }
304 }
305 
306 //! propagates deletion of views on the scene to the model
308 {
309  QModelIndexList indexes = m_selectionModel->selectedIndexes();
310 
311  QList<IView*> views_which_will_be_deleted;
312  for (auto index : indexes) {
313  views_which_will_be_deleted.append(
314  m_ItemToView[m_sampleModel->itemForIndex(m_proxy->mapToSource(index))]);
315  }
316  // deleting selected items on model side, corresponding views will be deleted automatically
317  // Since we don't know the order of items and their parent/child relationship, we need this
318  while (indexes.size()) {
319  QModelIndex current = m_proxy->mapToSource(indexes.back());
320  m_sampleModel->removeRows(current.row(), 1, current.parent());
321  indexes = m_selectionModel->selectedIndexes();
322  }
323  // Connections will be deleted automatically if one of connected views has been deleted.
324  // For the moment, we have to delete connections which are: 1) were selected 2) Do not connect
325  // views scheduled for deletion.
326  for (auto graphicsItem : selectedItems()) {
327  if (NodeEditorConnection* connection = dynamic_cast<NodeEditorConnection*>(graphicsItem)) {
328  if (views_which_will_be_deleted.contains(connection->getParentView())
329  || views_which_will_be_deleted.contains(connection->getChildView()))
330  continue;
331  removeConnection(connection);
332  }
333  }
334 }
335 
336 //! shows appropriate layer interface to drop while moving ILayerView
337 void DesignerScene::drawForeground(QPainter* painter, const QRectF& /* rect */)
338 {
339  if (isLayerDragged()) {
340  painter->setPen(QPen(Qt::darkBlue, 2, Qt::DashLine));
341  painter->drawLine(m_layer_interface_line);
342  }
343 }
344 
345 //! propagates connection established by NodeEditor to the model
347 {
348  ConnectableView* parentView = connection->getParentView();
349  ConnectableView* childView = connection->getChildView();
350 
351  QString tag;
352  if (connection->getParentView()->getItem()->modelType() == "ParticleLayout") {
353  if (connection->inputPort()->getPortType() == NodeEditorPort::INTERFERENCE)
355  } else if (connection->getParentView()->getItem()->modelType() == "ParticleCoreShell") {
356  if (parentView->getInputPortIndex(connection->inputPort()) == 0)
358  else if (parentView->getInputPortIndex(connection->inputPort()) == 1)
360  else if (connection->inputPort()->getPortType() == NodeEditorPort::TRANSFORMATION)
362 
363  } else if (connection->getParentView()->getItem()->modelType() == "ParticleComposition") {
364  if (connection->inputPort()->getPortType() == NodeEditorPort::TRANSFORMATION)
366  } else if (connection->getParentView()->getItem()->modelType() == "MesoCrystal") {
367  if (connection->inputPort()->getPortType() == NodeEditorPort::TRANSFORMATION)
369  }
370  delete connection; // deleting just created connection because it will be recreated from the
371  // model
372  m_sampleModel->moveItem(childView->getItem(), parentView->getItem(), -1, tag);
373 }
374 
375 //! propagates break of connection between views on scene to the model
377 {
378  IView* childView = dynamic_cast<IView*>(connection->outputPort()->parentItem());
379  m_sampleModel->moveItem(childView->getItem(), 0);
380 }
381 
382 //! handles drag event
383 //! LayerView can be dragged only over MultiLayerView
384 //! MultiLayerView can be dragged both, over the scene and over another MultiLayerView
385 void DesignerScene::dragMoveEvent(QGraphicsSceneDragDropEvent* event)
386 {
387  const DesignerMimeData* mimeData = checkDragEvent(event);
388  if (isAcceptedByMultiLayer(mimeData, event)) {
389  QGraphicsScene::dragMoveEvent(event);
390  }
391 }
392 
393 //! Hadles drop event
394 //! LayerView can be dropped on MultiLayerView only
395 //! MultiLayerView can be droped on the scene or another MultiLayerView
396 void DesignerScene::dropEvent(QGraphicsSceneDragDropEvent* event)
397 {
398  const DesignerMimeData* mimeData = checkDragEvent(event);
399  if (mimeData) {
400 
401  // to have possibility to drop MultiLayer on another MultiLayer
402  // * edit function DesignerScene::isAcceptedByMultiLayer
403  // * edit MultiLayerItem for addToValidChildren
404  // * remove method MultiLayerView::itemChange
405 
406  if (isAcceptedByMultiLayer(mimeData, event)) {
407  // certain views can be dropped on MultiLayer and so will be processed there
408  QGraphicsScene::dropEvent(event);
409 
410  } else {
411  // other views can be dropped on canvas anywhere
412  if (SampleViewFactory::isValidType(mimeData->getClassName())) {
413 
414  SessionItem* new_item(0);
415  if (mimeData->getClassName().startsWith("FormFactor")) {
416  new_item = m_sampleModel->insertItem<ParticleItem>();
417  QString ffName = mimeData->getClassName();
418  ffName.remove("FormFactor");
420 
421  } else {
422  new_item = m_sampleModel->insertNewItem(mimeData->getClassName());
423  }
424 
425  // propagating drop coordinates to SessionItem
426  QRectF boundingRect = DesignerHelper::getDefaultBoundingRect(new_item->modelType());
428  event->scenePos().x() - boundingRect.width() / 2);
430  event->scenePos().y() - boundingRect.height() / 2);
431 
432  } else if (GUIExamplesFactory::isValidExampleName(mimeData->getClassName())) {
435  QRectF boundingRect = DesignerHelper::getDefaultBoundingRect(topItem->modelType());
436  QPointF reference(event->scenePos().x() - boundingRect.width() / 2,
437  event->scenePos().y() - boundingRect.height() / 2);
438  m_aligner->alignSample(topItem, reference, true);
439  }
440  adjustSceneRect();
441  }
442  }
443 }
444 
445 //! returns proper MimeData if the object can be hadled by graphics scene
446 const DesignerMimeData* DesignerScene::checkDragEvent(QGraphicsSceneDragDropEvent* event)
447 {
448  const DesignerMimeData* mimeData = qobject_cast<const DesignerMimeData*>(event->mimeData());
449  if (!mimeData) {
450  event->ignore();
451  return 0;
452  }
453  event->setAccepted(true);
454  return mimeData;
455 }
456 
457 void DesignerScene::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
458 {
459  if (isLayerDragged()) {
460  invalidate(); // to redraw vertical dashed line which denotes where to drag the layer
461  }
462  QGraphicsScene::mouseMoveEvent(event);
463 }
464 
465 //! Returns true if there is MultiLayerView nearby during drag event.
466 bool DesignerScene::isMultiLayerNearby(QGraphicsSceneDragDropEvent* event)
467 {
469  rect.moveCenter(event->scenePos());
470  for (QGraphicsItem* item : items(rect)) {
471  if (item->type() == ViewTypes::MULTILAYER)
472  return true;
473  }
474  return false;
475 }
476 
478 {
479  QRectF boundingRect = itemsBoundingRect();
480  if (sceneRect().contains(boundingRect))
481  return;
482 
483  boundingRect.adjust(20.0, 20.0, 20.0, 20.0);
484  setSceneRect(sceneRect().united(boundingRect));
485 }
486 
488  QGraphicsSceneDragDropEvent* event)
489 {
490  if (!mimeData)
491  return false;
492 
493  // // MultiLayer can be inserted in MultiLayer
494  // if (mimeData->getClassName() == "MultiLayer" && isMultiLayerNearby(event)) {
495  // return true;
496  // }
497 
498  // layer can be inserted in MultiLayer
499  if (mimeData->getClassName() == "Layer" && isMultiLayerNearby(event)) {
500  return true;
501  }
502  return false;
503 }
504 
506 {
507  ILayerView* layer = dynamic_cast<ILayerView*>(mouseGrabberItem());
508  if (layer && !m_layer_interface_line.isNull()) {
509  return true;
510  }
511  return false;
512 }
513 
515 {
517 }
#define ASSERT(condition)
Definition: Assert.h:31
Defines class DesignerHelper.
Defines class DesignerMimeData.
Defines class DesignerScene.
Defines class FilterPropertyProxy.
Defines class GUIExamplesFactory.
Defines class InstrumentModel.
Defines class ItemFactory.
Defines class LayerView.
Defines class NodeEditorConnection.
Defines class NodeEditor.
Defines class ParticleCompositionItem.
Defines class ParticleCoreShellItem.
Defines class ParticleItem.
Defines class ParticleLayoutItem.
Defines class SampleBuilderFactory.
Defines class SampleModel.
Defines class SampleViewAligner.
Defines class SampleViewFactory.
view of ISampleNode's with rectangular shape and node functionality
int getInputPortIndex(NodeEditorPort *port)
static QPixmap getSceneBackground()
static QRectF getDefaultBoundingRect(const QString &name)
returns default bounding rectangle for given IvView name
static QRectF getDefaultMultiLayerRect()
Mime data for use with SampleDesigner drag and drop operations.
QString getClassName() const
virtual ~DesignerScene()
void updateViews(const QModelIndex &parentIndex={}, IView *parentView=0)
runs through all items recursively and updates corresponding views
bool m_block_selection
void deleteSelectedItems()
propagates deletion of views on the scene to the model
void selectionModeChangeRequest(int)
void deleteViews(const QModelIndex &parentIndex)
runs recursively through model's item and schedules view removal
void setInstrumentModel(InstrumentModel *instrumentModel)
SampleModel * m_sampleModel
void setMaterialModel(MaterialModel *materialModel)
QItemSelectionModel * m_selectionModel
QLineF m_layer_interface_line
Foreground line representing appropriate interface during layer's movement.
void onSessionSelectionChanged(const QItemSelection &, const QItemSelection &)
propagate selection from model to scene
void onRowsInserted(const QModelIndex &parent, int first, int last)
void alignViews()
aligns SampleView's on graphical canvas
void drawForeground(QPainter *painter, const QRectF &rect)
shows appropriate layer interface to drop while moving ILayerView
void adjustSceneRect()
void mouseMoveEvent(QGraphicsSceneMouseEvent *event)
IView * getViewForItem(SessionItem *item)
QMap< SessionItem *, IView * > m_ItemToView
Correspondance of model's item and scene's view.
void setSampleModel(SampleModel *sampleModel)
const DesignerMimeData * checkDragEvent(QGraphicsSceneDragDropEvent *event)
returns proper MimeData if the object can be hadled by graphics scene
bool isMultiLayerNearby(QGraphicsSceneDragDropEvent *event)
Returns true if there is MultiLayerView nearby during drag event.
SampleViewAligner * m_aligner
void onSceneSelectionChanged()
propagate selection from scene to model
bool isLayerDragged() const
IView * addViewForItem(SessionItem *item)
adds view for item, if it doesn't exists
FilterPropertyProxy * m_proxy
void removeItemViewFromScene(SessionItem *item)
removes view from scene corresponding to given item
void dragMoveEvent(QGraphicsSceneDragDropEvent *event)
handles drag event LayerView can be dragged only over MultiLayerView MultiLayerView can be dragged bo...
void removeConnection(NodeEditorConnection *)
propagates break of connection between views on scene to the model
DesignerScene(QObject *parent=0)
void onRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last)
NodeEditor * m_nodeEditor
void onRowsRemoved(const QModelIndex &parent, int first, int last)
void dropEvent(QGraphicsSceneDragDropEvent *event)
Hadles drop event LayerView can be dropped on MultiLayerView only MultiLayerView can be droped on the...
InstrumentModel * m_instrumentModel
bool isAcceptedByMultiLayer(const DesignerMimeData *mimeData, QGraphicsSceneDragDropEvent *event)
void onEstablishedConnection(NodeEditorConnection *)
propagates connection established by NodeEditor to the model
void setSelectionModel(QItemSelectionModel *model, FilterPropertyProxy *proxy)
MaterialModel * m_materialModel
The FilterPropertyProxy class filters out all PropertyItem's and similar from SessionModel to have on...
static SessionItem * createSampleItems(const QString &name, SampleModel *sampleModel, MaterialModel *materialModel)
Populate sample model with.
static bool isValidExampleName(const QString &name)
Base class for LayerView and MultiLayerView Provides functionality for moving view on top of MultiLay...
Definition: ILayerView.h:25
parent class for graphic representation of all ISampleNode's
Definition: IView.h:25
virtual SessionItem * getItem()
Definition: IView.h:59
void aboutToBeDeleted()
virtual void setParameterizedItem(SessionItem *item)
Definition: IView.cpp:31
virtual void addView(IView *childView, int row=0)
Definition: IView.cpp:53
ConnectableView * getChildView()
returns child view, i.e. the view which owns output port of given connection
NodeEditorPort * inputPort()
NodeEditorPort * outputPort()
ConnectableView * getParentView()
returns parent view, i.e. the view which owns input port of given connection
EPortType getPortType() const
The NodeEditor class implement for QGraphicsScene an editable schematic of the dependency graph,...
Definition: NodeEditor.h:35
void connectionIsEstablished(NodeEditorConnection *)
void install(QGraphicsScene *scene)
Definition: NodeEditor.cpp:25
void selectionModeChangeRequest(int)
static const QString T_CORE
static const QString T_SHELL
static const QString T_TRANSFORMATION
Definition: ParticleItem.h:29
static const QString P_FORM_FACTOR
Definition: ParticleItem.h:25
static const QString T_INTERFERENCE
Main model to hold sample items.
Definition: SampleModel.h:24
Makes alignment of sample droped on graphics scene.
void alignSample(SessionItem *item, QPointF reference={}, bool force_alignment=false)
Aligns sample starting from.
void smartAlign()
Spring based implified algorithm for smart alignment.
static IView * createSampleView(const QString &name)
static bool isValidType(const QString &name)
static const QString P_XPOS
static const QString P_YPOS
void setItemValue(const QString &tag, const QVariant &variant)
Directly set value of item under given tag.
SessionItem * setGroupProperty(const QString &groupTag, const QString &modelType) const
Set the current type of group item.
QString modelType() const
Get model type.
SessionItem * itemForIndex(const QModelIndex &index) const
T * insertItem(SessionItem *parent=nullptr, int row=-1, QString tag="")
Definition: SessionModel.h:125
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const
virtual int rowCount(const QModelIndex &parent) const
virtual bool removeRows(int row, int count, const QModelIndex &parent)
SessionItem * insertNewItem(QString model_type, SessionItem *parent_item=nullptr, int row=-1, QString tag="")
SessionItem * moveItem(SessionItem *item, SessionItem *new_parent=0, int row=-1, const QString &tag="")
Move given parameterized item to the new_parent at given row.
QModelIndex indexOfItem(SessionItem *item) const
@ MULTILAYER
Definition: ViewTypes.h:29