fw4spl
ActivityDataView.cpp
1 /* ***** BEGIN LICENSE BLOCK *****
2  * FW4SPL - Copyright (C) IRCAD, 2016-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 "uiMedDataQt/widget/ActivityDataView.hpp"
8 
9 #include <fwActivities/IActivityValidator.hpp>
10 #include <fwActivities/IObjectValidator.hpp>
11 #include <fwActivities/IValidator.hpp>
12 
13 #include <fwData/Boolean.hpp>
14 #include <fwData/Composite.hpp>
15 #include <fwData/Float.hpp>
16 #include <fwData/Integer.hpp>
17 #include <fwData/String.hpp>
18 #include <fwData/TransformationMatrix3D.hpp>
19 #include <fwData/Vector.hpp>
20 
21 #include <fwDataCamp/getObject.hpp>
22 #include <fwDataCamp/visitor/CompareObjects.hpp>
23 
24 #include <fwIO/ioTypes.hpp>
25 
26 #include <fwMedData/Patient.hpp>
27 #include <fwMedData/Series.hpp>
28 #include <fwMedData/SeriesDB.hpp>
29 #include <fwMedData/Study.hpp>
30 
31 #include <fwRuntime/Convert.hpp>
32 
33 #include <fwServices/IService.hpp>
34 #include <fwServices/op/Add.hpp>
35 #include <fwServices/registry/ObjectService.hpp>
36 #include <fwServices/registry/ServiceConfig.hpp>
37 
38 #include <QApplication>
39 #include <QDropEvent>
40 #include <QHBoxLayout>
41 #include <QInputDialog>
42 #include <QKeyEvent>
43 #include <QLabel>
44 #include <QMessageBox>
45 #include <QMimeData>
46 #include <QPixmap>
47 #include <QPushButton>
48 #include <QStandardItem>
49 #include <QTableWidget>
50 #include <QVBoxLayout>
51 
52 namespace uiMedDataQt
53 {
54 namespace widget
55 {
56 
57 const int ActivityDataView::s_UID_ROLE = Qt::UserRole + 1;
58 
59 //-----------------------------------------------------------------------------
60 
61 ActivityDataView::ActivityDataView(QWidget* parent) :
62  QTabWidget(parent)
63 {
64 }
65 
66 //-----------------------------------------------------------------------------
67 
68 ActivityDataView::~ActivityDataView()
69 {
70 }
71 
72 //-----------------------------------------------------------------------------
73 
74 void ActivityDataView::clear()
75 {
76  m_importedObject.clear();
77  m_treeWidgets.clear();
78  QTabWidget::clear();
79 }
80 
81 //-----------------------------------------------------------------------------
82 
83 bool ActivityDataView::eventFilter(QObject* obj, QEvent* event)
84 {
85  // get dropped data in tree widget
86  if (event->type() == QEvent::Drop)
87  {
88  QDropEvent* dropEvent = static_cast<QDropEvent*>(event);
89 
90  size_t index = static_cast<size_t>(this->currentIndex());
91  ::fwActivities::registry::ActivityRequirement requirement = m_activityInfo.requirements[index];
92  QPointer<QTreeWidget> tree = m_treeWidgets[index];
93 
94  // get dropped item from event mimedata
95  const QMimeData* qMimeData = dropEvent->mimeData();
96 
97  QByteArray encoded = qMimeData->data("application/x-qabstractitemmodeldatalist");
98  QDataStream stream(&encoded, QIODevice::ReadOnly);
99 
100  QList<QTreeWidgetItem* > itemList;
101  QTreeWidgetItem* item;
102 
103  // Get the dropped item
104  while (!stream.atEnd())
105  {
106  int row, col;
107 
108  QMap<int, QVariant> roleDataMap;
109 
110  stream >> row >> col >> roleDataMap;
111 
112  if(col == 0)
113  {
114  item = new QTreeWidgetItem();
115  itemList.push_back(item);
116  }
117 
118  QList<int> keys = roleDataMap.keys();
119  for (int key : keys)
120  {
121  item->setData(col, key, roleDataMap[key]);
122  }
123  }
124 
125  // check if the limit number of data is exceeded
126  int nbChild = tree->topLevelItemCount() + itemList.size();
127  if (static_cast<unsigned int>(nbChild) > requirement.maxOccurs )
128  {
129  QMessageBox::warning(this, "Drop", "The maximum number of element is reached.\n"
130  "You must remove one before adding another.");
131  }
132  else
133  {
134  // add the dropped item in the tree
135  for (QTreeWidgetItem* itemToAdd: itemList)
136  {
137  itemToAdd->setFlags(itemToAdd->flags() & ~Qt::ItemIsDropEnabled);
138  std::string uid = itemToAdd->data(int(ColumnType::NAME), s_UID_ROLE).toString().toStdString();
139  if (!uid.empty())
140  {
141  // insert the object if it is in the required type
142  ::fwData::Object::sptr obj = ::fwData::Object::dynamicCast(::fwTools::fwID::getObject(uid));
143  if (obj && obj->isA(requirement.type))
144  {
145  // Insert the new object
146  tree->addTopLevelItem(itemToAdd);
147  }
148  }
149  }
150  }
151  return true;
152  }
153  else if (event->type() == QEvent::KeyPress)
154  {
155  QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
156  if (keyEvent->key() == Qt::Key_Delete)
157  {
158  this->removeSelectedObjects();
159  return true;
160  }
161  }
162 
163  // standard event processing
164  return QObject::eventFilter(obj, event);
165 
166 }
167 
168 //-----------------------------------------------------------------------------
169 
170 void ActivityDataView::fillInformation(const ::fwActivities::registry::ActivityInfo& info)
171 {
172  namespace ActReg = ::fwActivities::registry;
173 
174  m_activityInfo = info;
175  this->clear();
176 
177  ActReg::ActivityInfo::RequirementsType reqVect = m_activityInfo.requirements;
178  for(const ActReg::ActivityRequirement& req : reqVect)
179  {
180  QVBoxLayout* layout = new QVBoxLayout();
181  QWidget* widget = new QWidget();
182  widget->setLayout(layout);
183 
184  QHBoxLayout* infoLayout = new QHBoxLayout();
185  layout->addLayout(infoLayout);
186 
187  QVBoxLayout* typeLayout = new QVBoxLayout();
188  QVBoxLayout* txtLayout = new QVBoxLayout();
189  infoLayout->addLayout(typeLayout);
190  infoLayout->addSpacerItem(new QSpacerItem(20, 0));
191  infoLayout->addLayout(txtLayout, 1);
192 
193  ObjectIconMapType::iterator iter = m_objectIcons.find(req.type);
194  if (iter != m_objectIcons.end())
195  {
196  QString filename = QString::fromStdString(iter->second);
197 
198  this->addTab(widget, QIcon(filename), QString::fromStdString(req.name));
199  QLabel* icon = new QLabel();
200  icon->setAlignment(Qt::AlignHCenter);
201  QPixmap pixmap(filename);
202  icon->setPixmap(pixmap.scaled(100, 100));
203  typeLayout->addWidget(icon);
204  }
205  else
206  {
207  this->addTab(widget, QString::fromStdString(req.name));
208  }
209 
210  QLabel* type = new QLabel(QString("<small>%1</small>").arg(QString::fromStdString(req.type)));
211  type->setAlignment(Qt::AlignHCenter);
212  typeLayout->addWidget(type);
213 
214  QLabel* name = new QLabel(QString("<h2>%1</h2>").arg(QString::fromStdString(req.name)));
215  name->setStyleSheet("QLabel { font: bold; }");
216  txtLayout->addWidget(name);
217 
218  QLabel* description = new QLabel(QString::fromStdString(req.description));
219  description->setStyleSheet("QLabel { font: italic; }");
220  txtLayout->addWidget(description);
221 
222  txtLayout->addStretch();
223 
224  QLabel* nb = new QLabel();
225  nb->setStyleSheet("QLabel { font: bold; }");
226  layout->addWidget(nb);
227 
228  QPointer<QTreeWidget> tree = new QTreeWidget();
229  m_treeWidgets.push_back(tree);
230  if (req.maxOccurs == 0)
231  {
232  nb->setText("No object is required, it will be automatically created.");
233  tree->setEnabled(false);
234  }
235  else if (req.minOccurs == 1 && req.maxOccurs == 1)
236  {
237  nb->setText("One object is required: ");
238  }
239  else
240  {
241  QString nbObj("Number of required object");
242  if (req.maxOccurs == std::numeric_limits<unsigned int>::max())
243  {
244  nbObj.append(QString(" >= %1").arg(req.minOccurs));
245  }
246  else
247  {
248  nbObj.append(QString(": [%1-%2]").arg(req.minOccurs).arg(req.maxOccurs));
249  }
250  nb->setText(nbObj);
251  }
252 
253  QHBoxLayout* treeLayout = new QHBoxLayout();
254  QVBoxLayout* buttonLayout = new QVBoxLayout();
255  if (req.type == "::fwData::String" || req.type == "::fwData::Boolean"
256  || req.type == "::fwData::Integer" || req.type == "::fwData::Float"
257  || req.type == "::fwData::TransformationMatrix3D")
258  {
259  QPushButton* buttonNew = new QPushButton("New");
260  buttonNew->setToolTip("Create a new empty object");
261  buttonLayout->addWidget(buttonNew);
262  QObject::connect(buttonNew, &QPushButton::clicked, this, &ActivityDataView::createNewObject);
263  }
264 
265  QPushButton* buttonAdd = new QPushButton("Load");
266  QPushButton* buttonRemove = new QPushButton("Remove");
267  QPushButton* buttonClear = new QPushButton("Clear");
268  buttonLayout->addWidget(buttonAdd);
269  buttonAdd->setToolTip(QString("Load an object of type '%1'.").arg(QString::fromStdString(req.type)));
270 
271  // If type is a series, we add a button to import the data from a SeriesDB
272  ::fwData::Object::sptr newObject = ::fwData::factory::New(req.type);
273  if (newObject && ::fwMedData::Series::dynamicCast(newObject))
274  {
275  QPushButton* buttonAddFromSDB = new QPushButton("Import");
276  buttonLayout->addWidget(buttonAddFromSDB);
277  buttonAddFromSDB->setToolTip(QString("Import a SeriesDB and extract the N first objects of type '%1', with "
278  "N the maximum number of required objects.").
279  arg(QString::fromStdString(req.type)));
280  QObject::connect(buttonAddFromSDB, &QPushButton::clicked, this, &ActivityDataView::importObjectFromSDB);
281  }
282 
283  buttonLayout->addWidget(buttonRemove);
284  buttonRemove->setToolTip(QString("Remove the selected objects"));
285  buttonLayout->addWidget(buttonClear);
286  buttonClear->setToolTip(QString("Remove all the objects"));
287  buttonLayout->addStretch();
288  QObject::connect(buttonAdd, &QPushButton::clicked, this, &ActivityDataView::importObject);
289  QObject::connect(buttonRemove, &QPushButton::clicked, this, &ActivityDataView::removeSelectedObjects);
290  QObject::connect(buttonClear, &QPushButton::clicked, this, &ActivityDataView::clearTree);
291  treeLayout->addLayout(buttonLayout);
292 
293  QStringList headers;
294  headers << "" << "object type" << "description" << "patient name" << "study description" << "" << "" << ""
295  << "" << "" << "" << "";
296  tree->setHeaderLabels(headers);
297 
298  treeLayout->addWidget(tree, 1);
299  tree->setAlternatingRowColors(true);
300  tree->setAcceptDrops(true);
301  tree->setDragDropMode(QAbstractItemView::DropOnly);
302  tree->viewport()->installEventFilter(this);
303  tree->installEventFilter(this);
304 
305  QObject::connect(tree.data(), &QTreeWidget::itemDoubleClicked, this,
306  &ActivityDataView::onTreeItemDoubleClicked);
307  layout->addLayout(treeLayout, 1);
308 
309  //TODO better management of composite container
310  }
311 
312  for (int i = 1; i < this->count(); ++i)
313  {
314  this->setTabEnabled(i, false);
315  }
316 }
317 
318 //-----------------------------------------------------------------------------
319 
320 void ActivityDataView::fillInformation(const ::fwMedData::ActivitySeries::sptr& activitySeries)
321 {
322  namespace ActReg = ::fwActivities::registry;
324  info = ::fwActivities::registry::Activities::getDefault()->getInfo(activitySeries->getActivityConfigId());
325  m_activityInfo = info;
326 
327  ::fwData::Composite::sptr data = activitySeries->getData();
328 
329  this->fillInformation(info);
330 
331  for (size_t i = 0; i < m_activityInfo.requirements.size(); ++i)
332  {
333  ::fwActivities::registry::ActivityRequirement req = m_activityInfo.requirements[i];
334 
335  ::fwData::Object::sptr obj = data->at< ::fwData::Object >(req.name);
336  if (obj)
337  {
338  if ((req.minOccurs == 0 && req.maxOccurs == 0) ||
339  (req.minOccurs == 1 && req.maxOccurs == 1) ||
340  req.create)
341  {
342  this->addObjectItem(i, obj);
343  }
344  else
345  {
346  if (req.container == "vector")
347  {
348  ::fwData::Vector::sptr vector = ::fwData::Vector::dynamicCast(obj);
349  if (vector)
350  {
351  for (auto subObj : vector->getContainer())
352  {
353  this->addObjectItem(i, subObj);
354  }
355  }
356  else
357  {
358  SLM_ERROR("Object param '" + req.name + "' must be a '::fwData::Vector'");
359  }
360  }
361  else // container == composite
362  {
363  ::fwData::Composite::sptr composite = ::fwData::Composite::dynamicCast(obj);
364  if (composite)
365  {
366  for (auto subObj : composite->getContainer())
367  {
368  this->addObjectItem(i, subObj.second);
369  }
370  }
371  else
372  {
373  SLM_ERROR("Object param '" + req.name + "' must be a '::fwData::Composite'");
374  }
375  }
376  }
377  }
378  this->setTabEnabled(int(i), true);
379  }
380 }
381 
382 //-----------------------------------------------------------------------------
383 
384 ::fwData::Object::sptr ActivityDataView::checkData(size_t index, std::string& errorMsg)
385 {
386  ::fwData::Object::sptr data;
387 
388  ::fwActivities::registry::ActivityRequirement req = m_activityInfo.requirements[index];
389  QPointer<QTreeWidget> tree = m_treeWidgets[index];
390 
391  bool ok = true;
392  if ((req.minOccurs == 1 && req.maxOccurs == 1) ||
393  (req.minOccurs == 0 && req.maxOccurs == 0) ||
394  req.create) // One object is required
395  {
396  if (tree->topLevelItemCount() == 1)
397  {
398  QTreeWidgetItem* item = tree->topLevelItem(0);
399 
400  std::string uid =
401  item->data(int(ColumnType::NAME), ActivityDataView::s_UID_ROLE).toString().toStdString();
402 
403  ::fwData::Object::sptr obj = ::fwData::Object::dynamicCast(::fwTools::fwID::getObject(uid));
404  if (obj && obj->isA(req.type))
405  {
406  data = obj;
407  }
408  else
409  {
410  ok = false;
411  errorMsg += "\n - The parameter '" + req.name + "' must be a '" + req.type + "'.";
412  }
413  }
414  else
415  {
416  if ((req.minOccurs == 0 && req.maxOccurs == 0) || req.create)
417  {
418  data = ::fwData::factory::New(req.type);
419  }
420  else
421  {
422  ok = false;
423  errorMsg += "\n - The parameter '" + req.name + "' is required but is not defined.";
424  }
425  }
426  }
427  else // optional object or several objects
428  {
429  unsigned int nbObj = static_cast<unsigned int>(tree->topLevelItemCount());
430 
431  if (nbObj < req.minOccurs)
432  {
433  ok = false;
434  errorMsg += "\n - The parameter '" + req.name + "' must contain at least " +
435  std::to_string(req.minOccurs) + " elements.";
436  }
437  else if (nbObj > req.maxOccurs)
438  {
439  ok = false;
440  errorMsg += "\n - The parameter '" + req.name + "' must contain at most " +
441  std::to_string(req.maxOccurs) + " elements.";
442  }
443  else
444  {
445  if (req.container == "vector")
446  {
447  ::fwData::Vector::sptr vector = ::fwData::Vector::New();
448 
449  for (unsigned int i = 0; i < nbObj; ++i)
450  {
451  QTreeWidgetItem* itemData = tree->topLevelItem(int(i));
452  std::string uid =
453  itemData->data(int(ColumnType::NAME), s_UID_ROLE).toString().toStdString();
454 
455  ::fwData::Object::sptr obj = ::fwData::Object::dynamicCast(::fwTools::fwID::getObject(uid));
456  if (obj && obj->isA(req.type))
457  {
458  vector->getContainer().push_back(obj);
459  }
460  else
461  {
462  ok = false;
463  errorMsg += "\n - The parameter '" + req.name + "' must be a " + req.type + ".";
464  }
465  }
466  if (ok)
467  {
468  data = vector;
469  }
470  }
471  else // container == composite
472  {
473  ::fwData::Composite::sptr composite = ::fwData::Composite::New();
474 
475  for (unsigned int i = 0; i < nbObj; ++i)
476  {
477  QTreeWidgetItem* itemData = tree->topLevelItem(int(i));
478  std::string uid =
479  itemData->data(int(ColumnType::NAME), s_UID_ROLE).toString().toStdString();
480 
481  ::fwData::Object::sptr obj = ::fwData::Object::dynamicCast(::fwTools::fwID::getObject(uid));
482  if (obj && obj->isA(req.type))
483  {
484  std::string key = req.keys[i].key;
485  std::string path = req.keys[i].path;
486  if(path.empty())
487  {
488  (*composite)[key] = obj;
489  }
490  else
491  {
492  (*composite)[key] = ::fwDataCamp::getObject( obj, path );
493  }
494  }
495  else
496  {
497  ok = false;
498  errorMsg += "\n - The parameter '" + req.name + "' must be a " + req.type + ".";
499  }
500  }
501  if (ok)
502  {
503  data = composite;
504  }
505 
506  }
507  }
508  }
509 
510  if (data && !req.validator.empty())
511  {
513  ::fwActivities::IValidator::sptr validator = ::fwActivities::validator::factory::New(req.validator);
514  ::fwActivities::IObjectValidator::sptr dataValidator = ::fwActivities::IObjectValidator::dynamicCast(validator);
515  SLM_ASSERT("Validator '" + req.validator + "' instantiation failed", dataValidator);
516 
517  ::fwActivities::IValidator::ValidationType validation = dataValidator->validate(data);
518  if(!validation.first)
519  {
520  errorMsg += "\n" + validation.second;
521  data = nullptr;
522  }
523  }
524 
525  return data;
526 }
527 
528 //-----------------------------------------------------------------------------
529 
530 bool ActivityDataView::checkAndComputeData(const ::fwMedData::ActivitySeries::sptr& actSeries, std::string& errorMsg)
531 {
532  namespace ActReg = ::fwActivities::registry;
533 
534  ::fwData::Composite::sptr data = actSeries->getData();
535 
536  bool ok = true;
537  errorMsg += "The required data are not correct:";
538 
539  // Check if all required data are present
540  for (size_t i = 0; i < m_activityInfo.requirements.size(); ++i)
541  {
542  ::fwActivities::registry::ActivityRequirement req = m_activityInfo.requirements[i];
543  std::string msg;
544  ::fwData::Object::sptr obj = this->checkData(i, msg);
545  if (obj)
546  {
547  (*data)[req.name] = obj;
548  }
549  else
550  {
551  ok = false;
552  errorMsg += msg;
553  }
554  }
555 
556  for (std::string validatotImpl : m_activityInfo.validatorsImpl)
557  {
559  ::fwActivities::IValidator::sptr validator = ::fwActivities::validator::factory::New(validatotImpl);
560 
561  ::fwActivities::IActivityValidator::sptr activityValidator =
562  ::fwActivities::IActivityValidator::dynamicCast(validator);
563  SLM_ASSERT("Validator '" + validatotImpl + "' instantiation failed", activityValidator);
564 
565  ::fwActivities::IValidator::ValidationType validation = activityValidator->validate(actSeries);
566  if(!validation.first)
567  {
568  ok = false;
569  errorMsg += "\n" + validation.second;
570  }
571  }
572 
573  return ok;
574 }
575 
576 //-----------------------------------------------------------------------------
577 
578 void ActivityDataView::removeSelectedObjects()
579 {
580  size_t tabIndex = static_cast<size_t>(this->currentIndex());
581  QPointer<QTreeWidget> tree = m_treeWidgets[tabIndex];
582  QList<QTreeWidgetItem*> items = tree->selectedItems();
583  for (QTreeWidgetItem* item: items)
584  {
585  if (item)
586  {
587  int itemIndex = tree->indexOfTopLevelItem(item);
588  QTreeWidgetItem* itemToRemove = tree->takeTopLevelItem(itemIndex);
589  if (itemToRemove)
590  {
591  delete itemToRemove;
592  this->update();
593  }
594  }
595  }
596 }
597 
598 //-----------------------------------------------------------------------------
599 
600 void ActivityDataView::clearTree()
601 {
602  size_t tabIndex = static_cast<size_t>(this->currentIndex());
603  QPointer<QTreeWidget> tree = m_treeWidgets[tabIndex];
604  tree->clear();
605 }
606 
607 //-----------------------------------------------------------------------------
608 
609 void ActivityDataView::createNewObject()
610 {
611  size_t index = static_cast<size_t>(this->currentIndex());
612 
613  ::fwActivities::registry::ActivityRequirement req = m_activityInfo.requirements[index];
614 
615  std::string type = req.type;
616 
617  QPointer<QTreeWidget> tree = m_treeWidgets[index];
618 
619  unsigned int nbItems = static_cast<unsigned int>(tree->topLevelItemCount());
620 
621  if(nbItems >= req.maxOccurs)
622  {
623  const QString message("Can't create more '"+ QString::fromStdString(type) +
624  "', please remove one to create another.");
625  QMessageBox::warning(this, "New", message );
626  return;
627  }
628  ::fwData::Object::sptr newObject = ::fwData::factory::New(type);
629 
630  m_importedObject.push_back(newObject);
631  this->addObjectItem(index, newObject);
632 }
633 
634 //-----------------------------------------------------------------------------
635 
636 void ActivityDataView::importObject()
637 {
638  const size_t index = static_cast<size_t>(this->currentIndex());
639 
640  ::fwActivities::registry::ActivityRequirement req = m_activityInfo.requirements[index];
641 
642  const std::string type = req.type;
643 
644  QPointer<QTreeWidget> tree = m_treeWidgets[index];
645 
646  const unsigned int nbItems = static_cast<unsigned int>(tree->topLevelItemCount());
647 
648  if(nbItems >= req.maxOccurs)
649  {
650  const QString message("Can't load more '"+ QString::fromStdString(type) +
651  "', please remove one to load another.");
652  QMessageBox::warning(this, "Import", message );
653  return;
654  }
655 
656  auto obj = this->readObject(type, m_ioSelectorSrvConfig);
657  if (obj)
658  {
659  m_importedObject.push_back(obj);
660 
661  this->addObjectItem(index, obj);
662  }
663 }
664 
665 //-----------------------------------------------------------------------------
666 
667 void ActivityDataView::importObjectFromSDB()
668 {
669  const size_t index = static_cast<size_t>(this->currentIndex());
670 
671  ::fwActivities::registry::ActivityRequirement req = m_activityInfo.requirements[index];
672 
673  const std::string type = req.type;
674 
675  QPointer<QTreeWidget> tree = m_treeWidgets[index];
676 
677  const unsigned int nbItems = static_cast<unsigned int>(tree->topLevelItemCount());
678 
679  if(nbItems >= req.maxOccurs)
680  {
681  const QString message("Can't load more '"+ QString::fromStdString(type) +
682  "', please remove one to load another.");
683  QMessageBox::warning(this, "Import from SeriesDB", message );
684  return;
685  }
686 
687  ::fwData::Object::sptr newObject = ::fwData::factory::New(type);
688  if (newObject)
689  {
690  OSLM_ERROR_IF("Imported object must inherit from 'Series'.", !::fwMedData::Series::dynamicCast(newObject));
691 
692  // We use the SeriesDB reader and then extract the object of this type.
693  auto obj = this->readObject("::fwMedData::SeriesDB", m_sdbIoSelectorSrvConfig);
694  auto seriesDB = ::fwMedData::SeriesDB::dynamicCast(obj);
695  if (seriesDB)
696  {
697  unsigned int nbImportedObj = 0;
698  for (const ::fwMedData::Series::sptr& series : *seriesDB)
699  {
700  if (series->isA(type))
701  {
702  ++nbImportedObj;
703  m_importedObject.push_back(series);
704 
705  this->addObjectItem(index, series);
706 
707  if (nbImportedObj >= req.maxOccurs)
708  {
709  break;
710  }
711  }
712  }
713  }
714  }
715  else
716  {
717  std::string msg = "Can not create object '" + type + "'";
718  OSLM_ERROR(msg);
719  QMessageBox messageBox(QMessageBox::Warning, "Error", QString::fromStdString(msg), QMessageBox::Ok);
720  }
721 }
722 
723 //-----------------------------------------------------------------------------
724 
725 ::fwData::Object::sptr ActivityDataView::readObject(const std::string& classname,
726  const std::string& ioSelectorSrvConfig)
727 {
728  ::fwData::Object::sptr obj;
729  ::fwServices::IService::sptr ioSelectorSrv;
730  ioSelectorSrv = ::fwServices::add("::uiIO::editor::SIOSelector");
731  ioSelectorSrv->setObjectId(::fwIO::s_DATA_KEY, "objRead");
732 
733  ::fwRuntime::ConfigurationElement::csptr ioCfg;
734  ioCfg = ::fwServices::registry::ServiceConfig::getDefault()->getServiceConfig(ioSelectorSrvConfig,
735  "::uiIO::editor::SIOSelector");
736 
737  auto ioConfig = ::fwRuntime::Convert::toPropertyTree(ioCfg);
738  auto srvConfig = ioConfig.get_child("config");
739  srvConfig.add("type.<xmlattr>.class", classname); // add the class of the output object
740 
741  try
742  {
743  ioSelectorSrv->setConfiguration(srvConfig);
744  ioSelectorSrv->configure();
745  ioSelectorSrv->start();
746  ioSelectorSrv->update();
747  obj = ioSelectorSrv->getOutput< ::fwData::Object >(::fwIO::s_DATA_KEY);
748  ioSelectorSrv->stop();
749  ::fwServices::OSR::unregisterService( ioSelectorSrv );
750  }
751  catch(std::exception& e)
752  {
753  std::stringstream msg;
754  msg << "The object can not be imported: " << e.what();
755  SLM_ERROR(msg.str());
756 
757  QMessageBox messageBox(QMessageBox::Warning, "Error", QString::fromStdString(msg.str()), QMessageBox::Ok);
758  if (ioSelectorSrv->isStarted())
759  {
760  ioSelectorSrv->stop();
761  }
762  ::fwServices::OSR::unregisterService( ioSelectorSrv );
763  }
764  return obj;
765 }
766 
767 //-----------------------------------------------------------------------------
768 
769 void ActivityDataView::addObjectItem(size_t index, const ::fwData::Object::csptr& obj)
770 {
771  QPointer<QTreeWidget> tree = m_treeWidgets[index];
772 
773  QTreeWidgetItem* newItem = new QTreeWidgetItem();
774  newItem->setFlags(newItem->flags() & ~Qt::ItemIsDropEnabled);
775  newItem->setData(int(ColumnType::NAME), s_UID_ROLE, QVariant(QString::fromStdString(obj->getID())));
776  newItem->setText(int(ColumnType::TYPE), QString::fromStdString(obj->getClassname()));
777 
778  // TODO add more information about object
779  ::fwMedData::Series::csptr series = ::fwMedData::Series::dynamicCast(obj);
780  ::fwData::String::csptr strObj = ::fwData::String::dynamicCast(obj);
781  ::fwData::Integer::csptr intObj = ::fwData::Integer::dynamicCast(obj);
782  ::fwData::Float::csptr floatObj = ::fwData::Float::dynamicCast(obj);
783  ::fwData::Boolean::csptr boolObj = ::fwData::Boolean::dynamicCast(obj);
784  ::fwData::TransformationMatrix3D::csptr trf = ::fwData::TransformationMatrix3D::dynamicCast(obj);
785  if (series)
786  {
787  newItem->setText(int(ColumnType::NAME), QString::fromStdString(series->getModality()));
788  newItem->setText(int(ColumnType::DESC), QString::fromStdString(series->getDescription()));
789  newItem->setText(int(ColumnType::PATIENT), QString::fromStdString(series->getPatient()->getName()));
790  newItem->setText(int(ColumnType::STUDY), QString::fromStdString(series->getStudy()->getDescription()));
791  }
792  else if (strObj)
793  {
794  newItem->setText(int(ColumnType::DESC), QString::fromStdString(strObj->value()));
795  }
796  else if (intObj)
797  {
798  newItem->setText(int(ColumnType::DESC), QString("%1").arg(intObj->value()));
799  }
800  else if (floatObj)
801  {
802  newItem->setText(int(ColumnType::DESC), QString("%1").arg(floatObj->value()));
803  }
804  else if (boolObj)
805  {
806  newItem->setText(int(ColumnType::DESC), boolObj->value() ? "true" : "false");
807  }
808  else if (trf)
809  {
810  std::stringstream str;
811  str << *trf;
812  newItem->setText(int(ColumnType::DESC), QString::fromStdString(str.str()));
813  }
814 
815  // set icon
816  ObjectIconMapType::iterator iter = m_objectIcons.find(obj->getClassname());
817  if (iter != m_objectIcons.end())
818  {
819  newItem->setIcon(int(ColumnType::NAME), QIcon(QString::fromStdString(iter->second)));
820  }
821 
822  tree->addTopLevelItem(newItem);
823  for (int i = 0; i < tree->columnCount(); ++i)
824  {
825  tree->resizeColumnToContents(i);
826  }
827 }
828 
829 //-----------------------------------------------------------------------------
830 
831 void ActivityDataView::onTreeItemDoubleClicked(QTreeWidgetItem* item, int)
832 {
833  if (item)
834  {
835  std::string uid = item->data(int(ColumnType::NAME), s_UID_ROLE).toString().toStdString();
836  if (!uid.empty())
837  {
838  ::fwData::Object::sptr obj = ::fwData::Object::dynamicCast(::fwTools::fwID::getObject(uid));
839  if (obj)
840  {
841  if (obj->isA("::fwData::String"))
842  {
843  ::fwData::String::sptr str = ::fwData::String::dynamicCast(obj);
844  bool isOkClicked = false;
845  QString value = QInputDialog::getText(
846  this, "Edition", "Enter the String value:",
847  QLineEdit::Normal, QString::fromStdString(str->value()), &isOkClicked);
848 
849  if ( isOkClicked)
850  {
851  str->value() = value.toStdString();
852  item->setText(int(ColumnType::DESC), value);
853  }
854  }
855  else if (obj->isA("::fwData::Integer"))
856  {
857  ::fwData::Integer::sptr intObj = ::fwData::Integer::dynamicCast(obj);
858 
859  bool isOkClicked = false;
860  int value = QInputDialog::getInt(
861  this, "Edition", "Enter the Integer value:", intObj->value(), std::numeric_limits<int>::min(),
862  std::numeric_limits<int>::max(), 1, &isOkClicked);
863  if (isOkClicked)
864  {
865  intObj->value() = value;
866  item->setText(int(ColumnType::DESC), QString("%1").arg(value));
867  }
868  }
869  else if (obj->isA("::fwData::Float"))
870  {
871  ::fwData::Float::sptr floatObj = ::fwData::Float::dynamicCast(obj);
872 
873  bool isOkClicked = false;
874  double value = QInputDialog::getDouble(
875  this, "Edition", "Enter the Integer value:", floatObj->value(), std::numeric_limits<int>::min(),
876  std::numeric_limits<int>::max(), 3, &isOkClicked);
877  if (isOkClicked)
878  {
879  floatObj->value() = static_cast<float>(value);
880  item->setText(int(ColumnType::DESC), QString("%1").arg(value));
881  }
882  }
883  else if (obj->isA("::fwData::Boolean"))
884  {
885  ::fwData::Boolean::sptr boolObj = ::fwData::Boolean::dynamicCast(obj);
886  QMessageBox::StandardButton button = QMessageBox::question(
887  this, "Edition", "Defines the Boolean value");
888  boolObj->value() = (button == QMessageBox::Yes);
889  item->setText(int(ColumnType::DESC), boolObj->value() ? "true" : "false" );
890  }
891  else if (obj->isA("::fwData::TransformationMatrix3D"))
892  {
893  ::fwData::TransformationMatrix3D::sptr trf = ::fwData::TransformationMatrix3D::dynamicCast(obj);
894  std::stringstream str;
895  str << *trf;
896 
897  bool isOkClicked = false;
898  QString value = QInputDialog::getMultiLineText(
899  this, "Edition", "Enter the Matrix coefficient (separated by a space):",
900  QString::fromStdString(str.str()), &isOkClicked);
901 
902  QStringList coeffList = value.trimmed().split(QRegularExpression("\\s+"));
903  if (isOkClicked && coeffList.size() == 16)
904  {
905  ::fwData::TransformationMatrix3D::TMCoefArray coeffs;
906 
907  bool conversionOK = false;
908  for (int i = 0; i < 16; ++i)
909  {
910  coeffs[size_t(i)] = coeffList[i].toDouble(&conversionOK);
911  if (!conversionOK)
912  {
913  QMessageBox::warning(this, "ERROR",
914  "This values cannot be converted to matrix coefficients");
915  return;
916  }
917  }
918  trf->setCoefficients(coeffs);
919  item->setText(int(ColumnType::DESC), value.trimmed() );
920  }
921  else if (isOkClicked)
922  {
923  QMessageBox::warning(this, "ERROR",
924  "This values cannot be converted to matrix coefficients. It must contain "
925  "16 values");
926  return;
927  }
928  }
929  else
930  {
931  SLM_DEBUG("Object of type '" + obj->classname() + "' can not yet be editted");
932  }
933  }
934  }
935  }
936 }
937 
938 //-----------------------------------------------------------------------------
939 
940 } // namespace widget
941 } // namespace uiMedDataQt
The namespace uiMedDataQt contains editors for medical data.
std::pair< bool, std::string > ValidationType
Defines validation result of an activity. First element tells if the activity is validated or not by ...
Definition: IValidator.hpp:39
Holds Activities configuration.
Definition: Activities.hpp:175
unsigned int maxOccurs
minimum number of data required
Definition: Activities.hpp:97
static FWACTIVITIES_API Activities::sptr getDefault()
Return the default global instance of Activities.
Definition: Activities.cpp:226
static const std::string & classname()
return object&#39;s classname without its namespace, i.e. BaseObject
Definition: Activities.hpp:222
#define SLM_DEBUG(message)
Definition: spyLog.hpp:239
std::string validator
parameter description
Definition: Activities.hpp:95
#define SLM_ERROR(message)
Definition: spyLog.hpp:272
#define OSLM_ERROR(message)
Definition: spyLog.hpp:274
KeyType keys
True if the data must be created if it is not present (only if minOccurs = 0 and maxOccurs = 1) ...
Definition: Activities.hpp:99
unsigned int minOccurs
Implementation of data validator.
Definition: Activities.hpp:96
#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
static UIMEDDATAQT_API const int s_UID_ROLE
Identifier of the role UID in the series tree item.
Base class for each data object.
std::string container
parameter type (ie. fwMedData::ImageSeries)
Definition: Activities.hpp:93
static FWSERVICES_API ServiceConfig::sptr getDefault()
Return the default global instance of ServiceConfig.
bool create
maximum number of data required
Definition: Activities.hpp:98
#define OSLM_ERROR_IF(message, cond)
Definition: spyLog.hpp:278
static FWTOOLS_API std::shared_ptr< ::fwTools::Object > getObject(IDType requestID)
Retrieve the object attached to the given id. Return a null sptr if no correspondence exist...
Definition: fwID.cpp:117