diff --git a/Modules/QtWidgets/include/QmitkDataStorageAbstractView.h b/Modules/QtWidgets/include/QmitkDataStorageAbstractView.h index 188cef44b1..e1da6da984 100644 --- a/Modules/QtWidgets/include/QmitkDataStorageAbstractView.h +++ b/Modules/QtWidgets/include/QmitkDataStorageAbstractView.h @@ -1,190 +1,190 @@ /*=================================================================== 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 QMITKDATASTORAGEABSTRACTVIEW_H #define QMITKDATASTORAGEABSTRACTVIEW_H #include // mitk core #include #include -// qtwidgets +// qt widgets module #include // qt #include #include /* * @brief This abstract widget provides base functionality for each concrete data storage viewer widget. * * It sets the viewer widget as a node event listener of the data storage. It accepts a node predicate * and forwards this filter to the 'QmitkIDataStorageViewModel', the actual model of the data storage viewer. * * The 'QmitkDataStorageAbstractView' offers a public slot and signal that can be used to set / propagate the selected * nodes in the current view: * The 'SetCurrentSelection'-slot finds the model indices of the given selected nodes and changes the selection of the * internal view's selection model accordingly. * The 'CurrentSelectionChanged'-signal sends a list of selected nodes to it's environment. * The 'CurrentSelectionChanged'-signal is emitted by the 'ModelSelectionChanged'-function, which transforms a model * selection into a data node list. The 'ModelSelectionChanged'-function is called when the selection of view's * selection model changes. * * The 'QmitkDataStorageAbstractView' 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. */ class MITKQTWIDGETS_EXPORT QmitkDataStorageAbstractView : public QWidget { Q_OBJECT public: virtual ~QmitkDataStorageAbstractView() = 0; /* * @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); /* * @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::Pointer nodePredicate); + void SetNodePredicate(mitk::NodePredicateBase* nodePredicate); /* * @brief Set the widget to change the current selection according to the filter. * * If true, an incoming selection will be filtered (reduced) to only those nodes that are visible by the current view. * An outgoing selection can then at most contain the filtered nodes. * If false, the incoming non-visible selection will be stored and later added to the outgoing selection, * to include the original selection that could not be modified. * The part of the original selection, that is non-visible are the nodes that are not * * @par selectOnlyVisibleNodes The bool value to define the selection modus. */ void SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes); Q_SIGNALS: /* * @brief A base signal that can be emitted in any subclass of this widget * * @par nodes A list of data nodes that are newly selected. */ void CurrentSelectionChanged(QList nodes); public Q_SLOTS: /* * @brief Transform a data node list into a model selection and set this as a new selection of the selection model of * the private member view. * * The function filters the given list of nodes according to the 'm_SelectOnlyVisibleNodes' member variable. If * necessary, the non-visible nodes are stored. This is done if 'm_SelectOnlyVisibleNodes' is false: In this case * the selection may be filtered and only a subset of the selected nodes may be visible and therefore (de-)selectable * in the data storage viewer. By storing the non-visible nodes it is possible to send the new, modified selection * but also include the selected nodes from the original selection that could not be modified (see 'SetSelectOnlyVisibleNodes'). * * @par nodes A list of data nodes that should be newly selected. */ void SetCurrentSelection(QList selectedNodes); private Q_SLOTS: /* * @brief Transform a model selection into a data node list and emit the * "CurrentSelectionChanged(QList)"-signal. * * The function adds the selected nodes from the original selection that could not be modified, if * 'm_SelectOnlyVisibleNodes' is false. * This slot is internally connected to the 'selectionChanged'-signal of the selection model of the private member view. * * @par selected The newly selected items. * @par deselected The newly deselected items. */ void ModelSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); protected: /* * @brief Set the model that should be used for the view in this widget. * * This model must return mitk::DataNode::Pointer objects for model indexes * when the role is QmitkDataNodeRole. * This model must return mitk::DataNode* objects for model indexes * when the role is QmitkDataNodeRawPointerRole. * * @par model The model to set. */ void SetModel(QmitkIDataStorageViewModel* model); /* * @brief Set the view that is included in this widget. * * @par view The view to set. */ void SetView(QAbstractItemView* view); QmitkDataStorageAbstractView(QWidget* parent = nullptr); QmitkDataStorageAbstractView(mitk::DataStorage* dataStorage, QWidget* parent = nullptr); mitk::DataStorage* m_DataStorage; - mitk::NodePredicateBase::Pointer m_NodePredicate; + mitk::NodePredicateBase* m_NodePredicate; bool m_SelectOnlyVisibleNodes; QList m_NonVisibleSelection; QmitkIDataStorageViewModel* m_Model; QAbstractItemView* m_View; private: /* * @brief Resets the model member after the data storage has been set /changed. */ void DataStorageChanged(); /* * @brief Update the model data after the node predicate has been set / changed. */ void NodePredicateChanged(); /* * @brief Callback for the data storage node added event. May be reimplemented * by subclasses. * * @par node The data node that was added. */ virtual void NodeAdded(const mitk::DataNode* node); /* * @brief Callback for the data storage node changed event. May be reimplemented * by subclasses. * * @par node The data node that was changed. */ virtual void NodeChanged(const mitk::DataNode* node); /* * @brief Callback for the data storage node removed event. May be reimplemented * by subclasses. * * @par node The data node that was removed. */ virtual void NodeRemoved(const mitk::DataNode* node); QList GetSelectedNodes() const; QList FilterNodeList(const QList& nodes) const; }; #endif // QMITKDATASTORAGEABSTRACTVIEW_H diff --git a/Modules/QtWidgets/include/QmitkIDataStorageViewModel.h b/Modules/QtWidgets/include/QmitkIDataStorageViewModel.h index 164e1fa512..dfca901206 100644 --- a/Modules/QtWidgets/include/QmitkIDataStorageViewModel.h +++ b/Modules/QtWidgets/include/QmitkIDataStorageViewModel.h @@ -1,62 +1,61 @@ /*=================================================================== 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 QMITKIDATASTORAGEVIEWMODEL_H #define QMITKIDATASTORAGEVIEWMODEL_H #include // mitk core #include #include // qt #include /* * @brief This interface extends the 'QAbstractItemModel' to accept an 'mitk::DataStorage' and a 'mitk::NodePredicateBase'. * * The interface is used inside the 'QmitkDataStorageAbstractView' and its concrete implementations 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 interface has to return mitk::DataNode::Pointer objects for model indices when the * role is QmitkDataNodeRole. */ class MITKQTWIDGETS_EXPORT QmitkIDataStorageViewModel : public QAbstractItemModel { - Q_OBJECT public: /* * @brief Sets the data storage. * * This function is called inside the "QmitkDataStorageAbstractView" to propagate a new data storage to the model. * * @par dataStorage A pointer to the data storage to set. */ virtual void SetDataStorage(mitk::DataStorage* dataStorage) = 0; /* * @brief Sets the node predicate and updates the model data, according to the node predicate. * * This function is called inside the "QmitkDataStorageAbstractView" to propagate a new node predicate to the model. * * @par nodePredicate A pointer to node predicate. */ - virtual void SetNodePredicate(mitk::NodePredicateBase::Pointer nodePredicate) = 0; + virtual void SetNodePredicate(mitk::NodePredicateBase* nodePredicate) = 0; }; #endif // QMITKIDATASTORAGEVIEWMODEL_H diff --git a/Modules/QtWidgets/src/QmitkDataStorageAbstractView.cpp b/Modules/QtWidgets/src/QmitkDataStorageAbstractView.cpp index dcb39d87d4..e9b2daaaee 100644 --- a/Modules/QtWidgets/src/QmitkDataStorageAbstractView.cpp +++ b/Modules/QtWidgets/src/QmitkDataStorageAbstractView.cpp @@ -1,244 +1,254 @@ /*=================================================================== 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 "QmitkDataStorageAbstractView.h" // qtwidgets #include "QmitkCustomVariants.h" #include "QmitkEnums.h" QmitkDataStorageAbstractView::QmitkDataStorageAbstractView(QWidget* parent/* = nullptr*/) : QWidget(parent) , m_DataStorage(nullptr) , m_NodePredicate(nullptr) , m_SelectOnlyVisibleNodes(false) , m_Model(nullptr) , m_View(nullptr) { // nothing here } QmitkDataStorageAbstractView::QmitkDataStorageAbstractView(mitk::DataStorage* dataStorage, QWidget* parent/* = nullptr*/) : QWidget(parent) , m_DataStorage(nullptr) , m_NodePredicate(nullptr) , m_SelectOnlyVisibleNodes(false) , m_Model(nullptr) , m_View(nullptr) { SetDataStorage(dataStorage); } QmitkDataStorageAbstractView::~QmitkDataStorageAbstractView() { if (nullptr != m_DataStorage) { // remove listener from old data storage m_DataStorage->AddNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkDataStorageAbstractView::NodeAdded)); m_DataStorage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkDataStorageAbstractView::NodeRemoved)); m_DataStorage->ChangedNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkDataStorageAbstractView::NodeChanged)); } } void QmitkDataStorageAbstractView::SetDataStorage(mitk::DataStorage* dataStorage) { - if (m_DataStorage == dataStorage || nullptr == dataStorage) + if (m_DataStorage == dataStorage) { return; } if (nullptr != m_DataStorage) { // remove listener from old data storage m_DataStorage->AddNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkDataStorageAbstractView::NodeAdded)); m_DataStorage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkDataStorageAbstractView::NodeRemoved)); m_DataStorage->ChangedNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkDataStorageAbstractView::NodeChanged)); } m_DataStorage = dataStorage; - // add listener for new data storage - m_DataStorage->AddNodeEvent.AddListener( - mitk::MessageDelegate1(this, &QmitkDataStorageAbstractView::NodeAdded)); - m_DataStorage->RemoveNodeEvent.AddListener( - mitk::MessageDelegate1(this, &QmitkDataStorageAbstractView::NodeRemoved)); - m_DataStorage->ChangedNodeEvent.AddListener( - mitk::MessageDelegate1(this, &QmitkDataStorageAbstractView::NodeChanged)); + if (nullptr != dataStorage) + { + // add listener for new data storage + m_DataStorage->AddNodeEvent.AddListener( + mitk::MessageDelegate1(this, &QmitkDataStorageAbstractView::NodeAdded)); + m_DataStorage->RemoveNodeEvent.AddListener( + mitk::MessageDelegate1(this, &QmitkDataStorageAbstractView::NodeRemoved)); + m_DataStorage->ChangedNodeEvent.AddListener( + mitk::MessageDelegate1(this, &QmitkDataStorageAbstractView::NodeChanged)); + } // update model if the data storage has been changed DataStorageChanged(); } void QmitkDataStorageAbstractView::DataStorageChanged() { + if (nullptr == m_Model) + { + return; + } + m_Model->SetDataStorage(m_DataStorage); } -void QmitkDataStorageAbstractView::SetNodePredicate(mitk::NodePredicateBase::Pointer nodePredicate) +void QmitkDataStorageAbstractView::SetNodePredicate(mitk::NodePredicateBase* nodePredicate) { if (m_NodePredicate == nodePredicate) { return; } m_NodePredicate = nodePredicate; // update model if the node predicate has been changed NodePredicateChanged(); } void QmitkDataStorageAbstractView::NodePredicateChanged() { m_Model->SetNodePredicate(m_NodePredicate); } void QmitkDataStorageAbstractView::SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes) { m_SelectOnlyVisibleNodes = selectOnlyVisibleNodes; } void QmitkDataStorageAbstractView::SetCurrentSelection(QList selectedNodes) { // filter input nodes and return the modified input node list QList filteredNodes = FilterNodeList(selectedNodes); // get the currently selected nodes from the model QList currentlySelectedNodes = GetSelectedNodes(); // compare currently selected nodes with filtered input node list if (currentlySelectedNodes.size() == filteredNodes.size()) { // lambda to compare node pointer inside both lists auto lambda = [](mitk::DataNode::Pointer lhs, mitk::DataNode::Pointer rhs) { return lhs == rhs; }; bool equal = std::equal(filteredNodes.begin(), filteredNodes.end(), currentlySelectedNodes.begin(), currentlySelectedNodes.end(), lambda); if (equal) { // node lists are equal, no need to update the selection model return; } } if (!m_SelectOnlyVisibleNodes) { // store the unmodified selection m_NonVisibleSelection = selectedNodes; // remove the nodes in the original selection that are already contained in the filtered input node list // this will keep the selection of the original nodes that are not presented by the current view unmodified, but allows to change the selection of the filtered nodes // later, the nodes of the 'm_NonVisibleSelection' member have to be added again to the list of selected (filtered) nodes, if a selection is sent from the current view (see 'ModelSelectionChanged') auto lambda = [&filteredNodes](mitk::DataNode::Pointer original) { return filteredNodes.contains(original); }; - m_NonVisibleSelection.erase(std::remove_if(m_NonVisibleSelection.begin(), m_NonVisibleSelection.end(), lambda)); + m_NonVisibleSelection.erase(std::remove_if(m_NonVisibleSelection.begin(), m_NonVisibleSelection.end(), lambda), + m_NonVisibleSelection.end()); } // create new selection by retrieving the corresponding indices of the (filtered) nodes QItemSelection newCurrentSelection; for (const auto& node : filteredNodes) { - QModelIndexList matched = m_Model->match(m_Model->index(0, 0), QmitkDataNodeRawPointerRole, QVariant::fromValue(node), 1, Qt::MatchRecursive); + QModelIndexList matched = m_Model->match(m_Model->index(0, 0), QmitkDataNodeRole, QVariant::fromValue(node), 1, Qt::MatchRecursive); if (!matched.empty()) { newCurrentSelection.select(matched.front(), matched.front()); } } - m_View->selectionModel()->select(newCurrentSelection, QItemSelectionModel::Select); + m_View->selectionModel()->select(newCurrentSelection, QItemSelectionModel::ClearAndSelect); } void QmitkDataStorageAbstractView::ModelSelectionChanged(const QItemSelection& /*selected*/, const QItemSelection& /*deselected*/) { QList nodes = GetSelectedNodes(); if (!m_SelectOnlyVisibleNodes) { // add the non-visible nodes from the original selection nodes.append(m_NonVisibleSelection); } emit CurrentSelectionChanged(nodes); } void QmitkDataStorageAbstractView::SetModel(QmitkIDataStorageViewModel* model) { m_Model = model; + DataStorageChanged(); } void QmitkDataStorageAbstractView::SetView(QAbstractItemView* view) { if (nullptr != m_View) { disconnect(m_View->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), this, SLOT(ModelSelectionChanged(const QItemSelection&, const QItemSelection&))); } m_View = view; connect(m_View->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), SLOT(ModelSelectionChanged(const QItemSelection&, const QItemSelection&))); } void QmitkDataStorageAbstractView::NodeAdded(const mitk::DataNode* node) { // nothing here; can be overwritten in subclass } void QmitkDataStorageAbstractView::NodeChanged(const mitk::DataNode* node) { // nothing here; can be overwritten in subclass } void QmitkDataStorageAbstractView::NodeRemoved(const mitk::DataNode* node) { // nothing here; can be overwritten in subclass } QList QmitkDataStorageAbstractView::GetSelectedNodes() const { QList nodes; QModelIndexList selectedIndexes = m_View->selectionModel()->selectedIndexes(); for (const auto& index : selectedIndexes) { QVariant qvariantDataNode = m_Model->data(index, QmitkDataNodeRole); if (qvariantDataNode.canConvert()) { nodes.push_back(qvariantDataNode.value()); } } return nodes; } QList QmitkDataStorageAbstractView::FilterNodeList(const QList& nodes) const { if (nodes.isEmpty()) { return QList(); } - if (m_NodePredicate.IsNull()) + if (nullptr == m_NodePredicate) { // no filter set return nodes; } QList result; for (const auto& node : nodes) { if (true == m_NodePredicate->CheckNode(node)) { result.push_back(node); } } return result; }