Medical Imaging Interaction Toolkit  2018.4.99-389bf124
Medical Imaging Interaction Toolkit
QmitkDataStorageComboBox.cpp
Go to the documentation of this file.
1 /*============================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center (DKFZ)
6 All rights reserved.
7 
8 Use of this source code is governed by a 3-clause BSD license that can be
9 found in the LICENSE file.
10 
11 ============================================================================*/
12 
14 
15 #include <itkCommand.h>
16 
17 QmitkDataStorageComboBox::QmitkDataStorageComboBox(QWidget *parent, bool autoSelectNewNodes)
18  : QComboBox(parent),
19  m_DataStorage(nullptr),
20  m_Predicate(nullptr),
21  m_BlockEvents(false),
22  m_AutoSelectNewNodes(autoSelectNewNodes)
23 {
24  this->Init();
25 }
26 
28  const mitk::NodePredicateBase *predicate,
29  QWidget *parent,
30  bool autoSelectNewNodes)
31  : QComboBox(parent),
32  m_DataStorage(nullptr),
33  m_Predicate(predicate),
34  m_BlockEvents(false),
35  m_AutoSelectNewNodes(autoSelectNewNodes)
36 {
37  // make connections, fill combobox
38  this->Init();
39  this->SetDataStorage(dataStorage);
40 }
41 
43 {
44  // if there was an old storage, remove listeners
45  if (!m_DataStorage.IsExpired())
46  {
47  auto dataStorage = m_DataStorage.Lock();
48 
49  dataStorage->AddNodeEvent.RemoveListener(
52 
53  dataStorage->RemoveNodeEvent.RemoveListener(
56  }
57  // we have lots of observers to nodes and their name properties, this gets ugly if nodes live longer than the box
58  while (m_Nodes.size() > 0)
59  RemoveNode(0);
60 }
61 
63 {
64  std::iterator_traits<mitk::DataNode*>::difference_type index = -1;
65 
66  auto nodeIt = std::find(m_Nodes.begin(), m_Nodes.end(), dataNode);
67 
68  if (nodeIt != m_Nodes.end())
69  index = std::distance(m_Nodes.begin(), nodeIt);
70 
71  return static_cast<int>(index);
72 }
73 
75 {
76  return m_DataStorage.Lock();
77 }
78 
80 {
81  return m_Predicate.GetPointer();
82 }
83 
85 {
86  return (this->HasIndex(index)) ? m_Nodes.at(index) : nullptr;
87 }
88 
90 {
91  if (this->count() == 0)
92  return nullptr;
93 
94  int currentIndex = this->currentIndex();
95 
96  return currentIndex >= 0 ? this->GetNode(currentIndex) : nullptr;
97 }
98 
99 mitk::DataStorage::SetOfObjects::ConstPointer QmitkDataStorageComboBox::GetNodes() const
100 {
101  mitk::DataStorage::SetOfObjects::Pointer setOfObjects = mitk::DataStorage::SetOfObjects::New();
102 
103  for (auto it = m_Nodes.begin(); it != m_Nodes.end(); ++it)
104  {
105  setOfObjects->push_back(*it);
106  }
107 
108  return setOfObjects.GetPointer();
109 }
110 
112 {
113  return m_AutoSelectNewNodes;
114 }
115 
117 {
118  auto currentDataStorage = m_DataStorage.Lock();
119 
120  // reset only if datastorage really changed
121  if (currentDataStorage.GetPointer() != dataStorage)
122  {
123  // if there was an old storage, remove listeners
124  if (currentDataStorage.IsNotNull())
125  {
126  currentDataStorage->AddNodeEvent.RemoveListener(
129 
130  currentDataStorage->RemoveNodeEvent.RemoveListener(
133  }
134  // set new storage
135  m_DataStorage = dataStorage;
136 
137  // if there is a new storage, add listeners
138  if (!m_DataStorage.IsExpired())
139  {
140  currentDataStorage = m_DataStorage.Lock();
141 
142  currentDataStorage->AddNodeEvent.AddListener(
145 
146  currentDataStorage->RemoveNodeEvent.AddListener(
149  }
150 
151  // reset predicate to reset the combobox
152  this->Reset();
153  }
154 }
155 
157 {
158  if (m_Predicate != predicate)
159  {
160  m_Predicate = predicate;
161  this->Reset();
162  }
163 }
164 
166 {
167  // this is an event function, make sure that we didn't call ourself
168  if (!m_BlockEvents)
169  {
170  m_BlockEvents = true;
171  // pass a -1 to the InsertNode function in order to append the datatree node to the end
172  this->InsertNode(-1, dataNode);
173  m_BlockEvents = false;
174  }
175 }
176 
178 {
179  if (this->HasIndex(index))
180  {
182  // remove node name from combobox
183  this->removeItem(index);
184  }
185 }
186 
188 {
189  // this is an event function, make sure that we didn't call ourself
190  if (!m_BlockEvents)
191  {
192  m_BlockEvents = true;
193  this->RemoveNode(this->Find(dataNode));
194  m_BlockEvents = false;
195  }
196 }
197 
198 void QmitkDataStorageComboBox::SetNode(int index, const mitk::DataNode *dataNode)
199 {
200  if (this->HasIndex(index))
201  {
202  // if node is identical, we only update the name in the QComboBoxItem
203  if (dataNode == m_Nodes.at(index))
204  {
205  this->setItemText(index, QString::fromStdString(dataNode->GetName()));
206  }
207  else
208  {
209  this->InsertNode(index, dataNode);
210  }
211  }
212 }
213 
214 void QmitkDataStorageComboBox::SetNode(const mitk::DataNode *dataNode, const mitk::DataNode *otherDataNode)
215 {
216  this->SetNode(this->Find(dataNode), otherDataNode);
217 }
218 
220 {
221  m_AutoSelectNewNodes = autoSelectNewItems;
222 }
223 
224 void QmitkDataStorageComboBox::OnPropertyListChanged(const itk::Object *caller, const itk::EventObject &event)
225 {
226  if (!m_BlockEvents)
227  {
228  m_BlockEvents = true;
229 
230  // check if we have a modified event
231  const itk::ModifiedEvent *modifiedEvent = dynamic_cast<const itk::ModifiedEvent *>(&event);
232  if (modifiedEvent)
233  {
234  const mitk::PropertyList *propertyList = dynamic_cast<const mitk::PropertyList *>(caller);
235  UpdateComboBoxText(propertyList);
236  }
237 
238  m_BlockEvents = false;
239  }
240 }
241 
242 bool QmitkDataStorageComboBox::HasIndex(unsigned int index) const
243 {
244  return (m_Nodes.size() > 0 && index < m_Nodes.size());
245 }
246 
248 {
249  if (index >= 0 && index < this->count())
250  emit OnSelectionChanged(this->GetSelectedNode());
251  if (index == -1)
252  emit OnSelectionChanged(nullptr);
253 }
254 
256 {
257  int index = this->Find(node);
258  if (index == -1)
259  {
260  MITK_INFO << "QmitkDataStorageComboBox: item not available";
261  }
262  else
263  {
264  this->setCurrentIndex(index);
265  }
266 }
267 
268 void QmitkDataStorageComboBox::InsertNode(int index, const mitk::DataNode *dataNode)
269 {
270  // check new or updated node first
271  if (m_Predicate.IsNotNull() && !m_Predicate->CheckNode(dataNode))
272  return;
273 
274  bool addNewNode = false;
275  bool insertNewNode = false;
276  bool changedNode = false;
277 
278  // if this->HasIndex(index), then a node shall be updated
279  if (this->HasIndex(index))
280  {
281  // if we really have another node at this position then ...
282  if (dataNode != m_Nodes.at(index))
283  {
284  // ... remove node, then proceed as usual
285  this->RemoveNode(index);
286  insertNewNode = true;
287  }
288  else
289  changedNode = true;
290  }
291  // otherwise a new node shall be added, let index point to the element after the last element
292  else
293  {
294  index = m_Nodes.size();
295  addNewNode = true;
296  }
297 
298  // const cast because we need non const nodes
299  mitk::DataNode *nonConstDataNode = const_cast<mitk::DataNode *>(dataNode);
300  if (!changedNode)
301  {
302  // break on duplicated nodes (that doesn't make sense to have duplicates in the combobox)
303  if (this->Find(dataNode) != -1)
304  return;
305 
306  // add modified observer
307  itk::MemberCommand<QmitkDataStorageComboBox>::Pointer propertyListChangedCommand =
308  itk::MemberCommand<QmitkDataStorageComboBox>::New();
309  propertyListChangedCommand->SetCallbackFunction(this, &QmitkDataStorageComboBox::OnPropertyListChanged);
310 
311  // add observer for the data node property list
312  mitk::PropertyList* dataNodePropertyList = nonConstDataNode->GetPropertyList();
313  if (nullptr != dataNodePropertyList)
314  {
315  m_DataNodePropertyListObserverTags.push_back(dataNodePropertyList->AddObserver(itk::ModifiedEvent(),
316  propertyListChangedCommand));
317  }
318  else
319  {
320  // fill vector with invalid value
322  }
323 
324  mitk::PropertyList* baseDataPropertyList;
325  //add observer for the base data property list
326  mitk::BaseData* baseData = dynamic_cast<mitk::BaseData*>(nonConstDataNode->GetData());
327  if (nullptr != baseData)
328  {
329  baseDataPropertyList = baseData->GetPropertyList();
330  if (nullptr != baseDataPropertyList)
331  {
332  m_BaseDatapropertyListObserverTags.push_back(baseDataPropertyList->AddObserver(itk::ModifiedEvent(),
333  propertyListChangedCommand));
334  }
335  else
336  {
337  // fill vector with invalid value
339  }
340  }
341  else
342  {
343  // fill vector with invalid value
345  }
346  }
347 
348  // add node to the vector
349  if (addNewNode)
350  m_Nodes.push_back(nonConstDataNode);
351  else if (insertNewNode)
352  m_Nodes.insert(m_Nodes.begin() + index, nonConstDataNode);
353 
354  if (addNewNode)
355  {
356  this->addItem(QString::fromStdString(nonConstDataNode->GetName()));
357  // select new node if m_AutoSelectNewNodes is true or if we have just added the first node
358  if (m_AutoSelectNewNodes || m_Nodes.size() == 1)
359  this->setCurrentIndex(index);
360  }
361  else
362  {
363  // update text in combobox
364  this->setItemText(index, QString::fromStdString(nonConstDataNode->GetName()));
365  }
366 }
367 
369 {
370  connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(OnCurrentIndexChanged(int)));
371 }
372 
374 {
375  // remove all nodes first
376  while (!m_Nodes.empty())
377  {
378  // remove last node
379  // explicitly calling RemoveNode of QmitkDataStorageComboBox since derived classes may prevent the removal of all
380  // nodes in their respective RemoveNode implementation. This is happening for example in
381  // QmitkDataStorageComboBoxWithSelectNone.
383  }
384 
385  // clear combobox
386  this->clear();
387 
388  if (!m_DataStorage.IsExpired())
389  {
390  auto dataStorage = m_DataStorage.Lock();
391  mitk::DataStorage::SetOfObjects::ConstPointer setOfObjects;
392 
393  // select all if predicate == nullptr
394  if (m_Predicate.IsNotNull())
395  setOfObjects = dataStorage->GetSubset(m_Predicate);
396  else
397  setOfObjects = dataStorage->GetAll();
398 
399  // add all found nodes
400  for (mitk::DataStorage::SetOfObjects::ConstIterator nodeIt = setOfObjects->Begin(); nodeIt != setOfObjects->End();
401  ++nodeIt) // for each dataNode
402  {
403  // add node to the node vector and to the combobox
404  this->AddNode(nodeIt.Value().GetPointer());
405  }
406  }
407 }
408 
410 {
411  // remove itk::Event observer
412  mitk::DataNode *dataNode = m_Nodes.at(index);
413 
414  // remove observer from data node property list
415  mitk::PropertyList* dataNodePropertyList = dataNode->GetPropertyList();
416  if (nullptr != dataNodePropertyList)
417  {
418  dataNodePropertyList->RemoveObserver(m_DataNodePropertyListObserverTags[index]);
419  // remove observer tags from lists
421  }
422 
423  // remove observer from base data property list
424  mitk::BaseData* baseData = dynamic_cast<mitk::BaseData*>(dataNode->GetData());
425  if (nullptr != baseData)
426  {
427  mitk::PropertyList* baseDataPropertyList = baseData->GetPropertyList();
428  if (nullptr != dataNodePropertyList)
429  {
430  baseDataPropertyList->RemoveObserver(m_BaseDatapropertyListObserverTags[index]);
431  // remove observer tags from lists
433  }
434  }
435 
436  // remove node from node vector
437  m_Nodes.erase(m_Nodes.begin() + index);
438 }
439 
441 {
442  mitk::PropertyList* dataNodePropertyList = nullptr;
443  mitk::PropertyList* baseDataPropertyList = nullptr;
444  mitk::BaseData* baseData;
445  for (const auto& node : m_Nodes)
446  {
447  dataNodePropertyList = node->GetPropertyList();
448 
449  baseData = dynamic_cast<mitk::BaseData*>(node->GetData());
450  if (nullptr != baseData)
451  {
452  baseDataPropertyList = baseData->GetPropertyList();
453  }
454 
455  if (propertyList == dataNodePropertyList
456  || propertyList == baseDataPropertyList)
457  {
458  // if one of the property list is the one that has just been modified
459  // get the node's index and set its text to the node name
460  // the node name might have been changed, depending on the modified property list
461  auto index = Find(node);
462  // update text in combobox
463  this->setItemText(index, QString::fromStdString(node->GetName()));
464  return;
465  }
466  }
467 }
std::vector< long > m_BaseDatapropertyListObserverTags
Holds the tags of the base data property observers.
virtual mitk::DataNode::Pointer GetSelectedNode() const
Returns the selected dataNode or 0 if there is none.
mitk::DataStorage::Pointer GetDataStorage() const
Get the DataStorage this ComboBox listens to.
Data management class that handles &#39;was created by&#39; relations.
itk::SmartPointer< T > Lock() const
#define MITK_INFO
Definition: mitkLogMacros.h:18
Base of all data objects.
Definition: mitkBaseData.h:37
virtual mitk::DataNode::Pointer GetNode(int index) const
Returns the dataNode at Index index or 0 if the index is out of bounds.
void Init()
Init-function this class with the given data storage and predicate. This function is called by all ct...
std::vector< mitk::DataNode * > m_Nodes
virtual void InsertNode(int index, const mitk::DataNode *dataNode)
Inserts a new node at the given index. If the index does not exist, the data node is simply appended ...
mitk::WeakPointer< mitk::DataStorage > m_DataStorage
Key-value list holding instances of BaseProperty.
void OnCurrentIndexChanged(int)
Slot for signal when the user selects another item.
~QmitkDataStorageComboBox() override
Standard Dtor. Nothing to do here.
virtual void OnPropertyListChanged(const itk::Object *caller, const itk::EventObject &event)
Called when the name property of the node was modified.
mitk::DataStorage::SetOfObjects::ConstPointer GetNodes() const
Returns all nodes that are stored in this combobox.
void SetSelectedNode(const mitk::DataNode::Pointer &node)
Slot for signal when user wants to set a node as current selected node.
void SetPredicate(const mitk::NodePredicateBase *predicate)
Set the predicate for this ComboBox. (QmitkDataStorageComboBox is now owner of the predicate) ...
BaseData * GetData() const
Get the data object (instance of BaseData, e.g., an Image) managed by this DataNode.
bool m_AutoSelectNewNodes
If set to "true" new Nodes will be automatically selected.
mitk::DataStorage::Pointer m_DataStorage
bool IsExpired() const noexcept
bool m_BlockEvents
Event function guard. Each function which is called by an event mechanism first checks if this is tru...
mitk::NodePredicateBase::ConstPointer m_Predicate
Holds the predicate that is responsible for the dataNode selection of this ComboBox. If the predicate is 0, every dataNode will be selected.
virtual void RemoveNode(int index)
const mitk::NodePredicateBase::ConstPointer GetPredicate() const
Return the predicate (may be nullptr) that is responsible for the dataNode selection of this ComboBox...
QmitkDataStorageComboBox(QWidget *parent=nullptr, bool autoSelectNewNodes=false)
Ctor for an empty combobox. Use setDataStorage and setPredicate afterwards.
mitk::PropertyList::Pointer GetPropertyList() const
Get the data&#39;s property list.
mitk::PropertyList * GetPropertyList(const mitk::BaseRenderer *renderer=nullptr) const
Get the PropertyList of the renderer. If renderer is nullptr, the BaseRenderer-independent PropertyLi...
void OnSelectionChanged(const mitk::DataNode *)
Throw a signal when the data node selection changed.
std::vector< long > m_DataNodePropertyListObserverTags
Holds the tags of the data node property observers.
virtual int Find(const mitk::DataNode *dataNode) const
Searches for a given node and returns a valid index or -1 if the node was not found.
virtual void AddNode(const mitk::DataNode *dataNode)
virtual void UpdateComboBoxText(const mitk::PropertyList *)
virtual void Reset()
Reset function whenever data storage or predicate changes.
Interface for evaluation conditions used in the DataStorage class GetSubset() method.
bool HasIndex(unsigned int index) const
Checks if the given index is within the range of the m_Nodes vector.
virtual void SetNode(int index, const mitk::DataNode *dataNode)
Class for nodes of the DataTree.
Definition: mitkDataNode.h:57
bool GetName(std::string &nodeName, const mitk::BaseRenderer *renderer=nullptr, const char *propertyKey="name") const
Convenience access method for accessing the name of an object (instance of StringProperty with proper...
Definition: mitkDataNode.h:369
void SetDataStorage(mitk::DataStorage *dataStorage)
Set the DataStorage this ComboBox should listen to.
virtual void SetAutoSelectNewItems(bool autoSelectNewItems)