diff --git a/Modules/QtWidgets/include/QmitkAbstractDataStorageModel.h b/Modules/QtWidgets/include/QmitkAbstractDataStorageModel.h index 0b9c1a1145..1c2ac385a5 100644 --- a/Modules/QtWidgets/include/QmitkAbstractDataStorageModel.h +++ b/Modules/QtWidgets/include/QmitkAbstractDataStorageModel.h @@ -1,83 +1,92 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical Image Computing. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QMITKABSTRACTDATASTORAGEMODEL_H #define QMITKABSTRACTDATASTORAGEMODEL_H #include // mitk core #include #include +#include // qt #include /* * @brief This abstract class extends the 'QAbstractItemModel' to accept an 'mitk::DataStorage' and a 'mitk::NodePredicateBase'., * It registers itself as a node event listener of the data storage. * The 'QmitkAbstractDataStorageModel' provides three empty functions, 'NodeAdded', 'NodeChanged' and 'NodeRemoved', that * may be implemented by the subclasses. These functions allow to react to the 'AddNodeEvent', 'ChangedNodeEvent' and * 'RemoveNodeEvent' of the data storage. This might be useful to force an update on the custom view to correctly * represent the content of the data storage. * * A concrete implementations of this class is used to store the temporarily shown data nodes of the data storage. * These nodes may be a subset of all the nodes inside the data storage, if a specific node predicate is set. * * A model that implements this class has to return mitk::DataNode::Pointer objects for model indexes when the * role is QmitkDataNodeRole. */ class MITKQTWIDGETS_EXPORT QmitkAbstractDataStorageModel : public QAbstractItemModel { Q_OBJECT public: virtual ~QmitkAbstractDataStorageModel(); /* * @brief Sets the data storage and adds listener for node events. * * @par dataStorage A pointer to the data storage to set. */ void SetDataStorage(mitk::DataStorage* dataStorage); - mitk::DataStorage* GetDataStorage() { return m_DataStorage; } + mitk::DataStorage* GetDataStorage() { return m_DataStorage.Lock().GetPointer(); } /* * @brief Sets the node predicate and updates the model data, according to the node predicate. * * @par nodePredicate A pointer to node predicate. */ void SetNodePredicate(mitk::NodePredicateBase* nodePredicate); mitk::NodePredicateBase* GetNodePredicate() { return m_NodePredicate; } protected: virtual void DataStorageChanged() = 0; virtual void NodePredicateChanged() = 0; virtual void NodeAdded(const mitk::DataNode* node) = 0; virtual void NodeChanged(const mitk::DataNode* node) = 0; virtual void NodeRemoved(const mitk::DataNode* node) = 0; QmitkAbstractDataStorageModel(QObject* parent = nullptr); QmitkAbstractDataStorageModel(mitk::DataStorage* dataStorage, QObject* parent = nullptr); - mitk::DataStorage* m_DataStorage; + + mitk::WeakPointer m_DataStorage; mitk::NodePredicateBase::Pointer m_NodePredicate; +private: + + /** Helper triggered on the storage delete event */ + void SetDataStorageDeleted(); + + unsigned long m_DataStorageDeletedTag; + }; #endif // QMITKABSTRACTDATASTORAGEMODEL_H diff --git a/Modules/QtWidgets/src/QmitkAbstractDataStorageModel.cpp b/Modules/QtWidgets/src/QmitkAbstractDataStorageModel.cpp index 6ef623fb1c..9afb4d48f6 100644 --- a/Modules/QtWidgets/src/QmitkAbstractDataStorageModel.cpp +++ b/Modules/QtWidgets/src/QmitkAbstractDataStorageModel.cpp @@ -1,93 +1,115 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical Image Computing. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include QmitkAbstractDataStorageModel::QmitkAbstractDataStorageModel(QObject* parent/* = nullptr*/) : QAbstractItemModel(parent) , m_DataStorage(nullptr) , m_NodePredicate(nullptr) { // nothing here } QmitkAbstractDataStorageModel::QmitkAbstractDataStorageModel(mitk::DataStorage* dataStorage, QObject* parent/* = nullptr*/) : QAbstractItemModel(parent) , m_DataStorage(nullptr) , m_NodePredicate(nullptr) { SetDataStorage(dataStorage); } QmitkAbstractDataStorageModel::~QmitkAbstractDataStorageModel() { - if (nullptr != m_DataStorage) + if (!m_DataStorage.IsExpired()) { + auto dataStorage = m_DataStorage.Lock(); + + // remove Listener for the data storage itself + dataStorage->RemoveObserver(m_DataStorageDeletedTag); + // remove listener from data storage - m_DataStorage->AddNodeEvent.RemoveListener( + dataStorage->AddNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeAdded)); - m_DataStorage->RemoveNodeEvent.RemoveListener( + dataStorage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeRemoved)); - m_DataStorage->ChangedNodeEvent.RemoveListener( + dataStorage->ChangedNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeChanged)); } } void QmitkAbstractDataStorageModel::SetDataStorage(mitk::DataStorage* dataStorage) { if (m_DataStorage == dataStorage) { return; } - if (nullptr != m_DataStorage) + if (!m_DataStorage.IsExpired()) { + auto dataStorage = m_DataStorage.Lock(); + + // remove Listener for the data storage itself + dataStorage->RemoveObserver(m_DataStorageDeletedTag); + // remove listener from old data storage - m_DataStorage->AddNodeEvent.RemoveListener( + dataStorage->AddNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeAdded)); - m_DataStorage->RemoveNodeEvent.RemoveListener( + dataStorage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeRemoved)); - m_DataStorage->ChangedNodeEvent.RemoveListener( + dataStorage->ChangedNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeChanged)); } m_DataStorage = dataStorage; - if (nullptr != dataStorage) + if (!m_DataStorage.IsExpired()) { + auto dataStorage = m_DataStorage.Lock(); + + // add Listener for the data storage itself + auto command = itk::SimpleMemberCommand::New(); + command->SetCallbackFunction(this, &QmitkAbstractDataStorageModel::SetDataStorageDeleted); + m_DataStorageDeletedTag = dataStorage->AddObserver(itk::DeleteEvent(), command); + // add listener for new data storage - m_DataStorage->AddNodeEvent.AddListener( + dataStorage->AddNodeEvent.AddListener( mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeAdded)); - m_DataStorage->RemoveNodeEvent.AddListener( + dataStorage->RemoveNodeEvent.AddListener( mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeRemoved)); - m_DataStorage->ChangedNodeEvent.AddListener( + dataStorage->ChangedNodeEvent.AddListener( mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeChanged)); } // update model if the data storage has been changed DataStorageChanged(); } +void QmitkAbstractDataStorageModel::SetDataStorageDeleted() +{ + this->SetDataStorage(nullptr); +} + void QmitkAbstractDataStorageModel::SetNodePredicate(mitk::NodePredicateBase* nodePredicate) { if (m_NodePredicate == nodePredicate) { return; } m_NodePredicate = nodePredicate; // update model if the node predicate has been changed NodePredicateChanged(); } diff --git a/Modules/QtWidgets/src/QmitkDataStorageDefaultListModel.cpp b/Modules/QtWidgets/src/QmitkDataStorageDefaultListModel.cpp index 1b47523333..6cedc1e324 100644 --- a/Modules/QtWidgets/src/QmitkDataStorageDefaultListModel.cpp +++ b/Modules/QtWidgets/src/QmitkDataStorageDefaultListModel.cpp @@ -1,158 +1,158 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical Image Computing. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include // qt widgets module #include "QmitkCustomVariants.h" #include "QmitkEnums.h" -QmitkDataStorageDefaultListModel::QmitkDataStorageDefaultListModel(QObject *parent) +QmitkDataStorageDefaultListModel::QmitkDataStorageDefaultListModel(QObject *parent) : QmitkAbstractDataStorageModel(parent) { - // nothing here } void QmitkDataStorageDefaultListModel::DataStorageChanged() { UpdateModelData(); } void QmitkDataStorageDefaultListModel::NodePredicateChanged() { UpdateModelData(); } void QmitkDataStorageDefaultListModel::NodeAdded(const mitk::DataNode* node) { UpdateModelData(); } void QmitkDataStorageDefaultListModel::NodeChanged(const mitk::DataNode* node) { // nothing here, since the "'NodeChanged'-event is currently sent far too often //UpdateModelData(); } void QmitkDataStorageDefaultListModel::NodeRemoved(const mitk::DataNode* node) { UpdateModelData(); } QModelIndex QmitkDataStorageDefaultListModel::index(int row, int column, const QModelIndex &parent) const { bool hasIndex = this->hasIndex(row, column, parent); if (hasIndex) { return this->createIndex(row, column); } return QModelIndex(); } QModelIndex QmitkDataStorageDefaultListModel::parent(const QModelIndex &child) const { return QModelIndex(); } int QmitkDataStorageDefaultListModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return m_DataNodes.size(); } int QmitkDataStorageDefaultListModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return 1; } QVariant QmitkDataStorageDefaultListModel::data(const QModelIndex &index, int role) const { - if (!index.isValid()) + if (!index.isValid() || index.model() != this) { return QVariant(); } if(index.row() < 0 || index.row() >= m_DataNodes.size()) { return QVariant(); } mitk::DataNode::Pointer dataNode = m_DataNodes.at(index.row()); if (Qt::DisplayRole == role) { return QVariant(QString::fromStdString(dataNode->GetName())); } else if (QmitkDataNodeRole == role) { return QVariant::fromValue(dataNode); } return QVariant(); } QVariant QmitkDataStorageDefaultListModel::headerData(int section, Qt::Orientation orientation, int role) const { return QVariant(tr("Nodes")); } Qt::ItemFlags QmitkDataStorageDefaultListModel::flags(const QModelIndex &index) const { - if (index.isValid()) + if (index.isValid() && index.model() == this) { return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } return Qt::NoItemFlags; } void QmitkDataStorageDefaultListModel::UpdateModelData() { mitk::DataStorage::SetOfObjects::ConstPointer dataNodes; - if (m_DataStorage != nullptr) + if (!m_DataStorage.IsExpired()) { - if (m_NodePredicate != nullptr) + auto dataStorage = m_DataStorage.Lock(); + if (m_NodePredicate.IsNotNull()) { - dataNodes = m_DataStorage->GetSubset(m_NodePredicate); + dataNodes = dataStorage->GetSubset(m_NodePredicate); } else { - dataNodes = m_DataStorage->GetAll(); + dataNodes = dataStorage->GetAll(); } } // update the model, so that it will be filled with the nodes of the new data storage beginResetModel(); m_DataNodes.clear(); // add all (filtered) nodes to the vector of nodes if (dataNodes != nullptr) { for (auto& node : *dataNodes) { m_DataNodes.push_back(node); } } endResetModel(); }