Medical Imaging Interaction Toolkit  2016.11.0
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,
6 Division of Medical and Biological Informatics.
7 All rights reserved.
8 
9 This software is distributed WITHOUT ANY WARRANTY; without
10 even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 A PARTICULAR PURPOSE.
12 
13 See LICENSE.txt or http://www.mitk.org for details.
14 
15 ===================================================================*/
16 
18 
19 #include <itkCommand.h>
20 
21 //#CTORS/DTOR
22 
23 QmitkDataStorageComboBox::QmitkDataStorageComboBox(QWidget *parent, bool _AutoSelectNewNodes)
24  : QComboBox(parent),
25  m_DataStorage(nullptr),
26  m_Predicate(nullptr),
27  m_BlockEvents(false),
28  m_AutoSelectNewNodes(_AutoSelectNewNodes)
29 {
30  this->Init();
31 }
32 
34  const mitk::NodePredicateBase *_Predicate,
35  QWidget *parent,
36  bool _AutoSelectNewNodes)
37  : QComboBox(parent),
38  m_DataStorage(nullptr),
39  m_Predicate(_Predicate),
40  m_BlockEvents(false),
41  m_AutoSelectNewNodes(_AutoSelectNewNodes)
42 {
43  // make connections, fill combobox
44  this->Init();
45  this->SetDataStorage(_DataStorage);
46 }
47 
49 {
50  // if there was an old storage, remove listeners
52  {
56 
60  }
61  // we have lots of observers to nodes and their name properties, this get's ugly if nodes live longer than the box
62  while (m_Nodes.size() > 0)
63  RemoveNode(0);
64 }
65 
66 //#PUBLIC GETTER
68 {
69  return m_DataStorage.GetPointer();
70 }
71 
73 {
74  return m_Predicate.GetPointer();
75 }
76 
78 {
79  return (this->HasIndex(index)) ? m_Nodes.at(index) : nullptr;
80 }
81 
83 {
84  if (this->count() == 0)
85  return nullptr;
86 
87  int currentIndex = this->currentIndex();
88 
89  return currentIndex >= 0 ? this->GetNode(currentIndex) : nullptr;
90 }
91 
93 {
95 
96  for (auto it = m_Nodes.begin(); it != m_Nodes.end(); ++it)
97  {
98  _SetOfObjects->push_back(*it);
99  }
100 
101  return _SetOfObjects.GetPointer();
102 }
103 
105 {
106  return m_AutoSelectNewNodes;
107 }
108 
109 //#PUBLIC SETTER
111 {
112  // reset only if datastorage really changed
113  if (m_DataStorage.GetPointer() != _DataStorage)
114  {
115  // if there was an old storage, remove listeners
116  if (m_DataStorage.IsNotNull())
117  {
121 
125  }
126  // set new storage
127  m_DataStorage = _DataStorage;
128 
129  // if there is a new storage, add listeners
130  if (m_DataStorage.IsNotNull())
131  {
135 
139  }
140 
141  // reset predicate to reset the combobox
142  this->Reset();
143  }
144 }
145 
147 {
148  if (m_Predicate != _Predicate)
149  {
150  m_Predicate = _Predicate;
151  this->Reset();
152  }
153 }
154 
156 {
157  // this is an event function, make sure that we didnt call ourself
158  if (!m_BlockEvents)
159  {
160  m_BlockEvents = true;
161  // pass a -1 to the InsertNode function in order to append the datatreenode to the end
162  this->InsertNode(-1, _DataNode);
163  m_BlockEvents = false;
164  }
165 }
166 
168 {
169  if (this->HasIndex(index))
170  {
171  //# remove itk::Event observer
172  mitk::DataNode *_DataNode = m_Nodes.at(index);
173  // get name property first
174  mitk::BaseProperty *nameProperty = _DataNode->GetProperty("name");
175  // if prop exists remove modified listener
176  if (nameProperty)
177  {
178  nameProperty->RemoveObserver(m_NodesModifiedObserverTags[index]);
179  // remove name property map
180  m_PropertyToNode.erase(_DataNode);
181  }
182  // then remove delete listener on the node itself
183  _DataNode->RemoveObserver(m_NodesDeleteObserverTags[index]);
184  // remove observer tags from lists
187  // remove node from node vector
188  m_Nodes.erase(m_Nodes.begin() + index);
189  // remove node name from combobox
190  this->removeItem(index);
191  }
192 }
193 
195 {
196  // this is an event function, make sure that we didnt call ourself
197  if (!m_BlockEvents)
198  {
199  m_BlockEvents = true;
200  this->RemoveNode(this->Find(_DataNode));
201  m_BlockEvents = false;
202  }
203 }
204 
205 void QmitkDataStorageComboBox::SetNode(int index, const mitk::DataNode *_DataNode)
206 {
207  if (this->HasIndex(index))
208  {
209  this->InsertNode(index, _DataNode);
210  }
211 }
212 
213 void QmitkDataStorageComboBox::SetNode(const mitk::DataNode *_DataNode, const mitk::DataNode *_OtherDataNode)
214 {
215  this->SetNode(this->Find(_DataNode), _OtherDataNode);
216 }
217 
219 {
220  m_AutoSelectNewNodes = _AutoSelectNewItems;
221 }
222 
223 void QmitkDataStorageComboBox::OnDataNodeDeleteOrModified(const itk::Object *caller, const itk::EventObject &event)
224 {
225  if (!m_BlockEvents)
226  {
227  m_BlockEvents = true;
228 
229  // check if we have a modified event (if not it is a delete event)
230  const itk::ModifiedEvent *modifiedEvent = dynamic_cast<const itk::ModifiedEvent *>(&event);
231 
232  // when node was modified reset text
233  if (modifiedEvent)
234  {
235  const mitk::BaseProperty *_NameProperty = dynamic_cast<const mitk::BaseProperty *>(caller);
236 
237  // node name changed, set it
238  // but first of all find associated node
239  for (auto it = m_PropertyToNode.begin(); it != m_PropertyToNode.end(); ++it)
240  {
241  // property is found take node
242  if (it->second == _NameProperty)
243  {
244  // looks strange but when calling setnode with the same node, that means the node gets updated
245  this->SetNode(it->first, it->first);
246  break;
247  }
248  }
249  }
250  else
251  {
252  const mitk::DataNode *_ConstDataNode = dynamic_cast<const mitk::DataNode *>(caller);
253  if (_ConstDataNode)
254  // node will be deleted, remove it
255  this->RemoveNode(_ConstDataNode);
256  }
257 
258  m_BlockEvents = false;
259  }
260 }
261 
263 {
264  int index = this->Find(item);
265  if (index == -1)
266  {
267  MITK_INFO << "QmitkDataStorageComboBox: item not available";
268  }
269  else
270  {
271  this->setCurrentIndex(index);
272  }
273 }
274 
275 //#PROTECTED GETTER
276 bool QmitkDataStorageComboBox::HasIndex(unsigned int index) const
277 {
278  return (m_Nodes.size() > 0 && index < m_Nodes.size());
279 }
280 
282 {
283  int index = -1;
284 
285  auto nodeIt = std::find(m_Nodes.begin(), m_Nodes.end(), _DataNode);
286 
287  if (nodeIt != m_Nodes.end())
288  index = std::distance(m_Nodes.begin(), nodeIt);
289 
290  return index;
291 }
292 
293 //#PROTECTED SETTER
295 {
296  if (index >= 0 && index < this->count())
297  emit OnSelectionChanged(this->GetSelectedNode());
298  if (index == -1)
299  emit OnSelectionChanged(nullptr);
300 }
301 
302 void QmitkDataStorageComboBox::InsertNode(int index, const mitk::DataNode *_DataNode)
303 {
304  // check new or updated node first
305  if (m_Predicate.IsNotNull() && !m_Predicate->CheckNode(_DataNode))
306  return;
307 
308  bool addNewNode = false;
309  bool insertNewNode = false;
310  bool changedNode = false;
311 
312  // if this->HasIndex(index), then a node shall be updated
313  if (this->HasIndex(index))
314  {
315  // if we really have another node at this position then ...
316  if (_DataNode != m_Nodes.at(index))
317  {
318  // ... remove node, then proceed as usual
319  this->RemoveNode(index);
320  insertNewNode = true;
321  }
322  else
323  changedNode = true;
324  }
325  // otherwise a new node shall be added, let index point to the element after the last element
326  else
327  {
328  index = m_Nodes.size();
329  addNewNode = true;
330  }
331 
332  // const cast because we need non const nodes
333  mitk::DataNode *_NonConstDataNode = const_cast<mitk::DataNode *>(_DataNode);
334  mitk::BaseProperty *nameProperty = _NonConstDataNode->GetProperty("name");
335 
336  if (!changedNode)
337  {
338  // break on duplicated nodes (that doesnt make sense to have duplicates in the combobox)
339  if (this->Find(_DataNode) != -1)
340  return;
341 
342  // add modified observer
345  modifiedCommand->SetCallbackFunction(this, &QmitkDataStorageComboBox::OnDataNodeDeleteOrModified);
346  // !!!! add modified observer for the name
348  if (nameProperty)
349  {
350  m_NodesModifiedObserverTags.push_back(nameProperty->AddObserver(itk::ModifiedEvent(), modifiedCommand));
351  m_PropertyToNode[_NonConstDataNode] = nameProperty;
352  }
353  // if there is no name node save an invalid value for the observer tag (-1)
354  else
355  m_NodesModifiedObserverTags.push_back(-1);
356 
357  // add delete observer
360  deleteCommand->SetCallbackFunction(this, &QmitkDataStorageComboBox::OnDataNodeDeleteOrModified);
361  m_NodesDeleteObserverTags.push_back(_NonConstDataNode->AddObserver(itk::DeleteEvent(), modifiedCommand));
362  }
363 
364  // add node to the vector
365  if (addNewNode)
366  m_Nodes.push_back(_NonConstDataNode);
367  else if (insertNewNode)
368  m_Nodes.insert(m_Nodes.begin() + index, _NonConstDataNode);
369 
370  // ... and to the combobox
371  std::string _NonConstDataNodeName = "unnamed node";
372  // _NonConstDataNodeName is "unnamed node" so far, change it if there is a name property in the node
373  if (nameProperty)
374  _NonConstDataNodeName = nameProperty->GetValueAsString();
375 
376  if (addNewNode)
377  {
378  this->addItem(QString::fromStdString(_NonConstDataNodeName));
379  // select new node if m_AutoSelectNewNodes is true or if we have just added the first node
380  if (m_AutoSelectNewNodes || m_Nodes.size() == 1)
381  this->setCurrentIndex(index);
382  }
383  else
384  {
385  // update text in combobox
386  this->setItemText(index, QString::fromStdString(_NonConstDataNodeName));
387  }
388 }
389 
391 {
392  connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(OnCurrentIndexChanged(int)));
393 }
394 
396 {
397  // remove all nodes first
398  while (!m_Nodes.empty())
399  {
400  // remove last node
401  // explicietely calling RemoveNode of QmitkDataStorageComboBox since derived classes may prevent the removal of all
402  // nodes in their respective RemoveNode implementation. This is happening for example in
403  // QmitkDataStorageComboBoxWithSelectNone.
405  }
406 
407  // clear combobox
408  this->clear();
409 
410  if (m_DataStorage.IsNotNull())
411  {
413 
414  // select all if predicate == NULL
415  if (m_Predicate.IsNotNull())
416  setOfObjects = m_DataStorage->GetSubset(m_Predicate);
417  else
418  setOfObjects = m_DataStorage->GetAll();
419 
420  // add all found nodes
421  for (mitk::DataStorage::SetOfObjects::ConstIterator nodeIt = setOfObjects->Begin(); nodeIt != setOfObjects->End();
422  ++nodeIt) // for each _DataNode
423  {
424  // add node to the node vector and to the combobox
425  this->AddNode(nodeIt.Value().GetPointer());
426  }
427  }
428 }
void RemoveListener(const AbstractDelegate &delegate) const
Definition: mitkMessage.h:397
void AddListener(const AbstractDelegate &delegate) const
Definition: mitkMessage.h:378
std::map< mitk::DataNode *, const mitk::BaseProperty * > m_PropertyToNode
Maps a a specific node to (Name-)property. This is needed because we have to find the assiociated nod...
virtual void OnDataNodeDeleteOrModified(const itk::Object *caller, const itk::EventObject &event)
Called when a node is deleted or the name property of the node was modified. Calls RemoveNode or SetN...
Data management class that handles 'was created by' relations.
itk::SmartPointer< Self > Pointer
void SetSelectedNode(mitk::DataNode::Pointer item)
Slot for signal when user wants to set a node as current selected node.
std::vector< long > m_NodesModifiedObserverTags
Holds the tags of the node-modified observers. (must be updated everytime m_Nodes changes) ...
#define MITK_INFO
Definition: mitkLogMacros.h:22
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 dataStorage and _Predicate. This function is called by all ct...
std::vector< mitk::DataNode * > m_Nodes
~QmitkDataStorageComboBox()
Standard Dtor. Nothing to do here.
mitk::WeakPointer< mitk::DataStorage > m_DataStorage
bool IsNotNull() const
virtual void InsertNode(int index, const mitk::DataNode *_DataNode)
Inserts a new node at the given index. If the index does not exist, the _DataNode is simply appended ...
QmitkDataStorageComboBox(QWidget *parent=nullptr, bool _AutoSelectNewNodes=false)
Ctor for an empty combobox. Use setDataStorage and setPredicate afterwards.
void OnCurrentIndexChanged(int)
Slot for signal when the user selects another item.
mitk::BaseProperty * GetProperty(const char *propertyKey, const mitk::BaseRenderer *renderer=nullptr) const
Get the property (instance of BaseProperty) with key propertyKey from the PropertyList of the rendere...
virtual void SetNode(int index, const mitk::DataNode *_DataNode)
virtual void SetAutoSelectNewItems(bool _AutoSelectNewItems)
mitk::DataStorage::SetOfObjects::ConstPointer GetNodes() const
Returns all nodes that are stored in this combobox.
itk::SmartPointer< const Self > ConstPointer
virtual void AddNode(const mitk::DataNode *_DataNode)
virtual int Find(const mitk::DataNode *_DataNode) const
Seaches for a given node and returns a valid index or -1 if the node was not found.
ObjectType * GetPointer() const
virtual SetOfObjects::ConstPointer GetAll() const =0
returns a set of all data objects that are stored in the data storage
Abstract base class for properties.
bool m_AutoSelectNewNodes
If set to "true" new Nodes will be automatically selected.
mitk::DataStorage::Pointer m_DataStorage
SetOfObjects::ConstPointer GetSubset(const NodePredicateBase *condition) const
returns a set of data objects that meet the given condition(s)
virtual std::string GetValueAsString() const
bool m_BlockEvents
Event function guard. Each function which is called by an event mechanism first checks if this is tru...
void SetPredicate(const mitk::NodePredicateBase *_Predicate)
Set the predicate for this ComboBox. (QmitkDataStorageComboBox is now owner of the predicate) ...
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.
DataStorageEvent AddNodeEvent
AddEvent is emitted whenever a new node has been added to the DataStorage.
virtual void RemoveNode(int index)
mitk::DataStorage::Pointer GetDataStorage() const
Get the DataStorage this ComboBox listens to.
virtual mitk::DataNode::Pointer GetSelectedNode() const
Returns the selected _DataNode or 0 if there is none.
std::vector< long > m_NodesDeleteObserverTags
Holds the tags of the node-modified observers. (must be updated everytime m_Nodes changes) ...
void OnSelectionChanged(const mitk::DataNode *)
Throw a signal when the _DataNode selection changed.
DataStorageEvent RemoveNodeEvent
RemoveEvent is emitted directly before a node is removed from the DataStorage.
const mitk::NodePredicateBase::ConstPointer GetPredicate() const
Return the predicate (may be NULL) that is responsible for the _DataNode selection of this ComboBox...
virtual void Reset()
Reset function whenever datastorage 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.
Class for nodes of the DataTree.
Definition: mitkDataNode.h:66
void SetDataStorage(mitk::DataStorage *dataStorage)
Set the DataStorage this ComboBox should listen to.
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.