diff --git a/Modules/QtWidgets/src/QmitkAbstractDataStorageModel.cpp b/Modules/QtWidgets/src/QmitkAbstractDataStorageModel.cpp index fc21e0bbd5..5718d7e938 100644 --- a/Modules/QtWidgets/src/QmitkAbstractDataStorageModel.cpp +++ b/Modules/QtWidgets/src/QmitkAbstractDataStorageModel.cpp @@ -1,130 +1,130 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #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 (!m_DataStorage.IsExpired()) { auto dataStorage = m_DataStorage.Lock(); - if (dataStorage.IsNull()) + if (dataStorage.IsNotNull()) { // remove Listener for the data storage itself dataStorage->RemoveObserver(m_DataStorageDeletedTag); // remove listener from data storage dataStorage->AddNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeAdded)); dataStorage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeRemoved)); dataStorage->ChangedNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeChanged)); } } } void QmitkAbstractDataStorageModel::SetDataStorage(mitk::DataStorage* dataStorage) { if (m_DataStorage == dataStorage) { return; } if (!m_DataStorage.IsExpired()) { auto dataStorage = m_DataStorage.Lock(); - if (dataStorage.IsNull()) + if (dataStorage.IsNotNull()) { // remove Listener for the data storage itself dataStorage->RemoveObserver(m_DataStorageDeletedTag); // remove listener from old data storage dataStorage->AddNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeAdded)); dataStorage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeRemoved)); dataStorage->ChangedNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeChanged)); } } m_DataStorage = dataStorage; if (!m_DataStorage.IsExpired()) { auto dataStorage = m_DataStorage.Lock(); - if (dataStorage.IsNull()) + if (dataStorage.IsNotNull()) { // 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 dataStorage->AddNodeEvent.AddListener( mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeAdded)); dataStorage->RemoveNodeEvent.AddListener( mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeRemoved)); dataStorage->ChangedNodeEvent.AddListener( mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeChanged)); } } // update model if the data storage has been changed DataStorageChanged(); } mitk::DataStorage* QmitkAbstractDataStorageModel::GetDataStorage() const { if (m_DataStorage.IsExpired()) { return nullptr; } return m_DataStorage.Lock().GetPointer(); } void QmitkAbstractDataStorageModel::SetDataStorageDeleted() { this->SetDataStorage(nullptr); } void QmitkAbstractDataStorageModel::SetNodePredicate(const 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/QmitkDataStorageHistoryModel.cpp b/Modules/QtWidgets/src/QmitkDataStorageHistoryModel.cpp index 0d93016be3..c394e7cf83 100644 --- a/Modules/QtWidgets/src/QmitkDataStorageHistoryModel.cpp +++ b/Modules/QtWidgets/src/QmitkDataStorageHistoryModel.cpp @@ -1,89 +1,89 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include "mitkWeakPointer.h" #include #include #include /** Type of the internal history container. Remark: It stores raw pointer instead of mitk::WeakPointer because this lead to occasional crashes when the application was closed (see T24770 for a similar problem with the same reason*/ using NodeHistoryType = std::deque< const mitk::DataNode* >; /** History of node selection. It is sorted from new to old.*/ NodeHistoryType _nodeHistory; std::mutex _historyMutex; QmitkDataStorageHistoryModel::QmitkDataStorageHistoryModel(QObject *parent) : QmitkDataStorageDefaultListModel(parent) { } void QmitkDataStorageHistoryModel::UpdateModelData() { std::vector dataNodes; if (!m_DataStorage.IsExpired()) { auto dataStorage = m_DataStorage.Lock(); - if (dataStorage.IsNull()) + if (dataStorage.IsNotNull()) { mitk::DataStorage::SetOfObjects::ConstPointer nodesCandidats; if (m_NodePredicate.IsNotNull()) { nodesCandidats = dataStorage->GetSubset(m_NodePredicate); } else { nodesCandidats = dataStorage->GetAll(); } const std::lock_guard lock(_historyMutex); for (auto historyNode : _nodeHistory) { auto finding = std::find(nodesCandidats->begin(), nodesCandidats->end(), historyNode); if (finding != nodesCandidats->end()) { dataNodes.push_back(*finding); } } } } // update the model, so that it will be filled with the nodes of the new data storage beginResetModel(); m_DataNodes = dataNodes; endResetModel(); } void QmitkDataStorageHistoryModel::AddNodeToHistory(mitk::DataNode* node) { const std::lock_guard lock(_historyMutex); auto finding = std::find(std::begin(_nodeHistory), std::end(_nodeHistory), node); while (finding != std::end(_nodeHistory)) { _nodeHistory.erase(finding); finding = std::find(std::begin(_nodeHistory), std::end(_nodeHistory), node); } _nodeHistory.push_front(node); } void QmitkDataStorageHistoryModel::ResetHistory() { const std::lock_guard lock(_historyMutex); _nodeHistory.clear(); } diff --git a/Plugins/org.mitk.gui.qt.common/src/QmitkSingleNodeSelectionWidget.cpp b/Plugins/org.mitk.gui.qt.common/src/QmitkSingleNodeSelectionWidget.cpp index c0e862a6bb..fbd19ddff3 100644 --- a/Plugins/org.mitk.gui.qt.common/src/QmitkSingleNodeSelectionWidget.cpp +++ b/Plugins/org.mitk.gui.qt.common/src/QmitkSingleNodeSelectionWidget.cpp @@ -1,338 +1,356 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkSingleNodeSelectionWidget.h" #include + +#include "mitkNodePredicateFunction.h" +#include "mitkNodePredicateAnd.h" + #include #include "QmitkNodeSelectionDialog.h" #include "QmitkNodeDetailsDialog.h" QmitkSingleNodeSelectionWidget::QmitkSingleNodeSelectionWidget(QWidget* parent) : QmitkAbstractNodeSelectionWidget(parent), m_AutoSelectNewNodes(false) { m_Controls.setupUi(this); m_Controls.btnSelect->installEventFilter(this); m_Controls.btnSelect->setVisible(true); m_Controls.btnClear->setVisible(false); m_Controls.btnClear->setIcon(berry::QtStyleManager::ThemeIcon(QStringLiteral(":/org.mitk.gui.qt.common/times.svg"))); this->UpdateInfo(); connect(m_Controls.btnClear, SIGNAL(clicked(bool)), this, SLOT(OnClearSelection())); } QmitkSingleNodeSelectionWidget::~QmitkSingleNodeSelectionWidget() { } mitk::DataNode::Pointer QmitkSingleNodeSelectionWidget::ExtractCurrentValidSelection(const NodeList& nodes) const { mitk::DataNode::Pointer result = nullptr; if (!m_DataStorage.IsExpired()) { auto storage = m_DataStorage.Lock(); if (storage.IsNotNull()) { for (auto node : nodes) { bool valid = storage->Exists(node); if (valid && m_NodePredicate.IsNotNull()) { valid = m_NodePredicate->CheckNode(node); } if (valid) { result = node; break; } } } } return result; } QmitkSingleNodeSelectionWidget::NodeList QmitkSingleNodeSelectionWidget::CompileEmitSelection() const { NodeList result; if (!m_SelectOnlyVisibleNodes) { result = m_ExternalSelection; } if (m_SelectedNode.IsNotNull() && !result.contains(m_SelectedNode)) { result.append(m_SelectedNode); } return result; } void QmitkSingleNodeSelectionWidget::OnNodePredicateChanged(const mitk::NodePredicateBase* /*newPredicate*/) { auto lastEmission = this->CompileEmitSelection(); if (m_NodePredicate.IsNotNull() && m_SelectedNode.IsNotNull() && !m_NodePredicate->CheckNode(m_SelectedNode)) { m_SelectedNode = nullptr; } if (m_SelectedNode.IsNull()) { m_SelectedNode = this->ExtractCurrentValidSelection(m_ExternalSelection); } this->DoAutoSelectIfNeeded(); this->EmitAndUpdateIfNeeded(lastEmission); }; void QmitkSingleNodeSelectionWidget::OnDataStorageChanged() { auto lastEmission = this->CompileEmitSelection(); if (m_DataStorage.IsExpired()) { m_SelectedNode = nullptr; } else if (m_SelectedNode.IsNotNull()) { auto storage = m_DataStorage.Lock(); if (storage.IsNotNull() && !storage->Exists(m_SelectedNode)) { m_SelectedNode = nullptr; } } if (m_SelectedNode.IsNull()) { m_SelectedNode = this->ExtractCurrentValidSelection(m_ExternalSelection); } this->DoAutoSelectIfNeeded(); this->EmitAndUpdateIfNeeded(lastEmission); }; void QmitkSingleNodeSelectionWidget::OnClearSelection() { if (m_IsOptional) { NodeList emptyList; this->SetCurrentSelection(emptyList); } this->UpdateInfo(); } mitk::DataNode::Pointer QmitkSingleNodeSelectionWidget::GetSelectedNode() const { return m_SelectedNode; }; bool QmitkSingleNodeSelectionWidget::eventFilter(QObject *obj, QEvent *ev) { if (obj == m_Controls.btnSelect) { if (ev->type() == QEvent::MouseButtonRelease) { auto mouseEv = dynamic_cast(ev); if (!mouseEv) { return false; } if (mouseEv->button() == Qt::LeftButton) { if (this->isEnabled()) { this->EditSelection(); return true; } } else { auto selection = this->CompileEmitSelection(); if (!selection.empty()) { QmitkNodeDetailsDialog infoDialog(selection, this); infoDialog.exec(); return true; } } } } return false; } void QmitkSingleNodeSelectionWidget::EditSelection() { QmitkNodeSelectionDialog* dialog = new QmitkNodeSelectionDialog(this, m_PopUpTitel, m_PopUpHint); dialog->SetDataStorage(m_DataStorage.Lock()); dialog->SetNodePredicate(m_NodePredicate); NodeList list; if (m_SelectedNode.IsNotNull()) { list.append(m_SelectedNode); } dialog->SetCurrentSelection(list); dialog->SetSelectOnlyVisibleNodes(m_SelectOnlyVisibleNodes); dialog->SetSelectionMode(QAbstractItemView::SingleSelection); m_Controls.btnSelect->setChecked(true); if (dialog->exec()) { auto lastEmission = this->CompileEmitSelection(); auto nodes = dialog->GetSelectedNodes(); if (nodes.empty()) { m_SelectedNode = nullptr; } else { m_SelectedNode = nodes.first(); } this->EmitAndUpdateIfNeeded(lastEmission); } m_Controls.btnSelect->setChecked(false); delete dialog; }; void QmitkSingleNodeSelectionWidget::UpdateInfo() { if (m_SelectedNode.IsNull()) { if (m_IsOptional) { m_Controls.btnSelect->SetNodeInfo(m_EmptyInfo); } else { m_Controls.btnSelect->SetNodeInfo(m_InvalidInfo); } m_Controls.btnSelect->SetSelectionIsOptional(m_IsOptional); m_Controls.btnClear->setVisible(false); } else { m_Controls.btnClear->setVisible(m_IsOptional); } m_Controls.btnSelect->SetSelectedNode(m_SelectedNode); }; void QmitkSingleNodeSelectionWidget::SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes) { auto lastEmission = this->CompileEmitSelection(); m_SelectOnlyVisibleNodes = selectOnlyVisibleNodes; this->EmitAndUpdateIfNeeded(lastEmission); }; -void QmitkSingleNodeSelectionWidget::DoAutoSelectIfNeeded() +void QmitkSingleNodeSelectionWidget::DoAutoSelectIfNeeded(const mitk::DataNode* ignoreNode) { if (m_SelectedNode.IsNull() && m_AutoSelectNewNodes && !m_DataStorage.IsExpired()) { auto storage = m_DataStorage.Lock(); - if (m_NodePredicate.IsNotNull()) - { - m_SelectedNode = storage->GetNode(m_NodePredicate); - } - else + if (storage.IsNotNull()) { - m_SelectedNode = nullptr; - auto nodes = storage->GetAll(); - if (!nodes->empty()) + auto ignoreCheck = [ignoreNode](const mitk::DataNode * node) { - m_SelectedNode = nodes->front(); + return node != ignoreNode; + }; + mitk::NodePredicateFunction::Pointer isNotIgnoredNode = mitk::NodePredicateFunction::New(ignoreCheck); + mitk::NodePredicateBase::Pointer predicate = isNotIgnoredNode.GetPointer(); + + if (m_NodePredicate.IsNotNull()) + { + predicate = mitk::NodePredicateAnd::New(m_NodePredicate.GetPointer(), predicate.GetPointer()).GetPointer(); } + + m_SelectedNode = storage->GetNode(predicate); } } } void QmitkSingleNodeSelectionWidget::EmitAndUpdateIfNeeded(const NodeList& lastEmission) { auto newEmission = this->CompileEmitSelection(); if (!EqualNodeSelections(lastEmission, newEmission)) { this->UpdateInfo(); emit CurrentSelectionChanged(newEmission); } }; +void QmitkSingleNodeSelectionWidget::SetCurrentSelectedNode(mitk::DataNode* selectedNode) +{ + NodeList selection; + if (selectedNode) + { + selection.append(selectedNode); + } + this->SetCurrentSelection(selection); +}; void QmitkSingleNodeSelectionWidget::SetCurrentSelection(NodeList selectedNodes) { auto lastEmission = this->CompileEmitSelection(); m_ExternalSelection = selectedNodes; m_SelectedNode = this->ExtractCurrentValidSelection(selectedNodes); this->DoAutoSelectIfNeeded(); this->EmitAndUpdateIfNeeded(lastEmission); }; void QmitkSingleNodeSelectionWidget::NodeAddedToStorage(const mitk::DataNode* /*node*/) { if (m_SelectedNode.IsNull() && m_AutoSelectNewNodes && !m_DataStorage.IsExpired()) { auto lastEmission = this->CompileEmitSelection(); this->DoAutoSelectIfNeeded(); this->EmitAndUpdateIfNeeded(lastEmission); } } void QmitkSingleNodeSelectionWidget::NodeRemovedFromStorage(const mitk::DataNode* node) { if (m_SelectedNode == node && node != nullptr) { m_SelectedNode = nullptr; - this->DoAutoSelectIfNeeded(); + //This event is triggerd before the node is realy removed from storage. + //Therefore we have to explictly ignore him in the autoselect search. + this->DoAutoSelectIfNeeded(node); auto newEmission = this->CompileEmitSelection(); emit CurrentSelectionChanged(newEmission); this->UpdateInfo(); } } bool QmitkSingleNodeSelectionWidget::GetAutoSelectNewNodes() const { return m_AutoSelectNewNodes; } void QmitkSingleNodeSelectionWidget::SetAutoSelectNewNodes(bool autoSelect) { m_AutoSelectNewNodes = autoSelect; this->NodeAddedToStorage(nullptr); } diff --git a/Plugins/org.mitk.gui.qt.common/src/QmitkSingleNodeSelectionWidget.h b/Plugins/org.mitk.gui.qt.common/src/QmitkSingleNodeSelectionWidget.h index 8df76ec2d3..3653dbf262 100644 --- a/Plugins/org.mitk.gui.qt.common/src/QmitkSingleNodeSelectionWidget.h +++ b/Plugins/org.mitk.gui.qt.common/src/QmitkSingleNodeSelectionWidget.h @@ -1,87 +1,88 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QMITK_SINGLE_NODE_SELECTION_WIDGET_H #define QMITK_SINGLE_NODE_SELECTION_WIDGET_H #include #include #include #include "org_mitk_gui_qt_common_Export.h" #include "ui_QmitkSingleNodeSelectionWidget.h" #include #include class QmitkAbstractDataStorageModel; /** * \class QmitkSingleNodeSelectionWidget * \brief Widget that represents a node selection of (max) one node. It acts like a button. Clicking on it * allows to change the selection. */ class MITK_QT_COMMON QmitkSingleNodeSelectionWidget : public QmitkAbstractNodeSelectionWidget { Q_OBJECT public: explicit QmitkSingleNodeSelectionWidget(QWidget* parent = nullptr); ~QmitkSingleNodeSelectionWidget() override; mitk::DataNode::Pointer GetSelectedNode() const; bool GetAutoSelectNewNodes() const; using NodeList = QmitkAbstractNodeSelectionWidget::NodeList; public Q_SLOTS: void SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes) override; void SetCurrentSelection(NodeList selectedNodes) override; + void SetCurrentSelectedNode(mitk::DataNode* selectedNode); /** Sets the auto selection mode (Default is false). If auto select is true and the following conditions are fullfilled, the widget will select a node automatically from the data storage: - a data storage is set - data storage contains at least one node that matches the given predicate - no selection is set.*/ void SetAutoSelectNewNodes(bool autoSelect); protected Q_SLOTS: virtual void OnClearSelection(); protected: mitk::DataNode::Pointer ExtractCurrentValidSelection(const NodeList& nodes) const; NodeList CompileEmitSelection() const; bool eventFilter(QObject *obj, QEvent *ev) override; void EditSelection(); void UpdateInfo() override; void OnNodePredicateChanged(const mitk::NodePredicateBase* newPredicate) override; void OnDataStorageChanged() override; void NodeAddedToStorage(const mitk::DataNode* node) override; void NodeRemovedFromStorage(const mitk::DataNode* node) override; - void DoAutoSelectIfNeeded(); + void DoAutoSelectIfNeeded(const mitk::DataNode* ignoreNode = nullptr); void EmitAndUpdateIfNeeded(const NodeList& lastEmission); NodeList m_ExternalSelection; mitk::DataNode::Pointer m_SelectedNode; /** See documentation of SetAutoSelectNewNodes for details*/ bool m_AutoSelectNewNodes; Ui_QmitkSingleNodeSelectionWidget m_Controls; }; #endif // QmitkSingleNodeSelectionWidget_H