fw4spl
ui/uiMeasurementQt/src/uiMeasurementQt/editor/SLandmarks.cpp
1 /* ***** BEGIN LICENSE BLOCK *****
2  * FW4SPL - Copyright (C) IRCAD, 2017-2018.
3  * Distributed under the terms of the GNU Lesser General Public License (LGPL) as
4  * published by the Free Software Foundation.
5  * ****** END LICENSE BLOCK ****** */
6 
7 #include "uiMeasurementQt/editor/SLandmarks.hpp"
8 
9 #include <fwCom/Signal.hpp>
10 #include <fwCom/Signal.hxx>
11 #include <fwCom/Slot.hpp>
12 #include <fwCom/Slot.hxx>
13 #include <fwCom/Slots.hpp>
14 #include <fwCom/Slots.hxx>
15 
16 #include <fwData/Exception.hpp>
17 #include <fwData/Landmarks.hpp>
18 
19 #include <fwGuiQt/container/QtContainer.hpp>
20 
21 #include <fwServices/macros.hpp>
22 
23 #include <fwTools/NumericRoundCast.hxx>
24 
25 #include <QColorDialog>
26 #include <QLabel>
27 #include <QMessageBox>
28 #include <QPushButton>
29 #include <QTreeWidgetItem>
30 #include <QVBoxLayout>
31 #include <QWidget>
32 
33 namespace uiMeasurementQt
34 {
35 namespace editor
36 {
37 
38 fwServicesRegisterMacro( ::fwGui::editor::IEditor, ::uiMeasurementQt::editor::SLandmarks );
39 
40 //------------------------------------------------------------------------------
41 
42 static const ::fwServices::IService::KeyType s_LANDMARKS_INOUT = "landmarks";
43 static const char* s_GROUP_PROPERTY_NAME = "group";
44 static const int s_GROUP_NAME_ROLE = ::Qt::UserRole + 1;
45 
46 static const ::fwCom::Slots::SlotKeyType s_ADD_PICKED_POINT_SLOT = "addPickedPoint";
47 static const ::fwCom::Slots::SlotKeyType s_ADD_POINT_SLOT = "addPoint";
48 static const ::fwCom::Slots::SlotKeyType s_MODIFY_POINT_SLOT = "modifyPoint";
49 static const ::fwCom::Slots::SlotKeyType s_SELECT_POINT_SLOT = "selectPoint";
50 static const ::fwCom::Slots::SlotKeyType s_DESELECT_POINT_SLOT = "deselectPoint";
51 static const ::fwCom::Slots::SlotKeyType s_REMOVE_POINT_SLOT = "removePoint";
52 static const ::fwCom::Slots::SlotKeyType s_ADD_GROUP_SLOT = "addGroup";
53 static const ::fwCom::Slots::SlotKeyType s_REMOVE_GROUP_SLOT = "removeGroup";
54 static const ::fwCom::Slots::SlotKeyType s_MODIFY_GROUP_SLOT = "modifyGroup";
55 static const ::fwCom::Slots::SlotKeyType s_RENAME_GROUP_SLOT = "renameGroup";
56 
57 //------------------------------------------------------------------------------
58 
60  m_advancedMode(false)
61 {
62  newSlot(s_ADD_PICKED_POINT_SLOT, &SLandmarks::addPickedPoint, this);
63  newSlot(s_ADD_POINT_SLOT, &SLandmarks::addPoint, this);
64  newSlot(s_MODIFY_POINT_SLOT, &SLandmarks::modifyPoint, this);
65  newSlot(s_SELECT_POINT_SLOT, &SLandmarks::selectPoint, this);
66  newSlot(s_DESELECT_POINT_SLOT, &SLandmarks::deselectPoint, this);
67  newSlot(s_ADD_GROUP_SLOT, &SLandmarks::addGroup, this);
68  newSlot(s_REMOVE_POINT_SLOT, &SLandmarks::removePoint, this);
69  newSlot(s_REMOVE_GROUP_SLOT, &SLandmarks::removeGroup, this);
70  newSlot(s_MODIFY_GROUP_SLOT, &SLandmarks::modifyGroup, this);
71  newSlot(s_RENAME_GROUP_SLOT, &SLandmarks::renameGroup, this);
72 
73  std::srand(::fwTools::numericRoundCast<unsigned int>(std::time(NULL)));
74 }
75 
76 //------------------------------------------------------------------------------
77 
79 {
80 }
81 
82 //------------------------------------------------------------------------------
83 
85 {
87 
88  const ::fwServices::IService::ConfigType config = this->getConfigTree();
89 
90  m_defaultLandmarkSize = config.get_optional<float>("size").get_value_or(10.0);
92  "'size' value must be a positive number greater than 0 (current value: " << m_defaultLandmarkSize << ")",
93  m_defaultLandmarkSize <= 0.f);
94 
95  m_defaultLandmarkOpacity = config.get_optional<float>("opacity").get_value_or(1.0);
97  "'opacity' value must be a number between 0.0 and 1.0 (current value: " << m_defaultLandmarkOpacity << ")",
98  m_defaultLandmarkOpacity < 0.f || m_defaultLandmarkOpacity > 1.f);
99 
100  const std::string advancedMode = config.get_optional<std::string>("advanced").get_value_or("no");
101  SLM_FATAL_IF("'advanced' value must be 'yes' or 'no', here : '" + advancedMode + "'.",
102  advancedMode != "yes" && advancedMode != "no");
103 
104  m_advancedMode = (advancedMode == "yes");
105 }
106 
107 //------------------------------------------------------------------------------
108 
110 {
112 
113  ::fwGuiQt::container::QtContainer::sptr qtContainer = ::fwGuiQt::container::QtContainer::dynamicCast(
114  this->getContainer() );
115 
116  QVBoxLayout* layout = new QVBoxLayout();
117  QGridLayout* gridLayout = new QGridLayout();
118 
119  m_visibilityCheckbox = new QCheckBox();
120  QLabel* visibilityLabel = new QLabel(QString("Visibility"));
121  m_sizeSlider = new QSlider(Qt::Horizontal);
122  m_sizeSlider->setMinimum(1);
123  m_sizeSlider->setMaximum(100);
124  QLabel* sizeLabel = new QLabel(QString("Size"));
125  m_opacitySlider = new QSlider(Qt::Horizontal);
126  QLabel* opacityLabel = new QLabel("Opacity");
127  m_shapeSelector = new QComboBox();
128  m_shapeSelector->addItem(QString("Cube"));
129  m_shapeSelector->addItem(QString("Sphere"));
130  QLabel* shapeLabel = new QLabel("Shape");
131 
132  m_newGroupButton = m_advancedMode ? new QPushButton("New Group") : nullptr;
133 
134  m_removeButton = new QPushButton("Delete");
135  m_removeButton->setShortcut(QKeySequence::Delete);
136 
137  gridLayout->addWidget(visibilityLabel, 0, 0);
138  gridLayout->addWidget(m_visibilityCheckbox, 0, 1);
139  gridLayout->addWidget(sizeLabel, 1, 0);
140  gridLayout->addWidget(m_sizeSlider, 1, 1);
141  gridLayout->addWidget(opacityLabel, 2, 0);
142  gridLayout->addWidget(m_opacitySlider, 2, 1);
143  gridLayout->addWidget(shapeLabel, 3, 0);
144  gridLayout->addWidget(m_shapeSelector, 3, 1);
145  gridLayout->addWidget(m_removeButton, 4, 1);
146 
147  m_groupEditorWidget = new QWidget();
148  m_groupEditorWidget->setLayout(gridLayout);
149 
150  m_treeWidget = new QTreeWidget();
151  QLabel* helperTextLabel = new QLabel("Use 'Ctrl+Left Click' to add new landmarks");
152  layout->addWidget(helperTextLabel);
153 
154  if(m_advancedMode)
155  {
156  layout->addWidget(m_newGroupButton);
157  }
158 
159  layout->addWidget(m_treeWidget);
160  layout->addWidget(m_groupEditorWidget);
161  m_groupEditorWidget->hide();
162 
163  QStringList headers;
164  headers << "Group" << "Color";
165 
166  if(m_advancedMode)
167  {
168  // Add empty third column to display and edit point coordinates.
169  headers << "";
170  }
171 
172  m_treeWidget->setHeaderLabels(headers);
173 
174  qtContainer->setLayout( layout );
175 
176  this->updating();
177 }
178 
179 //------------------------------------------------------------------------------
180 
182 {
183  m_treeWidget->blockSignals(true);
184 
185  ::fwData::Landmarks::sptr landmarks = this->getInOut< ::fwData::Landmarks >(s_LANDMARKS_INOUT);
186 
187  m_treeWidget->clear();
188 
189  for (const auto& name : landmarks->getGroupNames())
190  {
191  this->addGroup(name);
192  this->addPoint(name);
193  }
194 
195  QObject::connect(m_treeWidget.data(), &QTreeWidget::itemChanged, this, &SLandmarks::onGroupNameEdited);
196  QObject::connect(m_treeWidget.data(), &QTreeWidget::currentItemChanged, this, &SLandmarks::onSelectionChanged);
197  QObject::connect(m_sizeSlider.data(), &QSlider::valueChanged, this, &SLandmarks::onSizeChanged);
198  QObject::connect(m_opacitySlider.data(), &QSlider::valueChanged, this, &SLandmarks::onOpacityChanged);
199  QObject::connect(m_visibilityCheckbox.data(), &QCheckBox::stateChanged, this, &SLandmarks::onVisibilityChanged);
200  QObject::connect(m_shapeSelector.data(), &QComboBox::currentTextChanged, this, &SLandmarks::onShapeChanged);
201  QObject::connect(m_removeButton.data(), &QPushButton::clicked, this, &SLandmarks::onRemoveSelection);
202  QObject::connect(m_newGroupButton.data(), &QPushButton::clicked, this, &SLandmarks::onAddNewGroup);
203 
204  m_treeWidget->blockSignals(false);
205 }
206 
207 //------------------------------------------------------------------------------
208 
210 {
211  this->destroy();
212 }
213 
214 //------------------------------------------------------------------------------
215 
216 void SLandmarks::onColorButton()
217 {
218  QObject* sender = this->sender();
219 
220  // Create Color choice dialog.
221  auto qtContainer = ::fwGuiQt::container::QtContainer::dynamicCast( this->getContainer() );
222  QWidget* const container = qtContainer->getQtContainer();
223  SLM_ASSERT("container not instanced", container);
224 
225  const QColor oldColor = sender->property("color").value<QColor>();
226  const QColor colorQt = QColorDialog::getColor(oldColor, container, "Select Color", QColorDialog::ShowAlphaChannel);
227  if(colorQt.isValid())
228  {
229  QPushButton* colorButton = dynamic_cast<QPushButton*>(sender);
230  colorButton->setProperty("color", colorQt);
231 
232  setColorButtonIcon(colorButton, colorQt);
233 
234  const std::string groupName = colorButton->property(s_GROUP_PROPERTY_NAME).value<QString>().toStdString();
235 
236  ::fwData::Landmarks::sptr landmarks = this->getInOut< ::fwData::Landmarks >(s_LANDMARKS_INOUT);
237 
238  auto& group = landmarks->getGroup(groupName);
239  ::fwData::Landmarks::ColorType color = {{colorQt.red()/255.f, colorQt.green()/255.f, colorQt.blue()/255.f,
240  colorQt.alpha()/255.f}};
241 
242  m_opacitySlider->setValue(static_cast<int>(color[3] * m_opacitySlider->maximum()));
243 
244  group.m_color = color;
245 
246  auto sig = landmarks->signal< ::fwData::Landmarks::GroupModifiedSignalType >(
248 
249  {
250  ::fwCom::Connection::Blocker block(sig->getConnection(this->slot(s_MODIFY_GROUP_SLOT)));
251  sig->asyncEmit(groupName);
252  }
253  }
254 }
255 
256 //------------------------------------------------------------------------------
257 
258 void SLandmarks::onGroupNameEdited(QTreeWidgetItem* item, int column)
259 {
260  OSLM_ERROR_IF("A column different from the group's name is being edited", column != 0);
261  m_treeWidget->blockSignals(true);
262 
263  if(column == 0)
264  {
265  ::fwData::Landmarks::sptr landmarks = this->getInOut< ::fwData::Landmarks >(s_LANDMARKS_INOUT);
266 
267  QString oldGroupName = item->data(0, s_GROUP_NAME_ROLE).toString();
268  QString newGroupName = item->text(0);
269 
270  if(newGroupName.isEmpty())
271  {
272  QString msg = "The new group name for '" + oldGroupName +
273  "' is empty. Please enter a valid name and try again";
274  QMessageBox msgBox(QMessageBox::Warning, "No group name", msg, QMessageBox::Ok);
275  msgBox.exec();
276 
277  item->setText(0, oldGroupName);
278  }
279  else if(oldGroupName != newGroupName)
280  {
281  try
282  {
283  landmarks->renameGroup(oldGroupName.toStdString(), newGroupName.toStdString());
284 
285  auto sig = landmarks->signal< ::fwData::Landmarks::GroupRenamedSignalType >(
287 
288  {
289  ::fwCom::Connection::Blocker block(sig->getConnection(this->slot(s_RENAME_GROUP_SLOT)));
290  sig->asyncEmit(oldGroupName.toStdString(), newGroupName.toStdString());
291  }
292 
293  item->setData(0, s_GROUP_NAME_ROLE, newGroupName);
294  QWidget* widget = m_treeWidget->itemWidget(item, 1);
295 
296  widget->setProperty(s_GROUP_PROPERTY_NAME, newGroupName);
297  }
298  catch(::fwData::Exception& e)
299  {
300  QString msg = "Can't rename '" + oldGroupName +"' as '" + newGroupName + ".\n" +
301  QString(e.what());
302  QMessageBox msgBox(QMessageBox::Warning, "Can't rename" + oldGroupName, msg, QMessageBox::Ok);
303  msgBox.exec();
304 
305  item->setText(0, oldGroupName);
306  }
307  }
308  }
309  m_treeWidget->blockSignals(false);
310 }
311 
312 //------------------------------------------------------------------------------
313 
314 void SLandmarks::onSelectionChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous)
315 {
316  ::fwData::Landmarks::sptr landmarks = this->getInOut< ::fwData::Landmarks >(s_LANDMARKS_INOUT);
317 
318  if(previous)
319  {
320  auto deselectSig = landmarks->signal< ::fwData::Landmarks::PointDeselectedSignalType >(
322 
323  ::fwCom::Connection::Blocker block(deselectSig->getConnection(this->slot(s_DESELECT_POINT_SLOT)));
324 
325  if(m_advancedMode)
326  {
327  QTreeWidgetItem* previousParent = previous->parent();
328 
329  if(previousParent != nullptr)
330  {
331  const std::string& groupName = previousParent->text(0).toStdString();
332 
333  size_t index = static_cast<size_t>(previousParent->indexOfChild(previous));
334 
335  SLM_ASSERT("index must be inferior to the number of points in '" + groupName +"'.",
336  index < landmarks->getNumberOfPoints(groupName));
337 
338  deselectSig->asyncEmit(groupName, index);
339  }
340  }
341  else
342  {
343  const std::string& groupName = previous->text(0).toStdString();
344 
345  deselectSig->asyncEmit(groupName, 0);
346  }
347  }
348 
349  if(current)
350  {
351  auto selectSig = landmarks->signal< ::fwData::Landmarks::PointSelectedSignalType >(
353 
354  std::string groupName;
355  if(m_advancedMode)
356  {
357  QTreeWidgetItem* currentParent = current->parent();
358 
359  if(currentParent != nullptr)
360  {
361  groupName = currentParent->text(0).toStdString();
362 
363  size_t index = static_cast<size_t>(currentParent->indexOfChild(current));
364 
365  SLM_ASSERT("index must be inferior to the number of points in '" + groupName +"'.",
366  index < landmarks->getNumberOfPoints(groupName));
367 
368  ::fwCom::Connection::Blocker block(selectSig->getConnection(this->slot(s_SELECT_POINT_SLOT)));
369  selectSig->asyncEmit(groupName, index);
370  }
371  else
372  {
373  groupName = current->text(0).toStdString();
374  }
375  }
376  else
377  {
378  groupName = current->text(0).toStdString();
379 
380  ::fwCom::Connection::Blocker block(selectSig->getConnection(this->slot(s_SELECT_POINT_SLOT)));
381  selectSig->asyncEmit(groupName, 0);
382  }
383 
384  const ::fwData::Landmarks::LandmarksGroup& group = landmarks->getGroup(groupName);
385 
386  // Set widget values
387  m_sizeSlider->setValue(static_cast<int>(group.m_size));
388  m_visibilityCheckbox->setChecked(group.m_visibility);
389 
390  const QString shapeText = group.m_shape == ::fwData::Landmarks::Shape::CUBE ? "Cube" : "Sphere";
391  m_shapeSelector->setCurrentText(shapeText);
392 
393  float opacity = group.m_color[3];
394  m_opacitySlider->setValue(static_cast<int>(opacity * m_opacitySlider->maximum()));
395 
396  }
397 
398  m_groupEditorWidget->setHidden(current == nullptr);
399 }
400 
401 //------------------------------------------------------------------------------
402 
403 void SLandmarks::onSizeChanged(int newSize)
404 {
405  ::fwData::Landmarks::sptr landmarks = this->getInOut< ::fwData::Landmarks >(s_LANDMARKS_INOUT);
406 
407  const ::fwData::Landmarks::SizeType realSize = static_cast< ::fwData::Landmarks::SizeType >(newSize);
408 
409  std::string groupName;
410  if(currentSelection(groupName))
411  {
412  landmarks->setGroupSize(groupName, realSize);
413 
414  auto sig = landmarks->signal< ::fwData::Landmarks::GroupModifiedSignalType >(
416 
417  ::fwCom::Connection::Blocker block(sig->getConnection(this->slot(s_MODIFY_GROUP_SLOT)));
418 
419  sig->asyncEmit(groupName);
420  }
421 }
422 
423 //------------------------------------------------------------------------------
424 
425 void SLandmarks::onOpacityChanged(int newOpacity)
426 {
427  ::fwData::Landmarks::sptr landmarks = this->getInOut< ::fwData::Landmarks >(s_LANDMARKS_INOUT);
428 
429  const float sliderSize = static_cast<float>( m_opacitySlider->maximum() - m_opacitySlider->minimum());
430 
431  const float realOpacity = static_cast<float>(newOpacity) / sliderSize;
432 
433  std::string groupName;
434  if(currentSelection(groupName))
435  {
436  const auto groupColor = landmarks->getGroup(groupName).m_color;
437 
438  ::fwData::Landmarks::ColorType newGroupColor
439  = {{groupColor[0], groupColor[1], groupColor[2], realOpacity}};
440 
441  landmarks->setGroupColor(groupName, newGroupColor);
442 
443  QTreeWidgetItem* item = getGroupItem(groupName);
444  QPushButton* colorButton = dynamic_cast<QPushButton*>(m_treeWidget->itemWidget(item, 1));
445 
446  QColor currentColor = colorButton->property("color").value<QColor>();
447  currentColor.setAlphaF(realOpacity);
448  colorButton->setProperty("color", currentColor);
449 
450  setColorButtonIcon(colorButton, currentColor);
451 
452  auto sig = landmarks->signal< ::fwData::Landmarks::GroupModifiedSignalType >(
454 
455  ::fwCom::Connection::Blocker block(sig->getConnection(this->slot(s_MODIFY_GROUP_SLOT)));
456 
457  sig->asyncEmit(groupName);
458  }
459 }
460 
461 //------------------------------------------------------------------------------
462 
463 void SLandmarks::onVisibilityChanged(int visibility)
464 {
465  ::fwData::Landmarks::sptr landmarks = this->getInOut< ::fwData::Landmarks >(s_LANDMARKS_INOUT);
466 
467  std::string groupName;
468  if(currentSelection(groupName))
469  {
470  landmarks->setGroupVisibility(groupName, static_cast<bool>(visibility));
471 
472  auto sig = landmarks->signal< ::fwData::Landmarks::GroupModifiedSignalType >(
474 
475  ::fwCom::Connection::Blocker block(sig->getConnection(this->slot(s_MODIFY_GROUP_SLOT)));
476 
477  sig->asyncEmit(groupName);
478  }
479 }
480 
481 //------------------------------------------------------------------------------
482 
483 void SLandmarks::onShapeChanged(const QString& shape)
484 {
485  ::fwData::Landmarks::sptr landmarks = this->getInOut< ::fwData::Landmarks >(s_LANDMARKS_INOUT);
486 
487  std::string groupName;
488  if(currentSelection(groupName))
489  {
490  SLM_ASSERT("Shape must be 'Cube' or 'Sphere'.", shape == "Cube" || shape == "Sphere");
491  const ::fwData::Landmarks::Shape s
492  = (shape == "Cube") ? ::fwData::Landmarks::Shape::CUBE : ::fwData::Landmarks::Shape::SPHERE;
493 
494  landmarks->setGroupShape(groupName, s);
495 
496  auto sig = landmarks->signal< ::fwData::Landmarks::GroupModifiedSignalType >(
498 
499  ::fwCom::Connection::Blocker block(sig->getConnection(this->slot(s_MODIFY_GROUP_SLOT)));
500 
501  sig->asyncEmit(groupName);
502  }
503 }
504 
505 //------------------------------------------------------------------------------
506 
507 void SLandmarks::onAddNewGroup()
508 {
509  ::fwData::Landmarks::sptr landmarks = this->getInOut< ::fwData::Landmarks >(s_LANDMARKS_INOUT);
510 
511  const std::string groupName = this->generateNewGroupName();
512  landmarks->addGroup(groupName, this->generateNewColor(), m_defaultLandmarkSize);
513 
514  this->addGroup(groupName);
515 
516  auto sig = landmarks->signal< ::fwData::Landmarks::GroupAddedSignalType >(
518 
519  {
520  ::fwCom::Connection::Blocker block(sig->getConnection(this->slot(s_ADD_GROUP_SLOT)));
521  sig->asyncEmit(groupName);
522  }
523 }
524 
525 //------------------------------------------------------------------------------
526 
527 void SLandmarks::onRemoveSelection()
528 {
529  m_treeWidget->blockSignals(true);
530 
531  ::fwData::Landmarks::sptr landmarks = this->getInOut< ::fwData::Landmarks >(s_LANDMARKS_INOUT);
532  QTreeWidgetItem* item = m_treeWidget->currentItem();
533 
534  if(item != nullptr)
535  {
536  const int topLevelIndex = m_treeWidget->indexOfTopLevelItem(item);
537 
538  if(m_advancedMode && topLevelIndex == -1) // Delete point
539  {
540  QTreeWidgetItem* itemParent = item->parent();
541 
542  const size_t index = static_cast<size_t>(itemParent->indexOfChild(item));
543  const std::string& groupName = itemParent->text(0).toStdString();
544 
545  landmarks->removePoint(groupName, index);
546  itemParent->removeChild(item);
547 
548  auto sig = landmarks->signal< ::fwData::Landmarks::PointRemovedSignalType >(
550 
551  {
552  ::fwCom::Connection::Blocker block(sig->getConnection(this->slot(s_REMOVE_POINT_SLOT)));
553  sig->asyncEmit(groupName, index);
554  }
555  }
556  else
557  {
558  const std::string& groupName = item->text(0).toStdString();
559 
560  landmarks->removeGroup(groupName);
561  delete m_treeWidget->takeTopLevelItem(topLevelIndex);
562 
563  auto sig = landmarks->signal< ::fwData::Landmarks::GroupRemovedSignalType >(
565 
566  {
567  ::fwCom::Connection::Blocker block(sig->getConnection(this->slot(s_REMOVE_GROUP_SLOT)));
568  sig->asyncEmit(groupName);
569  }
570  }
571 
572  m_treeWidget->setCurrentItem(nullptr);
573  m_groupEditorWidget->setHidden(true);
574  }
575 
576  m_treeWidget->blockSignals(false);
577 }
578 
579 //------------------------------------------------------------------------------
580 
581 void SLandmarks::addPickedPoint(::fwDataTools::PickingInfo pickingInfo)
582 {
583  if(pickingInfo.m_eventId == ::fwDataTools::PickingInfo::Event::MOUSE_LEFT_UP &&
584  pickingInfo.m_modifierMask & ::fwDataTools::PickingInfo::CTRL)
585  {
586  double* pickedPos = pickingInfo.m_worldPos;
587  ::fwData::Landmarks::PointType newPoint = {{ pickedPos[0], pickedPos[1], pickedPos[2] }};
588 
589  ::fwData::Landmarks::sptr landmarks = this->getInOut< ::fwData::Landmarks >(s_LANDMARKS_INOUT);
590 
591  std::string groupName;
592  QTreeWidgetItem* item = m_treeWidget->currentItem();
593 
594  if(item == nullptr || !m_advancedMode) // No selection or simple mode, create a new group.
595  {
596  groupName = this->generateNewGroupName();
597  landmarks->addGroup(groupName, this->generateNewColor(), m_defaultLandmarkSize);
598 
599  this->addGroup(groupName);
600 
601  auto sig = landmarks->signal< ::fwData::Landmarks::GroupAddedSignalType >(
603 
604  {
605  ::fwCom::Connection::Blocker block(sig->getConnection(this->slot(s_ADD_GROUP_SLOT)));
606  sig->asyncEmit(groupName);
607  }
608  }
609  else // Advanced mode and a point or a group is selected.
610  {
611  const int topLevelIndex = m_treeWidget->indexOfTopLevelItem(item);
612 
613  if(topLevelIndex == -1) // Point selected, add new point to parent group.
614  {
615  item = item->parent();
616  }
617  groupName = item->text(0).toStdString();
618  }
619 
620  landmarks->addPoint(groupName, newPoint);
621  this->addPoint(groupName);
622 
623  auto sig =
625 
626  ::fwCom::Connection::Blocker block(sig->getConnection(this->slot(s_ADD_POINT_SLOT)));
627  sig->asyncEmit(groupName);
628  }
629 }
630 
631 //------------------------------------------------------------------------------
632 
633 void SLandmarks::addPoint(std::string groupName)
634 {
635  if(m_advancedMode)
636  {
637  m_treeWidget->blockSignals(true);
638 
639  ::fwData::Landmarks::sptr landmarks = this->getInOut< ::fwData::Landmarks >(s_LANDMARKS_INOUT);
640 
641  QTreeWidgetItem* item = getGroupItem(groupName);
642 
643  const size_t nbChilds = static_cast<size_t>(item->childCount());
644  const size_t nbPoints = landmarks->getNumberOfPoints(groupName);
645  for(size_t idx = nbChilds; idx < nbPoints; ++idx)
646  {
647  const ::fwData::Landmarks::PointType& newPoint = landmarks->getPoint(groupName, idx);
648 
649  QTreeWidgetItem* pt = new QTreeWidgetItem();
650  for(int i = 0; i < 3; ++i)
651  {
652  pt->setText(i, QString::fromStdString(std::to_string(newPoint[static_cast<size_t>(i)])));
653  }
654  item->addChild(pt);
655  }
656 
657  m_treeWidget->blockSignals(false);
658  }
659 }
660 
661 //------------------------------------------------------------------------------
662 
663 void SLandmarks::addGroup(std::string name)
664 {
665  ::fwData::Landmarks::sptr landmarks = this->getInOut< ::fwData::Landmarks >(s_LANDMARKS_INOUT);
666 
667  const auto& group = landmarks->getGroup(name);
668 
669  QTreeWidgetItem* item = new QTreeWidgetItem();
670  item->setFlags(item->flags() | Qt::ItemIsEditable);
671  item->setData(0, s_GROUP_NAME_ROLE, QString::fromStdString(name));
672 
673  m_treeWidget->addTopLevelItem(item);
674 
675  item->setText(0, QString::fromStdString(name));
676  QPushButton* button = new QPushButton();
677 
678  const QColor color = convertToQColor(group.m_color);
679 
680  setColorButtonIcon(button, color);
681  button->setProperty("color", color);
682  button->setProperty(s_GROUP_PROPERTY_NAME, QString::fromStdString(name));
683 
684  QObject::connect(button, &QPushButton::clicked, this, &SLandmarks::onColorButton);
685 
686  m_treeWidget->setItemWidget(item, 1, button);
687 }
688 
689 //------------------------------------------------------------------------------
690 
691 void SLandmarks::removeGroup(std::string name)
692 {
693  QTreeWidgetItem* item = getGroupItem(name);
694 
695  while(item->childCount() != 0)
696  {
697  item->removeChild(item->child(0));
698  }
699  const int index = m_treeWidget->indexOfTopLevelItem(item);
700  QTreeWidgetItem* topItem = m_treeWidget->takeTopLevelItem(index);
701  delete topItem;
702 }
703 
704 //------------------------------------------------------------------------------
705 
706 void SLandmarks::removePoint(std::string groupName, size_t index)
707 {
708  m_treeWidget->blockSignals(true);
709 
710  QTreeWidgetItem* item = getGroupItem(groupName);
711 
712  OSLM_ASSERT("Index must be less than " << item->childCount(), static_cast<int>(index) < item->childCount());
713 
714  QTreeWidgetItem* pointItem = item->child(static_cast<int>(index));
715  item->removeChild(pointItem);
716  m_treeWidget->blockSignals(false);
717 }
718 
719 //------------------------------------------------------------------------------
720 
721 void SLandmarks::renameGroup(std::string oldName, std::string newName)
722 {
723  m_treeWidget->blockSignals(true);
724 
725  const QString qtNewName = QString::fromStdString(newName);
726  QTreeWidgetItem* item = getGroupItem(oldName);
727  item->setData(0, s_GROUP_NAME_ROLE, qtNewName);
728  QWidget* widget = m_treeWidget->itemWidget(item, 1);
729  widget->setProperty(s_GROUP_PROPERTY_NAME, qtNewName);
730 
731  item->setText(0, qtNewName);
732  m_treeWidget->blockSignals(false);
733 }
734 
735 //------------------------------------------------------------------------------
736 
737 void SLandmarks::modifyGroup(std::string name)
738 {
739  QTreeWidgetItem* item = getGroupItem(name);
740 
741  item->setText(0, name.c_str());
742 
743  ::fwData::Landmarks::sptr landmarks = this->getInOut< ::fwData::Landmarks >(s_LANDMARKS_INOUT);
744 
745  const ::fwData::Landmarks::LandmarksGroup& group = landmarks->getGroup(name);
746 
747  QPushButton* colorButton = dynamic_cast< QPushButton* >(m_treeWidget->itemWidget(item, 1));
748 
749  const QColor color = convertToQColor(group.m_color);
750 
751  setColorButtonIcon(colorButton, color);
752 
753  bool groupSelected = item->isSelected();
754 
755  if(m_advancedMode) // Check if a child is selected.
756  {
757  for(int i = 0; i < item->childCount() && !groupSelected; ++i)
758  {
759  groupSelected = item->child(i)->isSelected();
760  }
761  }
762 
763  if(groupSelected)
764  {
765  const ::fwData::Landmarks::LandmarksGroup& group = landmarks->getGroup(name);
766 
767  // Set widget values
768  m_sizeSlider->setValue(static_cast<int>(group.m_size));
769  m_visibilityCheckbox->setChecked(group.m_visibility);
770 
771  const QString shapeText = group.m_shape == ::fwData::Landmarks::Shape::CUBE ? "Cube" : "Sphere";
772  m_shapeSelector->setCurrentText(shapeText);
773 
774  const float opacity = group.m_color[3];
775  m_opacitySlider->setValue(static_cast<int>(opacity * m_opacitySlider->maximum()));
776  }
777 
778 }
779 
780 //------------------------------------------------------------------------------
781 
782 void SLandmarks::modifyPoint(std::string groupName, size_t index)
783 {
784  if( m_advancedMode )
785  {
786  ::fwData::Landmarks::sptr landmarks = this->getInOut< ::fwData::Landmarks >(s_LANDMARKS_INOUT);
787 
788  auto itemList = m_treeWidget->findItems(QString::fromStdString(groupName), Qt::MatchExactly);
789 
790  SLM_ASSERT("Only a single item can be named '" + groupName + "'", itemList.size() == 1);
791 
792  QTreeWidgetItem* item = itemList.at(0);
793 
794  OSLM_ASSERT("Index must be less than " << item->childCount(), static_cast<int>(index) < item->childCount());
795 
796  QTreeWidgetItem* pointItem = item->child(static_cast<int>(index));
797  const ::fwData::Landmarks::PointType& point = landmarks->getPoint(groupName, index);
798 
799  m_treeWidget->blockSignals(true);
800  for(int i = 0; i < 3; ++i)
801  {
802  pointItem->setText(i, QString("%1").arg(point[static_cast<size_t>(i)]));
803  }
804  m_treeWidget->blockSignals(false);
805  }
806 }
807 
808 //------------------------------------------------------------------------------
809 
810 void SLandmarks::selectPoint(std::string groupName, size_t index)
811 {
812  m_treeWidget->blockSignals(true);
813 
814  QTreeWidgetItem* currentItem = getGroupItem(groupName);
815 
816  if(m_advancedMode)
817  {
818  OSLM_ASSERT(
819  "Index must be less than " << currentItem->childCount(),
820  static_cast<int>(index) < currentItem->childCount());
821 
822  currentItem = currentItem->child(static_cast<int>(index));
823  }
824  m_treeWidget->setCurrentItem(currentItem);
825 
826  ::fwData::Landmarks::sptr landmarks = this->getInOut< ::fwData::Landmarks >(s_LANDMARKS_INOUT);
827 
828  const ::fwData::Landmarks::LandmarksGroup& group = landmarks->getGroup(groupName);
829 
830  // Set widget values
831  m_sizeSlider->setValue(static_cast<int>(group.m_size));
832  m_visibilityCheckbox->setChecked(group.m_visibility);
833 
834  const QString shapeText = group.m_shape == ::fwData::Landmarks::Shape::CUBE ? "Cube" : "Sphere";
835  m_shapeSelector->setCurrentText(shapeText);
836 
837  const float opacity = group.m_color[3];
838  m_opacitySlider->setValue(static_cast<int>(opacity * m_opacitySlider->maximum()));
839 
840  m_groupEditorWidget->show();
841  m_treeWidget->blockSignals(false);
842 }
843 
844 //------------------------------------------------------------------------------
845 
846 void SLandmarks::deselectPoint(std::string, size_t)
847 {
848  m_treeWidget->blockSignals(true);
849  m_treeWidget->setCurrentItem(nullptr);
850  m_groupEditorWidget->hide();
851  m_treeWidget->blockSignals(false);
852 }
853 
854 //------------------------------------------------------------------------------
855 
856 std::string SLandmarks::generateNewGroupName() const
857 {
858  static size_t groupCount = 0;
859 
860  const ::fwData::Landmarks::sptr landmarks = this->getInOut< ::fwData::Landmarks >(s_LANDMARKS_INOUT);
861 
862  const ::fwData::Landmarks::GroupNameContainer groupNames = landmarks->getGroupNames();
863 
864  const std::string newGroupNamePrefix = m_advancedMode ? "Group_" : "Point_";
865 
866  while(std::find(groupNames.begin(), groupNames.end(),
867  newGroupNamePrefix + std::to_string(groupCount)) != groupNames.end())
868  {
869  ++groupCount;
870  }
871 
872  return newGroupNamePrefix + std::to_string(groupCount);
873 }
874 
875 //------------------------------------------------------------------------------
876 
877 std::array<float, 4> SLandmarks::generateNewColor()
878 {
879  const std::array<float,
880  4> color = {{rand()%255/255.f, rand()%255/255.f, rand()%255/255.f, m_defaultLandmarkOpacity}};
881  return color;
882 }
883 
884 //------------------------------------------------------------------------------
885 
886 bool SLandmarks::currentSelection(std::string& selection) const
887 {
888  QTreeWidgetItem* item = m_treeWidget->currentItem();
889 
890  const bool selectedGroup = (item != nullptr);
891 
892  if(selectedGroup)
893  {
894  const int topLevelIndex = m_treeWidget->indexOfTopLevelItem(item);
895 
896  if(m_advancedMode && topLevelIndex == -1)
897  {
898  item = item->parent();
899  }
900 
901  selection = item->text(0).toStdString();
902  }
903 
904  return selectedGroup;
905 }
906 
907 //------------------------------------------------------------------------------
908 
909 QColor SLandmarks::convertToQColor(const ::fwData::Landmarks::ColorType& color)
910 {
911  return QColor(
912  static_cast<int>(color[0]*255),
913  static_cast<int>(color[1]*255),
914  static_cast<int>(color[2]*255),
915  static_cast<int>(color[3]*255)
916  );
917 }
918 
919 //------------------------------------------------------------------------------
920 
921 void SLandmarks::setColorButtonIcon(QPushButton* button, const QColor& color)
922 {
923  const int iconSize = button->style()->pixelMetric(QStyle::PM_LargeIconSize);
924  QPixmap pix(iconSize, iconSize);
925  pix.fill(color);
926 
927  button->setIcon(QIcon(pix));
928 }
929 
930 //------------------------------------------------------------------------------
931 
932 QTreeWidgetItem* SLandmarks::getGroupItem(const std::string& groupName) const
933 {
934  const auto itemList = m_treeWidget->findItems(QString::fromStdString(groupName), Qt::MatchExactly);
935 
936  SLM_ASSERT("Only a single item can be named '" + groupName + "'", itemList.size() == 1);
937 
938  return itemList.at(0);
939 }
940 
941 //------------------------------------------------------------------------------
942 
944 {
945  KeyConnectionsMap connections;
946 
947  connections.push(s_LANDMARKS_INOUT, ::fwData::Landmarks::s_MODIFIED_SIG, s_UPDATE_SLOT);
948  connections.push(s_LANDMARKS_INOUT, ::fwData::Landmarks::s_POINT_ADDED_SIG, s_ADD_POINT_SLOT);
949  connections.push(s_LANDMARKS_INOUT, ::fwData::Landmarks::s_POINT_MODIFIED_SIG, s_MODIFY_POINT_SLOT);
950  connections.push(s_LANDMARKS_INOUT, ::fwData::Landmarks::s_POINT_SELECTED_SIG, s_SELECT_POINT_SLOT);
951  connections.push(s_LANDMARKS_INOUT, ::fwData::Landmarks::s_POINT_DESELECTED_SIG, s_DESELECT_POINT_SLOT);
952  connections.push(s_LANDMARKS_INOUT, ::fwData::Landmarks::s_GROUP_ADDED_SIG, s_ADD_GROUP_SLOT);
953  connections.push(s_LANDMARKS_INOUT, ::fwData::Landmarks::s_GROUP_REMOVED_SIG, s_REMOVE_GROUP_SLOT);
954  connections.push(s_LANDMARKS_INOUT, ::fwData::Landmarks::s_POINT_REMOVED_SIG, s_REMOVE_POINT_SLOT);
955  connections.push(s_LANDMARKS_INOUT, ::fwData::Landmarks::s_GROUP_MODIFIED_SIG, s_MODIFY_GROUP_SLOT);
956  connections.push(s_LANDMARKS_INOUT, ::fwData::Landmarks::s_GROUP_RENAMED_SIG, s_RENAME_GROUP_SLOT);
957 
958  return connections;
959 }
960 
961 } // namespace editor
962 } // namespace uiMeasurementQt
This class is a helper to define the connections of a service and its data.
Definition: IService.hpp:454
virtual UIMEASUREMENTQT_API ~SLandmarks() noexcept
Destructor. Do nothing.
double m_worldPos[3]
Position clicked in world coordinates.
Definition: PickingInfo.hpp:44
virtual void configuring() override
Configure the service before starting. Apply the configuration to service.
#define OSLM_ASSERT(message, cond)
work like &#39;assert&#39; from &#39;cassert&#39;, with in addition a message logged by spylog (with FATAL loglevel) ...
Definition: spyLog.hpp:310
Class allowing to block a Connection.
Definition: Connection.hpp:20
static FWDATA_APIconst::fwCom::Signals::SignalKeyType s_POINT_DESELECTED_SIG
Type of signal when a group is added.
Defines the service interface managing the editor service for object.
Definition: IEditor.hpp:25
FWGUI_API void destroy()
Stops sub-views and toobar services. Destroys view, sub-views and toolbar containers.
static FWDATA_APIconst::fwCom::Signals::SignalKeyType s_GROUP_ADDED_SIG
Type of signal when a group is added.
Implements data exception class.
static FWDATA_APIconst::fwCom::Signals::SignalKeyType s_GROUP_REMOVED_SIG
Type of signal when a group is added.
static FWDATA_APIconst::fwCom::Signals::SignalKeyType s_POINT_SELECTED_SIG
Type of signal when a group is added.
std::int8_t m_modifierMask
Modifier mask.
Definition: PickingInfo.hpp:52
static FWDATA_APIconst::fwCom::Signals::SignalKeyType s_POINT_ADDED_SIG
Type of signal when a group is added.
static FWDATA_APIconst::fwCom::Signals::SignalKeyType s_GROUP_RENAMED_SIG
Type of signal when a group is added.
#define SLM_ASSERT(message, cond)
work like &#39;assert&#39; from &#39;cassert&#39;, with in addition a message logged by spylog (with FATAL loglevel) ...
Definition: spyLog.hpp:308
Structure to store picking information.
Definition: PickingInfo.hpp:20
FWGUI_API void create()
Creates view, sub-views and toolbar containers. Manages sub-views and toobar services.
#define SLM_FATAL_IF(message, cond)
Definition: spyLog.hpp:287
virtual UIMEASUREMENTQT_API KeyConnectionsMap getAutoConnections() const override
Returns proposals to connect service slots to associated objects signals, this method is used for obj...
static FWDATA_APIconst::fwCom::Signals::SignalKeyType s_MODIFIED_SIG
Key in m_signals map of signal m_sigModified.
static FWDATA_APIconst::fwCom::Signals::SignalKeyType s_POINT_MODIFIED_SIG
Type of signal when a group is added.
static FWDATA_APIconst::fwCom::Signals::SignalKeyType s_GROUP_MODIFIED_SIG
Type of signal when a group is added.
static FWDATA_APIconst::fwCom::Signals::SignalKeyType s_POINT_REMOVED_SIG
Type of signal when a group is added.
UIMEASUREMENTQT_API SLandmarks() noexcept
Constructor. Do nothing.
#define OSLM_FATAL_IF(message, cond)
Definition: spyLog.hpp:289
static FWSERVICES_APIconst::fwCom::Slots::SlotKeyType s_UPDATE_SLOT
Slot to call start method.
Definition: IService.hpp:177
#define OSLM_ERROR_IF(message, cond)
Definition: spyLog.hpp:278
FWGUI_API void initialize()
Initialize managers.
This service defines a graphical editor to edit landmarks.
Event m_eventId
Mouse event.
Definition: PickingInfo.hpp:50
FWSERVICES_API ConfigType getConfigTree() const
Return the configuration, in an boost property tree.
Definition: IService.cpp:247