diff --git a/Modules/Core/include/mitkLevelWindowManager.h b/Modules/Core/include/mitkLevelWindowManager.h index bf3bdcc8f7..9a650648cb 100644 --- a/Modules/Core/include/mitkLevelWindowManager.h +++ b/Modules/Core/include/mitkLevelWindowManager.h @@ -1,177 +1,186 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 MITKLEVELWINDOWMANAGER_H #define MITKLEVELWINDOWMANAGER_H #include "mitkBaseProperty.h" #include "mitkDataStorage.h" #include "mitkLevelWindowProperty.h" #include #include namespace mitk { /** \brief Provides access to the LevelWindowProperty object and LevelWindow of the "current" image. - provides a LevelWindowProperty for purposes like GUI editors - this property comes from one of two possible sources - either something (e.g. the application) sets the property because of some user selection - OR the "Auto top-most" logic is used to search a DataStorage for the image with the highest "layer" property value Changes on Level/Window can be set with SetLevelWindow() and will affect either the topmost layer image, if isAutoTopMost() returns true, or an image which is set by SetLevelWindowProperty(LevelWindowProperty::Pointer levelWindowProperty). Changes to Level/Window, when another image gets active or by SetLevelWindow(const LevelWindow& levelWindow), will be sent to all listeners by Modified(). DataStorageChanged() listens to the DataStorage for new or removed images. Depending on how m_AutoTopMost is set, the new image becomes active or not. If an image is removed from the DataStorage and m_AutoTopMost is false, there is a check to proof, if the active image is still available. If not, then m_AutoTopMost becomes true. Note that this class is not thread safe at the moment! */ class MITKCORE_EXPORT LevelWindowManager : public itk::Object { public: mitkClassMacroItkParent(LevelWindowManager, itk::Object) itkFactorylessNewMacro(Self) itkCloneMacro(Self) void SetDataStorage(DataStorage *ds); DataStorage *GetDataStorage(); /** @brief (Re-)Initializes the LevelWindowManager by setting the topmost image. * Use the removedNode parameter if a node was removed. * @param autoTopMost Sets the topmost layer image to be affected by changes, if true. * @param removedNode A node was removed from the data storage if != nullptr. */ void SetAutoTopMostImage(bool autoTopMost, const DataNode *removedNode = nullptr); /** @brief (Re-)Initializes the LevelWindowManager by setting the selected images. * Use the removedNode parameter if a node was removed. * @param selectedImages Sets the selected images to be affected by changes, if true. * @param removedNode A node was removed from the data storage if != nullptr. */ void SetSelectedImages(bool selectedImages, const DataNode *removedNode = nullptr); - void RecalculateLevelWindowForSelectedComponent(const itk::EventObject &); + void RecalculateLevelWindowForSelectedComponent(const itk::EventObject&); - void Update(const itk::EventObject &e); ///< gets called if a visible property changes + /** + * @brief Updates the level window. + * This function is called if a property of a data node is changed. + * Relevant properties are defined in the protected 'ObserverToPropertyValueMap'-members. + */ + void Update(const itk::EventObject&); + void UpdateSelected(const itk::EventObject&); /** @brief Set a specific LevelWindowProperty; all changes will affect the image belonging to this property. * @throw mitk::Exception Throws an exception if the there is no image in the data storage which belongs to this * property.*/ void SetLevelWindowProperty(LevelWindowProperty::Pointer levelWindowProperty); /** @brief Sets new Level/Window values and informs all listeners about changes. */ void SetLevelWindow(const LevelWindow &levelWindow); /** @return Returns Level/Window values for the current image.*/ const LevelWindow &GetLevelWindow(); /** @return Returns the current mitkLevelWindowProperty object from the image that is affected by changes.*/ LevelWindowProperty::Pointer GetLevelWindowProperty(); /** @return true if changes on slider or line-edits will affect the topmost layer image. */ bool IsAutoTopMost(); /** @return true if changes on slider or line-edits will affect the currently selected images. */ bool IsSelectedImages(); /** @brief This method is called when a node is added to the data storage. * A listener on the data storage is used to call this method automatically after a node was added. * @throw mitk::Exception Throws an exception if something is wrong, e.g. if the number of observers differs from * the * number of nodes. */ void DataStorageAddedNode(const DataNode *n = nullptr); /** @brief This method is called when a node is removed to the data storage. * A listener on the data storage is used to call this method automatically directly before a node will be * removed. * @throw mitk::Exception Throws an exception if something is wrong, e.g. if the number of observers differs from * the * number of nodes. */ void DataStorageRemovedNode(const DataNode *removedNode = nullptr); /** @brief change notifications from mitkLevelWindowProperty */ void OnPropertyModified(const itk::EventObject &e); Image *GetCurrentImage(); ///< return the currently active image /** * @return Returns the current number of observers which are registered in this object. * @throw mitk::Exception Throws an exception if the number of observers differs from * the number of relevant objects * which means that something is wrong. * */ int GetNumberOfObservers(); /** * returns all nodes in the DataStorage that have the following properties: * "visible" == true, "binary" == false, "levelwindow", and DataType == Image */ DataStorage::SetOfObjects::ConstPointer GetRelevantNodes(); protected: LevelWindowManager(); ~LevelWindowManager() override; DataStorage::Pointer m_DataStorage; /// Pointer to the LevelWindowProperty of the current image. LevelWindowProperty::Pointer m_LevelWindowProperty; typedef std::pair PropDataPair; typedef std::map ObserverToPropertyValueMap; /// Map to hold observer IDs to every "visible" property of DataNode's BaseProperty. ObserverToPropertyValueMap m_ObserverToVisibleProperty; /// Map to hold observer IDs to every "layer" property of DataNode's BaseProperty. ObserverToPropertyValueMap m_ObserverToLayerProperty; /// Map to hold observer IDs to every "Image Rendering.Mode" property of DataNode's BaseProperty. ObserverToPropertyValueMap m_ObserverToRenderingModeProperty; /// Map to hold observer IDs to every "Image.Displayed Component" property of DataNode's BaseProperty. ObserverToPropertyValueMap m_ObserverToDisplayedComponentProperty; /// Map to hold observer IDs to every "imageForLevelWindow" property of DataNode's BaseProperty. ObserverToPropertyValueMap m_ObserverToLevelWindowImageProperty; + /// Map to hold observer IDs to every "selected" property of DataNode's BaseProperty. + ObserverToPropertyValueMap m_ObserverToSelectedProperty; /// Updates the internal observer list. /// Ignores nodes which are marked to be deleted in the variable m_NodeMarkedToDelete. void UpdateObservers(); /// Internal help method to clear both lists/maps. void ClearPropObserverLists(); /// Internal help method to create both lists/maps. void CreatePropObserverLists(); /// This variable holds a data node which will be deleted from the datastorage immediately /// Nullptr, if there is no data node to be deleted. const DataNode *m_NodeMarkedToDelete; bool m_AutoTopMost; + bool m_SelectedImages; unsigned long m_ObserverTag; bool m_IsObserverTagSet; unsigned long m_PropertyModifiedTag; Image *m_CurrentImage; bool m_IsPropertyModifiedTagSet; bool m_LevelWindowMutex; }; } #endif // MITKLEVELWINDOWMANAGER_H diff --git a/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp b/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp index cc0bf7fc09..ba7b1a49f1 100644 --- a/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp +++ b/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp @@ -1,629 +1,748 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 "mitkLevelWindowManager.h" #include "mitkDataStorage.h" #include "mitkImage.h" #include "mitkMessage.h" #include "mitkNodePredicateAnd.h" #include "mitkNodePredicateBase.h" #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateNot.h" #include "mitkNodePredicateOr.h" #include "mitkNodePredicateProperty.h" #include "mitkProperties.h" #include "mitkRenderingModeProperty.h" #include mitk::LevelWindowManager::LevelWindowManager() : m_DataStorage(nullptr) , m_LevelWindowProperty(nullptr) , m_AutoTopMost(true) + , m_SelectedImages(false) , m_IsObserverTagSet(false) , m_CurrentImage(nullptr) , m_IsPropertyModifiedTagSet(false) , m_LevelWindowMutex(false) { } mitk::LevelWindowManager::~LevelWindowManager() { if (m_DataStorage.IsNotNull()) { m_DataStorage->AddNodeEvent.RemoveListener( MessageDelegate1(this, &LevelWindowManager::DataStorageAddedNode)); m_DataStorage->RemoveNodeEvent.RemoveListener( MessageDelegate1(this, &LevelWindowManager::DataStorageRemovedNode)); m_DataStorage = nullptr; } if (m_IsPropertyModifiedTagSet && m_LevelWindowProperty.IsNotNull()) { m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); m_IsPropertyModifiedTagSet = false; } // clear both observer maps this->ClearPropObserverLists(); } void mitk::LevelWindowManager::SetDataStorage(DataStorage *ds) { if (ds == nullptr) return; /* remove listeners of old DataStorage */ if (m_DataStorage.IsNotNull()) { m_DataStorage->AddNodeEvent.RemoveListener( MessageDelegate1(this, &LevelWindowManager::DataStorageAddedNode)); m_DataStorage->RemoveNodeEvent.RemoveListener( MessageDelegate1(this, &LevelWindowManager::DataStorageRemovedNode)); } /* register listener for new DataStorage */ m_DataStorage = ds; // register m_DataStorage->AddNodeEvent.AddListener( MessageDelegate1(this, &LevelWindowManager::DataStorageAddedNode)); m_DataStorage->RemoveNodeEvent.AddListener( MessageDelegate1(this, &LevelWindowManager::DataStorageRemovedNode)); this->DataStorageAddedNode(); // update us with new DataStorage } void mitk::LevelWindowManager::OnPropertyModified(const itk::EventObject &) { Modified(); } void mitk::LevelWindowManager::SetAutoTopMostImage(bool autoTopMost, const DataNode *removedNode/* = nullptr*/) { m_AutoTopMost = autoTopMost; if (false == m_AutoTopMost) { return; } + // deactivate other mode + m_SelectedImages = false; + if (m_IsPropertyModifiedTagSet && m_LevelWindowProperty.IsNotNull()) { m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); m_IsPropertyModifiedTagSet = false; } // find topmost image in the data storage if (m_DataStorage.IsNull()) { itkExceptionMacro("DataStorage not set"); } DataNode::Pointer topLevelNode; int maxLayer = itk::NumericTraits::min(); m_LevelWindowProperty = nullptr; DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode::Pointer node = it->Value(); if (node.IsNull() || (removedNode != nullptr && node == removedNode)) continue; m_LevelWindowMutex = true; node->SetBoolProperty("imageForLevelWindow", false); m_LevelWindowMutex = false; if (node->IsVisible(nullptr) == false) continue; int layer = 0; node->GetIntProperty("layer", layer); if (layer < maxLayer) continue; LevelWindowProperty::Pointer levelWindowProperty = dynamic_cast(node->GetProperty("levelwindow")); if (levelWindowProperty.IsNull()) continue; int nonLvlWinMode1 = RenderingModeProperty::LOOKUPTABLE_COLOR; int nonLvlWinMode2 = RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR; RenderingModeProperty::Pointer mode = dynamic_cast(node->GetProperty("Image Rendering.Mode")); if (mode.IsNotNull()) { int currMode = mode->GetRenderingMode(); if (currMode == nonLvlWinMode1 || currMode == nonLvlWinMode2) { continue; } } else continue; m_LevelWindowProperty = levelWindowProperty; m_CurrentImage = dynamic_cast(node->GetData()); topLevelNode = node; maxLayer = layer; } if (topLevelNode.IsNotNull()) { m_LevelWindowMutex = true; topLevelNode->SetBoolProperty("imageForLevelWindow", true); m_LevelWindowMutex = false; } this->SetLevelWindowProperty(m_LevelWindowProperty); if (m_LevelWindowProperty.IsNull()) { Modified(); } // else SetLevelWindowProperty will call Modified(); } +void mitk::LevelWindowManager::SetSelectedImages(bool selectedImages, const DataNode *removedNode/* = nullptr*/) +{ + m_SelectedImages = selectedImages; + if (false == m_SelectedImages) + { + return; + } + + // deactivate other mode + m_AutoTopMost = false; + + if (m_IsPropertyModifiedTagSet && m_LevelWindowProperty.IsNotNull()) + { + m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); + m_IsPropertyModifiedTagSet = false; + } + + // find selected images in the data storage + if (m_DataStorage.IsNull()) + { + itkExceptionMacro("DataStorage not set"); + } + + DataNode::Pointer lastSelectedNode; + m_LevelWindowProperty = nullptr; + + DataStorage::SetOfObjects::ConstPointer all = GetRelevantNodes(); + for (DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) + { + DataNode::Pointer node = it->Value(); + if (node.IsNull() || node == removedNode) + { + continue; + } + + m_LevelWindowMutex = true; + node->SetBoolProperty("imageForLevelWindow", false); + m_LevelWindowMutex = false; + + bool selected = node->IsSelected(); + if (false == selected) + { + continue; + } + + LevelWindowProperty::Pointer levelWindowProperty = + dynamic_cast(node->GetProperty("levelwindow")); + if (levelWindowProperty.IsNull()) + { + continue; + } + + m_LevelWindowMutex = true; + node->SetBoolProperty("imageForLevelWindow", true); + m_LevelWindowMutex = false; + int nonLvlWinMode1 = RenderingModeProperty::LOOKUPTABLE_COLOR; + int nonLvlWinMode2 = RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR; + + RenderingModeProperty::Pointer mode = + dynamic_cast(node->GetProperty("Image Rendering.Mode")); + + if (mode.IsNotNull()) + { + int currMode = mode->GetRenderingMode(); + if (currMode == nonLvlWinMode1 || currMode == nonLvlWinMode2) + { + continue; + } + } + else + { + continue; + } + + m_LevelWindowProperty = levelWindowProperty; + m_CurrentImage = dynamic_cast(node->GetData()); + lastSelectedNode = node; + } + + SetLevelWindowProperty(m_LevelWindowProperty); + + if (m_LevelWindowProperty.IsNull()) + { + Modified(); + } +} + void mitk::LevelWindowManager::SetLevelWindowProperty(LevelWindowProperty::Pointer levelWindowProperty) { if (levelWindowProperty.IsNull()) { return; } // find data node that belongs to the property DataStorage::SetOfObjects::ConstPointer all = m_DataStorage->GetAll(); mitk::DataNode::Pointer propNode = nullptr; for (DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode::Pointer node = it.Value(); LevelWindowProperty::Pointer property = dynamic_cast(node->GetProperty("levelwindow")); if (property == levelWindowProperty) { propNode = node; } else { m_LevelWindowMutex = true; node->SetBoolProperty("imageForLevelWindow", false); m_LevelWindowMutex = false; } } if (propNode.IsNull()) { mitkThrow() << "No Image in the data storage that belongs to level-window property " << m_LevelWindowProperty; } if (m_IsPropertyModifiedTagSet) // remove listener for old property { m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); m_IsPropertyModifiedTagSet = false; } m_LevelWindowProperty = levelWindowProperty; itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); // register listener for new property command->SetCallbackFunction(this, &LevelWindowManager::OnPropertyModified); m_PropertyModifiedTag = m_LevelWindowProperty->AddObserver(itk::ModifiedEvent(), command); m_IsPropertyModifiedTagSet = true; m_CurrentImage = dynamic_cast(propNode->GetData()); m_LevelWindowMutex = true; propNode->SetBoolProperty("imageForLevelWindow", true); m_LevelWindowMutex = false; this->Modified(); } // returns the current mitkLevelWindowProperty object from the image that is affected by changes mitk::LevelWindowProperty::Pointer mitk::LevelWindowManager::GetLevelWindowProperty() { return m_LevelWindowProperty; } // returns Level/Window values for the current image const mitk::LevelWindow &mitk::LevelWindowManager::GetLevelWindow() { if (m_LevelWindowProperty.IsNotNull()) { return m_LevelWindowProperty->GetLevelWindow(); } else { itkExceptionMacro("No LevelWindow available!"); } } // sets new Level/Window values and informs all listeners about changes void mitk::LevelWindowManager::SetLevelWindow(const LevelWindow &levelWindow) { if (m_LevelWindowProperty.IsNotNull()) { m_LevelWindowProperty->SetLevelWindow(levelWindow); } this->Modified(); } void mitk::LevelWindowManager::DataStorageAddedNode(const DataNode *) { // update observers with new data storage UpdateObservers(); // Initialize LevelWindowsManager to new image SetAutoTopMostImage(true); // check if everything is still ok if ((m_ObserverToVisibleProperty.size() != m_ObserverToLayerProperty.size()) || (m_ObserverToLayerProperty.size() != this->GetRelevantNodes()->size())) { mitkThrow() << "Wrong number of observers in Level Window Manager!"; } } void mitk::LevelWindowManager::DataStorageRemovedNode(const DataNode *removedNode) { // first: check if deleted node is part of relevant nodes. If not, abort method because there is no need change // anything. if ((this->GetRelevantNodes()->size() == 0)) return; bool removedNodeIsRelevant = false; /* Iterator code: is crashing, don't know why... so using for loop for (DataStorage::SetOfObjects::ConstIterator it = this->GetRelevantNodes()->Begin(); it != this->GetRelevantNodes()->End(); ++it) {if (it->Value() == removedNode) {removedNodeIsRelevant=true;}}*/ for (unsigned int i = 0; i < this->GetRelevantNodes()->size(); i++) { if (this->GetRelevantNodes()->at(i) == removedNode) { removedNodeIsRelevant = true; } } if (!removedNodeIsRelevant) return; + } // remember node which will be removed m_NodeMarkedToDelete = removedNode; // update observers UpdateObservers(); // search image that belongs to the property if (m_LevelWindowProperty.IsNull()) { SetAutoTopMostImage(true, removedNode); } else { NodePredicateProperty::Pointer property = NodePredicateProperty::New("levelwindow", m_LevelWindowProperty); DataNode *n = m_DataStorage->GetNode(property); if (n == nullptr || m_AutoTopMost) // if node was deleted, change our behavior to AutoTopMost, if AutoTopMost is true // change level window to topmost node { SetAutoTopMostImage(true, removedNode); } } // reset variable m_NodeMarkedToDelete = nullptr; // check if everything is still ok if ((m_ObserverToVisibleProperty.size() != m_ObserverToLayerProperty.size()) || (m_ObserverToLayerProperty.size() != (this->GetRelevantNodes()->size() - 1))) { mitkThrow() << "Wrong number of observers in Level Window Manager!"; } } void mitk::LevelWindowManager::UpdateObservers() { this->ClearPropObserverLists(); // remove old observers CreatePropObserverLists(); // create new observer lists } int mitk::LevelWindowManager::GetNumberOfObservers() { return m_ObserverToVisibleProperty.size(); } mitk::DataStorage *mitk::LevelWindowManager::GetDataStorage() { return m_DataStorage.GetPointer(); } bool mitk::LevelWindowManager::IsAutoTopMost() { return m_AutoTopMost; } bool mitk::LevelWindowManager::IsSelectedImages() { return m_SelectedImages; } void mitk::LevelWindowManager::RecalculateLevelWindowForSelectedComponent(const itk::EventObject &event) { DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode::Pointer node = it->Value(); if (node.IsNull()) continue; bool isSelected = false; node->GetBoolProperty("selected", isSelected); if (isSelected) { LevelWindow selectedLevelWindow; node->GetLevelWindow(selectedLevelWindow); // node is an image node because of predicates auto *image = dynamic_cast(node->GetData()); int displayedComponent = 0; if (image && (node->GetIntProperty("Image.Displayed Component", displayedComponent))) { // we found a selected image with a displayed component // let's recalculate the levelwindow for this. selectedLevelWindow.SetAuto(image, true, true, static_cast(displayedComponent)); node->SetLevelWindow(selectedLevelWindow); } } LevelWindow levelWindow; node->GetLevelWindow(levelWindow); } this->Update(event); } void mitk::LevelWindowManager::Update(const itk::EventObject &) { if (m_LevelWindowMutex) // no mutex, should still help { return; } if (m_AutoTopMost) { SetAutoTopMostImage(true); return; } - else if (m_SelectedImages) + + if (m_SelectedImages) { SetSelectedImages(true); return; } int maxVisibleLayer = itk::NumericTraits::min(); DataNode::Pointer highestVisible = nullptr; std::vector visProbNodes; DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode::Pointer node = it->Value(); if (node.IsNull()) { continue; } bool visible = node->IsVisible(nullptr); if (visible) { int layer = -1; node->GetIntProperty("layer", layer); if (layer > maxVisibleLayer) { maxVisibleLayer = layer; highestVisible = node; } bool prop = false; node->GetBoolProperty("imageForLevelWindow", prop); if (prop) { visProbNodes.push_back(node); } } } int numVisProbNodes = visProbNodes.size(); if (numVisProbNodes > 2) { MITK_ERROR << "Error: not more than two visible nodes are expected to have the imageForLevelWindow property set at " "any point."; } else if (numVisProbNodes == 2) { for (std::vector::const_iterator it = visProbNodes.begin(); it != visProbNodes.end(); ++it) { LevelWindowProperty::Pointer newProp = dynamic_cast((*it)->GetProperty("levelwindow")); if (newProp != m_LevelWindowProperty) { this->SetLevelWindowProperty(newProp); return; } } } else if (numVisProbNodes == 1) { LevelWindowProperty::Pointer newProp = dynamic_cast(visProbNodes[0]->GetProperty("levelwindow")); if (newProp != m_LevelWindowProperty) { this->SetLevelWindowProperty(newProp); return; } } else if (highestVisible) { LevelWindowProperty::Pointer lvlProp = dynamic_cast(highestVisible->GetProperty("levelwindow")); this->SetLevelWindowProperty(lvlProp); } else { Modified(); } } +void mitk::LevelWindowManager::UpdateSelected(const itk::EventObject &) +{ + if (m_LevelWindowMutex) // no mutex, should still help + { + return; + } + + if (m_SelectedImages) + { + SetSelectedImages(true); + } +} + mitk::DataStorage::SetOfObjects::ConstPointer mitk::LevelWindowManager::GetRelevantNodes() { if (m_DataStorage.IsNull()) return DataStorage::SetOfObjects::ConstPointer(DataStorage::SetOfObjects::New()); // return empty set NodePredicateProperty::Pointer notBinary = NodePredicateProperty::New("binary", BoolProperty::New(false)); NodePredicateProperty::Pointer hasLevelWindow = NodePredicateProperty::New("levelwindow", nullptr); NodePredicateDataType::Pointer isImage = NodePredicateDataType::New("Image"); NodePredicateDataType::Pointer isDImage = NodePredicateDataType::New("DiffusionImage"); NodePredicateDataType::Pointer isTImage = NodePredicateDataType::New("TensorImage"); NodePredicateDataType::Pointer isOdfImage = NodePredicateDataType::New("OdfImage"); NodePredicateDataType::Pointer isShImage = NodePredicateDataType::New("ShImage"); NodePredicateOr::Pointer predicateTypes = NodePredicateOr::New(); predicateTypes->AddPredicate(isImage); predicateTypes->AddPredicate(isDImage); predicateTypes->AddPredicate(isTImage); predicateTypes->AddPredicate(isOdfImage); predicateTypes->AddPredicate(isShImage); NodePredicateAnd::Pointer predicate = NodePredicateAnd::New(); predicate->AddPredicate(notBinary); predicate->AddPredicate(hasLevelWindow); predicate->AddPredicate(predicateTypes); DataStorage::SetOfObjects::ConstPointer relevantNodes = m_DataStorage->GetSubset(predicate); return relevantNodes; } mitk::Image *mitk::LevelWindowManager::GetCurrentImage() { return m_CurrentImage; } void mitk::LevelWindowManager::ClearPropObserverLists() { for (auto iter = m_ObserverToVisibleProperty.begin(); iter != m_ObserverToVisibleProperty.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } m_ObserverToVisibleProperty.clear(); for (auto iter = m_ObserverToLayerProperty.begin(); iter != m_ObserverToLayerProperty.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } m_ObserverToLayerProperty.clear(); for (auto iter = m_ObserverToRenderingModeProperty.begin(); iter != m_ObserverToRenderingModeProperty.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } m_ObserverToRenderingModeProperty.clear(); for (auto iter = m_ObserverToDisplayedComponentProperty.begin(); iter != m_ObserverToDisplayedComponentProperty.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } m_ObserverToDisplayedComponentProperty.clear(); for (auto iter = m_ObserverToLevelWindowImageProperty.begin(); iter != m_ObserverToLevelWindowImageProperty.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } m_ObserverToLevelWindowImageProperty.clear(); + for (auto iter = m_ObserverToSelectedProperty.begin(); iter != m_ObserverToSelectedProperty.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } + m_ObserverToSelectedProperty.clear(); } void mitk::LevelWindowManager::CreatePropObserverLists() { if (m_DataStorage.IsNull()) // check if data storage is set { itkExceptionMacro("DataStorage not set"); } /* add observers for all relevant nodes */ DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { if ((it->Value().IsNull()) || (it->Value() == m_NodeMarkedToDelete)) { continue; } /* register listener for changes in visible property */ itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &LevelWindowManager::Update); unsigned long visIdx = it->Value()->GetProperty("visible")->AddObserver(itk::ModifiedEvent(), command); m_ObserverToVisibleProperty[PropDataPair(visIdx, it->Value())] = it->Value()->GetProperty("visible"); /* register listener for changes in layer property */ itk::ReceptorMemberCommand::Pointer command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction(this, &LevelWindowManager::Update); unsigned long layerIdx = it->Value()->GetProperty("layer")->AddObserver(itk::ModifiedEvent(), command2); m_ObserverToLayerProperty[PropDataPair(layerIdx, it->Value())] = it->Value()->GetProperty("layer"); /* register listener for changes in layer property */ itk::ReceptorMemberCommand::Pointer command3 = itk::ReceptorMemberCommand::New(); command3->SetCallbackFunction(this, &LevelWindowManager::Update); BaseProperty::Pointer imageRenderingMode = it->Value()->GetProperty("Image Rendering.Mode"); if (imageRenderingMode.IsNotNull()) { unsigned long rendIdx = imageRenderingMode->AddObserver(itk::ModifiedEvent(), command3); m_ObserverToRenderingModeProperty[PropDataPair(rendIdx, it->Value())] = imageRenderingMode.GetPointer(); } itk::ReceptorMemberCommand::Pointer command4 = itk::ReceptorMemberCommand::New(); command4->SetCallbackFunction(this, &LevelWindowManager::RecalculateLevelWindowForSelectedComponent); BaseProperty::Pointer displayedImageComponent = it->Value()->GetProperty("Image.Displayed Component"); if (displayedImageComponent.IsNotNull()) { unsigned long dispIdx = displayedImageComponent->AddObserver(itk::ModifiedEvent(), command4); m_ObserverToDisplayedComponentProperty[PropDataPair(dispIdx, it->Value())] = displayedImageComponent.GetPointer(); } itk::ReceptorMemberCommand::Pointer command5 = itk::ReceptorMemberCommand::New(); command5->SetCallbackFunction(this, &LevelWindowManager::Update); BaseProperty::Pointer imgForLvlWin = it->Value()->GetProperty("imageForLevelWindow"); if (imgForLvlWin.IsNull()) { it->Value()->SetBoolProperty("imageForLevelWindow", false); imgForLvlWin = it->Value()->GetProperty("imageForLevelWindow"); } unsigned long lvlWinIdx = imgForLvlWin->AddObserver(itk::ModifiedEvent(), command5); m_ObserverToLevelWindowImageProperty[PropDataPair(lvlWinIdx, it->Value())] = it->Value()->GetProperty("imageForLevelWindow"); + itk::ReceptorMemberCommand::Pointer command6 = + itk::ReceptorMemberCommand::New(); + command6->SetCallbackFunction(this, &LevelWindowManager::UpdateSelected); + BaseProperty::Pointer selectedDataNode = it->Value()->GetProperty("selected"); + if (selectedDataNode.IsNull()) + { + it->Value()->SetBoolProperty("selected", false); + selectedDataNode = it->Value()->GetProperty("selected"); + } + unsigned long selectedIdx = selectedDataNode->AddObserver(itk::ModifiedEvent(), command5); + m_ObserverToSelectedProperty[PropDataPair(selectedIdx, it->Value())] = it->Value()->GetProperty("selected"); } } diff --git a/Modules/QtWidgets/include/QmitkLevelWindowWidgetContextMenu.h b/Modules/QtWidgets/include/QmitkLevelWindowWidgetContextMenu.h index 8fae9be780..0b42568a4b 100644 --- a/Modules/QtWidgets/include/QmitkLevelWindowWidgetContextMenu.h +++ b/Modules/QtWidgets/include/QmitkLevelWindowWidgetContextMenu.h @@ -1,120 +1,121 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 QMITKLEVELWINDOWWIDGETCONTEXTMENU_H #define QMITKLEVELWINDOWWIDGETCONTEXTMENU_H #include #include #include #include /** * \ingroup QmitkModule * \brief Provides a contextmenu for Level/Window functionality. * * Either creates * a new contextmenu with standard functions or adds Level/Window standard * functions to an predefined contextmenu. */ class MITKQTWIDGETS_EXPORT QmitkLevelWindowWidgetContextMenu : public QWidget { Q_OBJECT public: /// constructor QmitkLevelWindowWidgetContextMenu(QWidget *parent, Qt::WindowFlags f = nullptr); ~QmitkLevelWindowWidgetContextMenu() override; /*! * data structure which reads and writes presets defined in a XML-file */ mitk::LevelWindowPreset *m_LevelWindowPreset; /*! * data structure which stores the values manipulated * by a QmitkLevelWindowWidgetContextMenu */ mitk::LevelWindow m_LevelWindow; /// submenu with all presets for contextmenu QMenu *m_PresetSubmenu; /// submenu with all images for contextmenu QMenu *m_ImageSubmenu; /// pointer to the object which manages all Level/Window changes on images and holds the LevelWindowProperty /// of the current image mitk::LevelWindowManager *m_Manager; /// map to hold all image-properties, one can get the image which is selected in the contextmenu /// with the QAction representing the image for the contextmenu std::map m_Images; /*! * returns the contextmenu with standard functions for Level/Window * * input is a prefilled contextmenu to which standard functions will be added */ void GetContextMenu(QMenu *contextMenu); /// returns the contextmenu with standard functions for Level/Window void GetContextMenu(); /// lets this object know about the LevelWindowManager to get all images and tell about changes void SetLevelWindowManager(mitk::LevelWindowManager *levelWindowManager); protected: QAction *m_PresetAction; QAction *m_AutoTopmostAction; + QAction *m_SelectedImagesAction; protected Q_SLOTS: /// sets level and window value of the current image to the values defined for the selected preset void OnSetPreset(const QAction *presetAction); /// calls the mitkLevelWindow SetAuto method with guessByCentralSlice false, so that the greyvalues from whole image /// will be considered void OnUseOptimizedLevelWindow(); /// calls the mitkLevelWindow SetToImageRange method, so that the greyvalues from whole image will be used void OnUseAllGreyvaluesFromImage(); /// sets the level window slider to be fixed void OnSetFixed(); /// adds a new Preset for presets-contextmenu void OnAddPreset(); /// resets the current images Level/Window to its default values void OnSetDefaultLevelWindow(); /// resets the current images scalerange to its default values void OnSetDefaultScaleRange(); /// changes the current images scalerange void OnChangeScaleRange(); /// sets the selected image or the topmost layer image to the new current image void OnSetImage(QAction *imageAction); /// sets the window to its maximum Size to fit the scalerange void OnSetMaximumWindow(); }; #endif // QMITKLEVELWINDOWWIDGETCONTEXTMENU_H diff --git a/Modules/QtWidgets/src/QmitkLevelWindowWidgetContextMenu.cpp b/Modules/QtWidgets/src/QmitkLevelWindowWidgetContextMenu.cpp index c02a327120..3676429edf 100644 --- a/Modules/QtWidgets/src/QmitkLevelWindowWidgetContextMenu.cpp +++ b/Modules/QtWidgets/src/QmitkLevelWindowWidgetContextMenu.cpp @@ -1,286 +1,305 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 // mitk core #include // mitk qt widgets #include "QmitkLevelWindowPresetDefinitionDialog.h" #include "QmitkLevelWindowRangeChangeDialog.h" // qt #include QmitkLevelWindowWidgetContextMenu::QmitkLevelWindowWidgetContextMenu(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f) { m_LevelWindowPreset = mitk::LevelWindowPreset::New(); m_LevelWindowPreset->LoadPreset(); } QmitkLevelWindowWidgetContextMenu::~QmitkLevelWindowWidgetContextMenu() { m_LevelWindowPreset->Delete(); } void QmitkLevelWindowWidgetContextMenu::OnSetPreset(const QAction *presetAction) { QString item = presetAction->text(); if (!(presetAction == m_PresetAction)) { double dlevel = m_LevelWindowPreset->getLevel(item.toStdString()); double dwindow = m_LevelWindowPreset->getWindow(item.toStdString()); if ((dlevel + dwindow / 2) > m_LevelWindow.GetRangeMax()) { double lowerBound = (dlevel - dwindow / 2); if (!(lowerBound > m_LevelWindow.GetRangeMax())) { dwindow = m_LevelWindow.GetRangeMax() - lowerBound; dlevel = lowerBound + dwindow / 2; } else { dlevel = m_LevelWindow.GetRangeMax() - 1; dwindow = 2; } } else if ((dlevel - dwindow / 2) < m_LevelWindow.GetRangeMin()) { double upperBound = (dlevel + dwindow / 2); if (!(upperBound < m_LevelWindow.GetRangeMin())) { dwindow = m_LevelWindow.GetRangeMin() + upperBound; dlevel = upperBound - dwindow / 2; } else { dlevel = m_LevelWindow.GetRangeMin() + 1; dwindow = 2; } } m_LevelWindow.SetLevelWindow(dlevel, dwindow); m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkLevelWindowWidgetContextMenu::SetLevelWindowManager(mitk::LevelWindowManager *levelWindowManager) { m_Manager = levelWindowManager; } void QmitkLevelWindowWidgetContextMenu::OnAddPreset() { QmitkLevelWindowPresetDefinitionDialog addPreset(this); addPreset.setPresets(m_LevelWindowPreset->getLevelPresets(), m_LevelWindowPreset->getWindowPresets(), QString::number((int)m_LevelWindow.GetLevel()), QString::number((int)m_LevelWindow.GetWindow())); if (addPreset.exec()) { m_LevelWindowPreset->newPresets(addPreset.getLevelPresets(), addPreset.getWindowPresets()); } } void QmitkLevelWindowWidgetContextMenu::OnSetFixed() { m_LevelWindow.SetFixed(!m_LevelWindow.GetFixed()); m_Manager->SetLevelWindow(m_LevelWindow); } void QmitkLevelWindowWidgetContextMenu::OnUseAllGreyvaluesFromImage() { m_LevelWindow.SetToImageRange(m_Manager->GetCurrentImage()); m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLevelWindowWidgetContextMenu::OnUseOptimizedLevelWindow() { m_LevelWindow.SetAuto(m_Manager->GetCurrentImage(), false, false); m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLevelWindowWidgetContextMenu::OnSetDefaultLevelWindow() { m_LevelWindow.ResetDefaultLevelWindow(); m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLevelWindowWidgetContextMenu::OnSetMaximumWindow() { m_LevelWindow.SetToMaxWindowSize(); m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLevelWindowWidgetContextMenu::OnSetDefaultScaleRange() { m_LevelWindow.ResetDefaultRangeMinMax(); m_LevelWindow.SetLevelWindow(m_LevelWindow.GetLevel(), m_LevelWindow.GetWindow()); m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLevelWindowWidgetContextMenu::OnChangeScaleRange() { QmitkLevelWindowRangeChangeDialog changeRange(this); changeRange.setLowerLimit((mitk::ScalarType)m_LevelWindow.GetRangeMin()); changeRange.setUpperLimit((mitk::ScalarType)m_LevelWindow.GetRangeMax()); if (changeRange.exec()) { m_LevelWindow.SetRangeMinMax(changeRange.getLowerLimit(), changeRange.getUpperLimit()); m_LevelWindow.SetLevelWindow(m_LevelWindow.GetLevel(), m_LevelWindow.GetWindow()); m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkLevelWindowWidgetContextMenu::OnSetImage(QAction *imageAction) { if (imageAction == m_AutoTopmostAction) { if (m_Manager->IsAutoTopMost() == false) { m_Manager->SetAutoTopMostImage(true); } else { m_Manager->SetAutoTopMostImage(false); } } + else if(imageAction == m_SelectedImagesAction) + { + if (m_Manager->IsSelectedImages() == false) + { + m_Manager->SetSelectedImages(true); + } + else + { + m_Manager->SetSelectedImages(false); + } + } else { m_Manager->SetLevelWindowProperty(m_Images.at(imageAction)); } } void QmitkLevelWindowWidgetContextMenu::GetContextMenu(QMenu *contextMenu) { if (nullptr == contextMenu) { return; } try { m_LevelWindow = m_Manager->GetLevelWindow(); QAction *sliderFixed = contextMenu->addAction(tr("Set slider fixed"), this, &QmitkLevelWindowWidgetContextMenu::OnSetFixed); sliderFixed->setCheckable(true); sliderFixed->setChecked(m_LevelWindow.IsFixed()); contextMenu->addSeparator(); contextMenu->addAction(tr("Use whole image grey values"), this, &QmitkLevelWindowWidgetContextMenu::OnUseAllGreyvaluesFromImage); contextMenu->addAction(tr("Use optimized level-window"), this, &QmitkLevelWindowWidgetContextMenu::OnUseOptimizedLevelWindow); contextMenu->addSeparator(); contextMenu->addAction(tr("Set maximum window"), this, &QmitkLevelWindowWidgetContextMenu::OnSetMaximumWindow); contextMenu->addAction(tr("Default level-window"), this, &QmitkLevelWindowWidgetContextMenu::OnSetDefaultLevelWindow); contextMenu->addSeparator(); contextMenu->addAction(tr("Change scale range"), this, &QmitkLevelWindowWidgetContextMenu::OnChangeScaleRange); contextMenu->addAction(tr("Default scale range"), this, &QmitkLevelWindowWidgetContextMenu::OnSetDefaultScaleRange); contextMenu->addSeparator(); m_PresetSubmenu = new QMenu(this); m_PresetSubmenu->setTitle("Presets"); m_PresetAction = m_PresetSubmenu->addAction(tr("Preset definition"), this, &QmitkLevelWindowWidgetContextMenu::OnAddPreset); m_PresetSubmenu->addSeparator(); std::map preset = m_LevelWindowPreset->getLevelPresets(); for (auto iter = preset.begin(); iter != preset.end(); iter++) { QString item = ((*iter).first.c_str()); m_PresetSubmenu->addAction(item); } connect(m_PresetSubmenu, &QMenu::triggered, this, &QmitkLevelWindowWidgetContextMenu::OnSetPreset); contextMenu->addMenu(m_PresetSubmenu); contextMenu->addSeparator(); m_ImageSubmenu = new QMenu(this); m_ImageSubmenu->setTitle("Images"); // add action for "auto topmost image" action m_AutoTopmostAction = m_ImageSubmenu->addAction(tr("Set topmost image")); m_AutoTopmostAction->setCheckable(true); if (m_Manager->IsAutoTopMost()) { m_AutoTopmostAction->setChecked(true); } + // add action for "selected images" action + m_ImageSubmenu->addSeparator(); + m_SelectedImagesAction = m_ImageSubmenu->addAction(tr("Use selected images")); + m_SelectedImagesAction->setCheckable(true); + if (m_Manager->IsSelectedImages()) + { + m_SelectedImagesAction->setChecked(true); + } // add action for individual images m_ImageSubmenu->addSeparator(); mitk::DataStorage::SetOfObjects::ConstPointer allObjects = m_Manager->GetRelevantNodes(); for (mitk::DataStorage::SetOfObjects::ConstIterator objectIter = allObjects->Begin(); objectIter != allObjects->End(); ++objectIter) { mitk::DataNode *node = objectIter->Value(); if (nullptr == node) { continue; } bool isHelperObject = false; node->GetBoolProperty("helper object", isHelperObject); if (isHelperObject) { continue; } if (!node->IsVisible(nullptr)) { continue; } mitk::LevelWindowProperty::Pointer levelWindowProperty = dynamic_cast(node->GetProperty("levelwindow")); if (levelWindowProperty.IsNotNull()) { std::string name; node->GetName(name); QString item = name.c_str(); QAction *id = m_ImageSubmenu->addAction(item); id->setCheckable(true); m_Images[id] = levelWindowProperty; if (levelWindowProperty == m_Manager->GetLevelWindowProperty()) { id->setChecked(true); } } } connect(m_ImageSubmenu, &QMenu::triggered, this, &QmitkLevelWindowWidgetContextMenu::OnSetImage); contextMenu->addMenu(m_ImageSubmenu); contextMenu->exec(QCursor::pos()); } catch (...) { } } void QmitkLevelWindowWidgetContextMenu::GetContextMenu() { auto contextMenu = new QMenu(this); GetContextMenu(contextMenu); delete contextMenu; }