fw4spl
SelectorModel.cpp
1 /* ***** BEGIN LICENSE BLOCK *****
2  * FW4SPL - Copyright (C) IRCAD, 2009-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/SelectorModel.hpp"
8 
9 #include "uiMedDataQt/InsertSeries.hpp"
10 
11 #include <fwActivities/registry/Activities.hpp>
12 
13 #include <fwData/Image.hpp>
14 
15 #include <fwMedData/ActivitySeries.hpp>
16 #include <fwMedData/Equipment.hpp>
17 #include <fwMedData/ImageSeries.hpp>
18 #include <fwMedData/ModelSeries.hpp>
19 #include <fwMedData/Patient.hpp>
20 #include <fwMedData/Series.hpp>
21 
22 #include <fwRuntime/operations.hpp>
23 
24 #include <fwTools/fwID.hpp>
25 
26 #include <boost/algorithm/string/trim.hpp>
27 #include <boost/math/special_functions/round.hpp>
28 #include <boost/regex.hpp>
29 
30 #include <QFont>
31 #include <QStandardItem>
32 #include <QString>
33 
34 namespace uiMedDataQt
35 {
36 namespace widget
37 {
38 
39 //-----------------------------------------------------------------------------
40 
41 SelectorModel::SelectorModel(QWidget* parent) :
42  QStandardItemModel(parent),
43  m_studyRowCount(0),
44  m_insert(false)
45 {
46  this->init();
47 }
48 
49 //-----------------------------------------------------------------------------
50 
52 {
53 }
54 
55 //-----------------------------------------------------------------------------
56 
57 void SelectorModel::init()
58 {
59  m_studyRowCount = 0;
60  m_items.clear();
61 
62  QStringList headers;
63  headers << "Patient name" << "Modality" << "Acquisition date" << "Image dimension" << "Voxel size"
64  << "Patient position" << "Study description" << "Patient ID" << "Age"
65  << "Referring physician / Performing physician " << "Sex" << "Birthdate" << "Institution";
66  this->setHorizontalHeaderLabels(headers);
67 }
68 
69 //-----------------------------------------------------------------------------
70 
72 {
73  m_insert = insert;
74 }
75 
76 //-----------------------------------------------------------------------------
77 
79 {
80  QModelIndex idx = this->createIndex(index.row(), 0, index.internalPointer());
81  QStandardItem* item = this->itemFromIndex(idx);
82  return (SelectorModel::ItemType) item->data(SelectorModel::ITEM_TYPE).toInt();
83 }
84 
85 //-----------------------------------------------------------------------------
86 
88 {
89  this->QStandardItemModel::clear();
90  this->init();
91 }
92 
93 //-----------------------------------------------------------------------------
94 
95 ::fwData::Image::SpacingType roundSpacing(const ::fwData::Image::SpacingType& spacing)
96 {
97  ::fwData::Image::SpacingType roundSpacing;
98  for(::fwData::Image::SpacingType::value_type val : spacing)
99  {
100  ::fwData::Image::SpacingType::value_type roundVal = ::boost::math::round(val * 100.)/100.;
101  roundSpacing.push_back(roundVal);
102  }
103  return roundSpacing;
104 }
105 
106 //-----------------------------------------------------------------------------
107 
108 std::string formatDate(const std::string& date)
109 {
110  std::string formatDate = date;
111  ::boost::algorithm::trim(formatDate);
112 
113  const std::string regexYyear = "[0-9]{4}";
114  const std::string regexMonth = "[0-9]{2}";
115  const std::string regexDay = "[0-9]{2}";
116 
117  const std::string regexStr = "("+regexYyear+")"+"("+regexMonth+")"+"("+regexDay+")";
118  ::boost::regex re(regexStr);
119  ::boost::smatch match;
120  if( ::boost::regex_match(formatDate, match, re) )
121  {
122  std::string year, month, day, hour, min, sec;
123  OSLM_ASSERT("Wrong match for "<<formatDate, match.size() >= 4);
124  year.assign(match[1].first, match[1].second);
125  month.assign(match[2].first, match[2].second);
126  day.assign(match[3].first, match[3].second);
127 
128  formatDate = year + "/" + month + "/" + day;
129  }
130 
131  return formatDate;
132 }
133 
134 //-----------------------------------------------------------------------------
135 
136 std::string formatTime(const std::string& time)
137 {
138  std::string formatTime = time;
139  ::boost::algorithm::trim(formatTime);
140 
141  const std::string regexHour = "[0-9]{2}";
142  const std::string regexMin = "[0-9]{2}";
143  const std::string regexSec = "[0-9]{2}";
144  const std::string regexEnd = "[.0-9]*";
145 
146  const std::string regexStr = "("+regexHour+")"+"("+regexMin+")"+"("+regexSec+")"+regexEnd;
147  ::boost::regex re(regexStr);
148  ::boost::smatch match;
149  if( ::boost::regex_match(formatTime, match, re) )
150  {
151  std::string year, month, day, hour, min, sec;
152  OSLM_ASSERT("Wrong match for "<<formatTime, match.size() >= 4);
153  hour.assign(match[1].first, match[1].second);
154  min.assign(match[2].first, match[2].second);
155  sec.assign(match[3].first, match[3].second);
156 
157  formatTime = hour + ":" + min + ":" + sec;
158  }
159 
160  return formatTime;
161 }
162 
163 //-----------------------------------------------------------------------------
164 
165 void SelectorModel::addSeries(::fwMedData::Series::sptr series)
166 {
167  ::fwMedData::Study::sptr study = series->getStudy();
168  ::fwMedData::DicomValueType studyUID = study->getInstanceUID();
169  StudyUidItemMapType::iterator itr = m_items.find(studyUID);
170  QStandardItem* studyRootItem;
171 
172  if(itr != m_items.end())
173  {
174  studyRootItem = itr->second;
175  }
176  else
177  {
178  ::fwMedData::Patient::sptr patient = series->getPatient();
179  ::fwMedData::Equipment::sptr equipment = series->getEquipment();
180 
181  QStandardItem* patientName = new QStandardItem( QString::fromStdString(patient->getName()) );
182  patientName->setData(QVariant((int)SelectorModel::STUDY), SelectorModel::ITEM_TYPE);
183  patientName->setData(QVariant(QString::fromStdString(study->getInstanceUID())), UID);
184  QStandardItem* patientId = new QStandardItem( QString::fromStdString(patient->getPatientId()) );
185  std::string birthDate = formatDate(patient->getBirthdate());
186  QStandardItem* patientBirthdate = new QStandardItem( QString::fromStdString(birthDate) );
187  QStandardItem* patientSex = new QStandardItem( QString::fromStdString(patient->getSex()) );
188 
189  std::string studyDateTime = formatDate(study->getDate()) + " " + formatTime(study->getTime());
190  QStandardItem* studyDate = new QStandardItem( QString::fromStdString(studyDateTime));
191  QStandardItem* studyReferringPhysicianName = new QStandardItem(
192  QString::fromStdString(study->getReferringPhysicianName()));
193  QStandardItem* studyDescription = new QStandardItem( QString::fromStdString(study->getDescription()));
194  QStandardItem* studyPatientAge = new QStandardItem( QString::fromStdString(study->getPatientAge()));
195 
196  QStandardItem* institution = new QStandardItem( QString::fromStdString(equipment->getInstitutionName()));
197 
198  this->setItem(m_studyRowCount, 0, patientName);
199  this->setItem(m_studyRowCount, 1, new QStandardItem());
200  this->setItem(m_studyRowCount, 2, studyDate);
201  this->setItem(m_studyRowCount, 3, new QStandardItem());
202  this->setItem(m_studyRowCount, 4, new QStandardItem());
203  this->setItem(m_studyRowCount, 5, new QStandardItem());
204  this->setItem(m_studyRowCount, 6, studyDescription);
205  this->setItem(m_studyRowCount, 7, patientId);
206  this->setItem(m_studyRowCount, 8, studyPatientAge);
207  this->setItem(m_studyRowCount, 9, studyReferringPhysicianName);
208  this->setItem(m_studyRowCount, 10, patientSex);
209  this->setItem(m_studyRowCount, 11, patientBirthdate);
210  this->setItem(m_studyRowCount, 12, institution);
211 
212  const int nbColumns = institution->index().column() + 1;
213  for (int i = 0; i < nbColumns; ++i)
214  {
215  QStandardItem* item = this->item(m_studyRowCount, i);
216  item->setFlags(item->flags() & ~Qt::ItemIsSelectable );
217  }
218 
219  m_studyRowCount++;
220  studyRootItem = patientName;
221  m_items[studyUID] = studyRootItem;
222  }
223 
224  QStandardItem* seriesModality = new QStandardItem(QString::fromStdString(series->getModality()));
225  std::string seriesDateTime = formatDate(series->getDate()) + " " + formatTime(series->getTime());
226  QStandardItem* seriesDate = new QStandardItem( QString::fromStdString(seriesDateTime));
227 
228  QStandardItem* seriesPerformingPhysician =
229  this->getInfo< ::fwMedData::DicomValuesType >(series->getPerformingPhysiciansName(), ", ");
230 
231  QStandardItem* seriesDescription1 = new QStandardItem(QString::fromStdString(series->getDescription()));
232  seriesDescription1->setData(QVariant((int)SelectorModel::SERIES), SelectorModel::ITEM_TYPE);
233  seriesDescription1->setData(QVariant(QString::fromStdString(series->getID())), UID);
234  QStandardItem* seriesDescription2 = new QStandardItem(QString::fromStdString(series->getDescription()));
235 
236  const int nbRow = studyRootItem->rowCount();
237  studyRootItem->setChild(nbRow, 0, seriesDescription1);
238  studyRootItem->setChild(nbRow, 1, seriesModality);
239  studyRootItem->setChild(nbRow, 2, seriesDate);
240  studyRootItem->setChild(nbRow, 6, seriesDescription2);
241  studyRootItem->setChild(nbRow, 9, seriesPerformingPhysician);
242  studyRootItem->setChild(nbRow, 12, new QStandardItem());
243 
244  ::fwMedData::ImageSeries::sptr imageSeries = ::fwMedData::ImageSeries::dynamicCast(series);
245  if(imageSeries)
246  {
247 
248  ::fwData::Image::sptr image = imageSeries->getImage();
249 
250  ::fwData::Image::SizeType imageNumber = image->getSize();
251  QStandardItem* imageSize = this->getInfo< ::fwData::Image::SizeType>(imageNumber, " x ");
252  studyRootItem->setChild(nbRow, 3, imageSize);
253 
254  ::fwData::Image::SpacingType voxelSize = roundSpacing(image->getSpacing());
255  QStandardItem* voxelSizeItem = this->getInfo< ::fwData::Image::SpacingType>(voxelSize, " x ");
256  studyRootItem->setChild(nbRow, 4, voxelSizeItem);
257 
258  ::fwData::Image::OriginType patientPosition = image->getOrigin();
259  QStandardItem* originItem = this->getInfo< ::fwData::Image::OriginType>(patientPosition, ", ");
260  studyRootItem->setChild(nbRow, 5, originItem);
261  }
262 
263  if(m_insert)
264  {
265  ::uiMedDataQt::InsertSeries::sptr insertSeries = ::uiMedDataQt::InsertSeries::dynamicCast(series);
266 
267  const int nbColumns = studyRootItem->columnCount();
268  for(int i = 0; i < nbColumns; ++i)
269  {
270  QStandardItem* item = studyRootItem->child(nbRow, i);
271  if(!item)
272  {
273  studyRootItem->setChild(nbRow, i, new QStandardItem());
274  item = studyRootItem->child(nbRow, i);
275  }
276 
277  if(insertSeries)
278  {
279  QFont f = item->font();
280  f.setBold(true);
281  item->setFont(f);
282  }
283  else
284  {
285  item->setFlags(item->flags() & ~Qt::ItemIsSelectable);
286  }
287  }
288  }
289 
290  this->addSeriesIcon(series, seriesDescription1);
291 }
292 
293 //-----------------------------------------------------------------------------
294 
295 void SelectorModel::addSeriesIcon(::fwMedData::Series::sptr series, QStandardItem* item)
296 {
297  SeriesIconType::iterator iter = m_seriesIcons.find(series->getClassname());
298  if (iter != m_seriesIcons.end())
299  {
300  item->setIcon(QIcon(QString::fromStdString(iter->second)));
301  }
302  else
303  {
304  ::fwMedData::ImageSeries::sptr imageSeries = ::fwMedData::ImageSeries::dynamicCast(series);
305  ::fwMedData::ModelSeries::sptr modelSeries = ::fwMedData::ModelSeries::dynamicCast(series);
306  ::fwMedData::ActivitySeries::sptr activitySeries = ::fwMedData::ActivitySeries::dynamicCast(series);
307  if(imageSeries)
308  {
309  const auto path = ::fwRuntime::getBundleResourceFilePath("media", "icons/ImageSeries.svg");
310  item->setIcon(QIcon(QString::fromStdString(path.string())));
311  }
312  else if (modelSeries)
313  {
314  const auto path = ::fwRuntime::getBundleResourceFilePath("media", "icons/ModelSeries.svg");
315  item->setIcon(QIcon(QString::fromStdString(path.string())));
316  }
317  else if (activitySeries)
318  {
319  ::fwActivities::registry::Activities::sptr registry = ::fwActivities::registry::Activities::getDefault();
320  std::string id = activitySeries->getActivityConfigId();
321  OSLM_ASSERT("Activity information not found for" << id, registry->hasInfo(id));
322 
324  activityInfo = registry->getInfo(id);
325  item->setIcon(QIcon(QString::fromStdString(activityInfo.icon)));
326  }
327  else
328  {
329  OSLM_WARN("This type of series is not defined (" << series->getClassname() << ")");
330  }
331  }
332 }
333 
334 //-----------------------------------------------------------------------------
335 
336 void SelectorModel::removeSeries(::fwMedData::Series::sptr series)
337 {
338  QStandardItem* seriesItem = this->findSeriesItem(series);
339  this->removeSeriesItem(seriesItem);
340 }
341 
342 //-----------------------------------------------------------------------------
343 
344 QModelIndex SelectorModel::getIndex(const QModelIndex& index, int column )
345 {
346  QModelIndex idx = this->createIndex(index.row(), column, index.internalPointer());
347  return idx;
348 }
349 
350 //-----------------------------------------------------------------------------
351 
352 void SelectorModel::removeRows(const QModelIndexList indexes)
353 {
354  QList<QStandardItem*> seriesItems;
355  QList<QStandardItem*> studyItems;
356 
357  for(QModelIndex index : indexes)
358  {
359  SLM_ASSERT("Index must be in first column.", index.column() == 0);
360  QStandardItem* item = this->itemFromIndex(index);
362  {
363  studyItems.append(item);
364  }
365  else if (item->data(SelectorModel::ITEM_TYPE) == SelectorModel::SERIES)
366  {
367  seriesItems.append(item);
368  }
369  }
370 
371  // Remove series items from selector
372  for(QStandardItem* item : seriesItems)
373  {
374  QStandardItem* studyItem = item->parent();
375 
376  // Remove series item if it is not included in a study which will be remove.
377  if (std::find(studyItems.begin(), studyItems.end(), studyItem) == studyItems.end())
378  {
379  this->removeSeriesItem(item);
380  }
381  }
382 
383  // Remove study items from selector
384  for(QStandardItem* item : studyItems)
385  {
386  this->removeStudyItem(item);
387  }
388 }
389 
390 //-----------------------------------------------------------------------------
391 
392 bool SelectorModel::removeStudyItem(QStandardItem* item)
393 {
394  bool isRemoved = false;
395  SLM_ASSERT("Index must represent a study.", item->data(SelectorModel::ITEM_TYPE) == SelectorModel::STUDY);
396  QString uid = item->data(SelectorModel::UID).toString();
397  ::fwMedData::DicomValueType instanceUID = uid.toStdString();
398 
399  isRemoved = this->QStandardItemModel::removeRow(item->row());
400  SLM_ASSERT("Remove can not be done!", isRemoved);
401  m_items.erase(instanceUID);
402  --m_studyRowCount;
403 
404  return isRemoved;
405 }
406 
407 //-----------------------------------------------------------------------------
408 
409 bool SelectorModel::removeSeriesItem(QStandardItem* item)
410 {
411  bool isRemoved = false;
412 
413  SLM_ASSERT("Index must represent series", item->data(SelectorModel::ITEM_TYPE) == SelectorModel::SERIES);
414  QStandardItem* parent = item->parent();
415  isRemoved = this->QStandardItemModel::removeRow(item->row(), this->indexFromItem(parent));
416  SLM_ASSERT("Remove can not be done!", isRemoved);
417  if(parent && parent->rowCount() == 0)
418  {
419  this->removeStudyItem(parent);
420  }
421  return isRemoved;
422 }
423 
424 //-----------------------------------------------------------------------------
425 
426 QStandardItem* SelectorModel::findSeriesItem(::fwMedData::Series::sptr series)
427 {
428  QStandardItem* seriesItem;
429  ::fwMedData::Study::sptr study = series->getStudy();
430  QStandardItem* studyItem = this->findStudyItem(study);
431 
432  int nbRow = studyItem->rowCount();
433  for(int row = 0; row < nbRow; ++row)
434  {
435  QStandardItem* child = studyItem->child(row);
436  std::string seriesId = child->data(SelectorModel::UID).toString().toStdString();
437  if(seriesId == series->getID())
438  {
439  seriesItem = child;
440  break;
441  }
442  }
443  return seriesItem;
444 }
445 
446 //-----------------------------------------------------------------------------
447 
448 QStandardItem* SelectorModel::findStudyItem(::fwMedData::Study::sptr study)
449 {
450  ::fwMedData::DicomValueType studyInstanceUid = study->getInstanceUID();
451 
452  QStandardItem* studyItem = m_items[studyInstanceUid];
453  return studyItem;
454 }
455 
456 //-----------------------------------------------------------------------------
457 
459 {
460  m_seriesIcons = seriesIcons;
461 }
462 
463 //-----------------------------------------------------------------------------
464 
465 } // namespace widget
466 } // namespace uiMedDataQt
The namespace uiMedDataQt contains editors for medical data.
Contains fwAtomsFilter::registry details.
Role for the fwID of the object.
Holds Activities configuration.
Definition: Activities.hpp:175
#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
Type to represent Study/Patient.
UIMEDDATAQT_API void clear()
Clears all items in the model.
static FWACTIVITIES_API Activities::sptr getDefault()
Return the default global instance of Activities.
Definition: Activities.cpp:226
ItemType
Defines item type (STUDY or SERIES), it is used in items data (ITEM_TYPE role).
UIMEDDATAQT_API QModelIndex getIndex(const QModelIndex &index, int column)
Returns the index in the same row as the given index and at the specified column. ...
#define OSLM_WARN(message)
Definition: spyLog.hpp:263
UIMEDDATAQT_API void setInsertMode(bool insert)
Sets if the selector must be in insert mode.
#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
UIMEDDATAQT_API void removeRows(const QModelIndexList indexes)
Removes the rows given by the indexes.
UIMEDDATAQT_API void addSeries(::fwMedData::Series::sptr series)
Add the Series in the tree. If the associated study already exist in the tree, the series is added to...
UIMEDDATAQT_API void setSeriesIcons(const SeriesIconType &seriesIcons)
Sets the specific icons for series in selector.
std::vector< double > SpacingType
Image spacing type.
Role for the item type (STUDY or SERIES)
UIMEDDATAQT_API void removeSeries(::fwMedData::Series::sptr series)
Removes the Series from the tree. After deletion, if the study is empty, it will be removed...
UIMEDDATAQT_API SelectorModel(QWidget *parent=0)
Constructor. Inits the model.
UIMEDDATAQT_API ~SelectorModel()
Destructor. Does nothing.
std::map< std::string, std::string > SeriesIconType
Map associating icons to series (map<series classname, icon path>)
UIMEDDATAQT_API ItemType getItemType(const QModelIndex &index)
Returns the type of the item (SERIES or STUDY) associated to the ITEM_TYPE role.
UIMEDDATAQT_API QStandardItem * findSeriesItem(::fwMedData::Series::sptr series)
Returns the series item representing the series.
::fwData::Array::SizeType SizeType
Image size type.
UIMEDDATAQT_API QStandardItem * findStudyItem(::fwMedData::Study::sptr study)
Returns the item representing the study.
std::vector< double > OriginType
Image origin type.