diff --git a/Modules/Annotation/files.cmake b/Modules/Annotation/files.cmake index c6072d8d30..7758a2a5a0 100644 --- a/Modules/Annotation/files.cmake +++ b/Modules/Annotation/files.cmake @@ -1,18 +1,21 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkManualPlacementAnnotationRenderer.cpp mitkColorBarAnnotation.cpp mitkLabelAnnotation3D.cpp mitkLogoAnnotation.cpp mitkLayoutAnnotationRenderer.cpp mitkScaleLegendAnnotation.cpp mitkTextAnnotation2D.cpp mitkTextAnnotation3D.cpp mitkVtkLogoRepresentation.cxx mitkVtkAnnotation.cpp mitkVtkAnnotation2D.cpp mitkVtkAnnotation3D.cpp mitkAnnotationFactory.cpp + mitkDynamicAnnotation.cpp + mitkDataProvider.cpp + mitkProviderRegistry.cpp ) diff --git a/Modules/Annotation/include/mitkAnnotationFactory.h b/Modules/Annotation/include/mitkAnnotationFactory.h index 389291069e..9559a9f217 100644 --- a/Modules/Annotation/include/mitkAnnotationFactory.h +++ b/Modules/Annotation/include/mitkAnnotationFactory.h @@ -1,105 +1,60 @@ /*=================================================================== 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 MITKANNOTATIONFACTORY_H #define MITKANNOTATIONFACTORY_H // mitk #include "MitkAnnotationExports.h" #include "mitkAnnotation.h" #include "mitkCommon.h" #include "mitkDynamicAnnotation.h" #include "mitkLayoutAnnotationRenderer.h" // boost #include // stl #include namespace mitk { - /* - * Responsibilities - * Read json configuration - * Create Annotations - * Listen to slice change events - * Observe (relevant) property modification and deletion - * Data container for dynamic annotations - */ class MITKANNOTATION_EXPORT AnnotationFactory : public itk::Object { public: - mitkClassMacroNoParent(AnnotationFactory); - static AnnotationFactory::Pointer GetServiceInstance(); + using AnnotationPtrVector = std::vector; - using ActionFunctionMap = std::unordered_map; - using EventMap = std::unordered_map; - using AnnotationVector = std::vector; - - void CreateAnnotationsFromJson(const std::string &fileName, - ActionFunctionMap actionFunctionMap = ActionFunctionMap(), - EventMap eventMap = EventMap()); - void SaveConfigurationToJsonFile(const std::string &fileName); - - void AddSingleAnnotation(DynamicAnnotation annotation); - Annotation::Pointer GetAnnotationByName(const std::string &name); - - void SetProvider(const std::string &providerJsonKey, itk::Object::Pointer provider); - void SetProviderForAllPropertyAnnotations(BaseData *provider); - - bool Update(); - - void Reset(); - - // for testing - std::vector GetManagedAnnotationIds(); - unsigned int GetAmountOfPendingAnnotations() const; + static void CreateAnnotationsFromConfiguration(AnnotationPtrVector &annotations); + static void RegisterAnnotations(const AnnotationPtrVector &annotations); private: - AnnotationFactory() = default; - // private listener functions - void ObserveSliceChangedEvents(BaseRenderer *renderer); - void OnSliceChanged(Object *caller, const itk::EventObject &event); - - // private helper functions - static std::vector JsonToAnnotationInfoVector(boost::property_tree::ptree const &root); - static void SetProviderForAnnotation(itk::Object::Pointer provider, DynamicAnnotation &annotation); - LayoutAnnotationRenderer::Alignment GetAlignment(const std::string &alignmentName) const; - bool RegisterAnnotation(DynamicAnnotation dynamicAnnotation); - - // private data members - AnnotationVector m_Annotations; - AnnotationVector m_WaitingAnnotations; - std::unordered_map m_SliceNavigationControllerTags; - unsigned m_Slice = 0; - unsigned m_TimeStep = 0; + static LayoutAnnotationRenderer::Alignment GetAlignment(const std::string &alignmentName); const std::unordered_map m_Defaults = { {AnnotationConstants::NAME, "Unnamed Annotation"}, {AnnotationConstants::DEFAULT, "NA"}, {AnnotationConstants::PREFIX, ""}, {AnnotationConstants::SUFFIX, ""}, {AnnotationConstants::TYPE, "text"}, {AnnotationConstants::PROVIDER, ""}, {AnnotationConstants::EVENT, ""}, {AnnotationConstants::ACTION, ""}, {AnnotationConstants::WINDOW, "stdmulti.widget1"}, {AnnotationConstants::LAYOUT, "TopLeft"}, }; }; } // namespace mitk #endif // MITKANNOTATIONFACTORY_H diff --git a/Modules/Annotation/include/mitkAnnotationJsonReaderWriter.h b/Modules/Annotation/include/mitkAnnotationJsonReaderWriter.h new file mode 100644 index 0000000000..92ebb4bdfd --- /dev/null +++ b/Modules/Annotation/include/mitkAnnotationJsonReaderWriter.h @@ -0,0 +1,119 @@ +/*=================================================================== + + 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 MITKANNOTATIONJSONREADERWRITER_H +#define MITKANNOTATIONJSONREADERWRITER_H + +#include "mitkDynamicAnnotation.h" + +#include + +#include +#include + +namespace mitk +{ + // TODO simplify ReaderWriter to only take and produce configurations instead of full objects + class AnnotationJsonReaderWriter + { + public: + static std::vector ReadAnnotationsFromJsonFile(const std::string &fileName); + static void WriteAnnotationsToJsonFile(const std::string &fileName, + std::vector annotations); + + private: + static void AddAnnotationsToRootJsonElement(boost::property_tree::ptree &tree, + const std::vector &annotations); + }; + + inline std::vector AnnotationJsonReaderWriter::ReadAnnotationsFromJsonFile( + const std::string &fileName) + { + boost::property_tree::ptree root; + read_json(fileName, root); + + std::vector annotations; + + auto elements = root.begin(); + if (elements->first == "annotations") + { + for (const auto &window : elements->second) + { + for (const auto &layout : window.second) + { + for (const auto &object : layout.second) + { + auto config = std::unordered_map{}; + config[AnnotationConstants::WINDOW] = window.first; + config[AnnotationConstants::LAYOUT] = layout.first; + for (const auto &property : object.second) + { + config[property.first] = property.second.get_value(); + } + if (config[AnnotationConstants::DEFAULT].empty()) + { + config[AnnotationConstants::DEFAULT] = config[AnnotationConstants::NAME]; + } + annotations.push_back(DynamicAnnotation::New(config)); + } + } + } + } + return annotations; + } + + inline void AnnotationJsonReaderWriter::WriteAnnotationsToJsonFile( + const std::string &fileName, std::vector annotations) + { + using boost::property_tree::ptree; + ptree root; + AddAnnotationsToRootJsonElement(root, annotations); + + ofstream out; + out.open(fileName); + if (out.is_open()) + { + boost::property_tree::write_json(out, root); + } + else + { + MITK_ERROR << "Error opening file"; + } + } + + inline void AnnotationJsonReaderWriter::AddAnnotationsToRootJsonElement( + boost::property_tree::ptree &root, const std::vector &annotations) + { + using boost::property_tree::ptree; + for (const auto &annotation : annotations) + { + const auto &window = annotation->ConfigEntry(AnnotationConstants::WINDOW); + const auto &layout = annotation->ConfigEntry(AnnotationConstants::LAYOUT); + const ptree::path_type path{"annotations:" + window + ":" + layout, ':'}; + ptree object; + for (const auto &pair : annotation->GetConfiguration()) + { + if (pair.first == AnnotationConstants::WINDOW || pair.first == AnnotationConstants::LAYOUT) + continue; + object.put(pair.first, pair.second); + } + ptree layoutNode = root.get_child_optional(path).has_value() ? root.get_child(path) : ptree{}; + layoutNode.push_back(std::make_pair("", object)); + root.put_child(path, layoutNode); + } + } +} // namespace mitk +#endif // MITKANNOTATIONJSONREADERWRITER_H diff --git a/Modules/Annotation/include/mitkDataProvider.h b/Modules/Annotation/include/mitkDataProvider.h new file mode 100644 index 0000000000..4342f09840 --- /dev/null +++ b/Modules/Annotation/include/mitkDataProvider.h @@ -0,0 +1,92 @@ +/*=================================================================== + + 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 MITKDATAPROVIDER_H +#define MITKDATAPROVIDER_H + +#include "MitkAnnotationExports.h" +#include "mitkAnnotation.h" +#include +#include +#include +#include +#include +#include + +namespace mitk +{ + class MITKANNOTATION_EXPORT DataProvider : public itk::Object + { + using ReturnType = boost::any; + using AdditionalInfoMap = std::unordered_map; + + public: + mitkClassMacroItkParent(DataProvider, itk::Object); + using OnEventAction = std::function; + using DependentOnEventAction = + std::function dependentValues)>; + + mitkNewMacro4Param(DataProvider, std::string, itk::Object::Pointer, const itk::EventObject *, OnEventAction); + mitkNewMacro5Param(DataProvider, + std::string, + itk::Object::Pointer, + const itk::EventObject *, + DependentOnEventAction, + std::vector); + mitkNewMacro6Param(DataProvider, + std::string, + itk::Object::Pointer, + const itk::EventObject *, + DependentOnEventAction, + std::vector, + AdditionalInfoMap); + + void SetProviderObject(itk::Object::Pointer providerObject); + std::string GetName() const; + ReturnType GetValue() const; + + private: + DataProvider(std::string name, + itk::Object::Pointer object, + const itk::EventObject *event, + OnEventAction action, + AdditionalInfoMap info = {{}}); + DataProvider(std::string name, + Object::Pointer object, + const itk::EventObject *event, + DependentOnEventAction action, + std::vector dependencies, + AdditionalInfoMap info = {{}}); + void OnEventTriggeredConstCaller(const itk::Object *caller, const itk::EventObject &event); + void OnEventTriggered(itk::Object *caller, const itk::EventObject &event); + void OnDependencyModified(const itk::Object *caller, const itk::EventObject &event); + void FetchAndObserveDependencies(); + + std::string m_Name; + ReturnType m_Result; + itk::Object::Pointer m_Object; + const itk::EventObject *m_DataChangedEvent; + DependentOnEventAction m_DataRetrievalAction; + std::unordered_map m_DependencyResults; + std::unordered_set m_UnknownDependencies; + std::map m_DependencyTags; + unsigned int m_ProviderTag; + }; +} // namespace mitk + +#endif // MITKDATAPROVIDER_H diff --git a/Modules/Annotation/include/mitkDynamicAnnotation.h b/Modules/Annotation/include/mitkDynamicAnnotation.h index 4e897ed02f..bc850bebfe 100644 --- a/Modules/Annotation/include/mitkDynamicAnnotation.h +++ b/Modules/Annotation/include/mitkDynamicAnnotation.h @@ -1,178 +1,71 @@ /*=================================================================== 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 MITKDYNAMICANNOTATION_H #define MITKDYNAMICANNOTATION_H #include "mitkAnnotation.h" -#include "mitkLogMacros.h" +#include "mitkDataProvider.h" + #include -#include -#include #include -#include - -// namespace mitk -//{ -// class DataProvider : public itk::Object -// { -// public: -// using OnEventAction = std::function; -// mitkClassMacroItkParent(DataProvider, itk::Object); -// mitkNewMacro4Param(DataProvider, std::string, WeakPointer, itk::EventObject *, OnEventAction); -// -// std::string Fetch() { return m_Result; }; -// template -// std::string Fetch(Args...); -// -// protected: -// DataProvider(std::string name, WeakPointer provider, itk::EventObject *trigger, OnEventAction -// action); -// -// std::string m_Name; -// WeakPointer m_Provider; -// const itk::EventObject *m_Trigger; -// OnEventAction m_Action; -// std::string m_Result; -// -// std::vector tags; -// }; -// class SliceProvider : public DataProvider -// { -// SliceProvider(WeakPointer sliceNavigationController) -// : DataProvider("slice", -// sliceNavigationController.Lock().GetPointer(), -// SliceNavigationController::GeometrySliceEvent(nullptr, 0).MakeObject(), -// [](const itk::Object *caller, const itk::EventObject &event) { -// auto sliceEvent = dynamic_cast(&event); -// if (sliceEvent) -// { -// return std::to_string(sliceEvent->GetPos()); -// } -// return std::string{"Slice Error"}; -// }){}; -// }; -// class TimeProvider : public DataProvider -// { -// TimeProvider(WeakPointer sliceNavigationController) -// : DataProvider("timeStep", -// sliceNavigationController.Lock().GetPointer(), -// SliceNavigationController::GeometryTimeEvent(nullptr, 0).MakeObject(), -// [](const itk::Object *caller, const itk::EventObject &event) { -// auto timeEvent = dynamic_cast(&event); -// if (timeEvent) -// { -// return std::to_string(timeEvent->GetPos()); -// } -// return std::string{"Time Error"}; -// }){}; -// }; -// template -// class DependantProvider : public DataProvider -// { -// DependantProvider(); -// -// void SetProviders(Providers... providers) { m_Providers = std::tuple{providers...}; } -// -// virtual std::string Fetch(Providers... providers) = 0; -// -// std::tuple m_Providers; -// }; -// -// class DicomProvider : public DependantProvider -// { -// std::string Fetch(SliceProvider sliceProvider, TimeProvider timeProvider) override -// { -// -// }; -// }; -// -// // SetText("Nr. " + DicomProvider.fetch(SliceProvider.fetch(), TimeProvider.fetch(), -// // BaseData->GetProp("DICOM.0010.0010")); -// -// class DynamicAnnotation : public itk::Object -// { -// using Config = std::unordered_map; -// using ProviderCollection = std::vector; -// using TextSetter = std::function)>; -// -// public: -// mitkClassMacroItkParent(DynamicAnnotation, itk::Object); -// mitkNewMacro2Param(DynamicAnnotation, Annotation::Pointer, Config); -// -// void AddProvider(DataProvider::Pointer provider); -// void SetTextAssemblyFunction(TextSetter function); -// -// private: -// DynamicAnnotation(Annotation::Pointer annotation, std::unordered_map config); -// -// mitk::Annotation::Pointer m_Annotation; -// Config m_Config; -// ProviderCollection m_Providers; -// }; -//} // namespace mitk namespace mitk { namespace AnnotationConstants { const std::string NAME = "name"; const std::string DEFAULT = "defaultValue"; const std::string PREFIX = "prefix"; const std::string SUFFIX = "suffix"; const std::string LAYOUT = "layout"; const std::string TYPE = "type"; const std::string PROVIDER = "provider"; const std::string EVENT = "event"; const std::string ACTION = "action"; const std::string WINDOW = "window"; const std::string SLICENAVIGATIONCONTROLLER = "SliceNavigationController"; const std::string PATH = "path"; } // namespace AnnotationConstants - struct DynamicAnnotation + class MITKANNOTATION_EXPORT DynamicAnnotation : public itk::Object { - using OnEventAction = std::function; - using OnSliceChanged = std::function; - using OnTimeStepChanged = std::function; - DynamicAnnotation() : ptr(nullptr), config({}), providerInfo(DataProvider()) - { - MITK_INFO << "DynamicAnnotation constructor"; - }; - mitk::Annotation::Pointer ptr; - std::unordered_map config; - struct DataProvider - { - DataProvider() : object(nullptr), dataChangedEvent(nullptr) { MITK_INFO << "DataProvider constructor"; }; - itk::Object::Pointer object; - const itk::EventObject *dataChangedEvent; + public: + mitkClassMacroItkParent(DynamicAnnotation, itk::Object); + using ConfigMap = std::unordered_map; + mitkNewMacro1Param(DynamicAnnotation, ConfigMap); + + + void SetDataProvider(DataProvider::Pointer provider); + void SetDataProvider(std::string provider); + void SetManagedAnnotation(Annotation::Pointer annotation); + Annotation::Pointer GetManagedAnnotation(); + void SetText(const std::string &text); + std::string GetText(); + std::string ConfigEntry(const std::string& key); + ConfigMap GetConfiguration(); - OnEventAction dataRetrievalAction; - OnSliceChanged sliceChanged; - OnTimeStepChanged timeStepChanged; + private: + DynamicAnnotation(ConfigMap config, Annotation::Pointer annotation = nullptr); + void OnProviderModified(); - std::vector tags; - } providerInfo; - void SetText(const std::string &text) - { - const auto &prefix = this->config[AnnotationConstants::PREFIX]; - const auto &suffix = this->config[AnnotationConstants::SUFFIX]; - this->ptr->SetText(prefix + text + suffix); - } + mitk::Annotation::Pointer m_Ptr; + ConfigMap m_Config; + DataProvider::Pointer m_DataProvider; + unsigned long m_ProviderTag; }; } // namespace mitk #endif // MITKDYNAMICANNOTATION_H diff --git a/Modules/Annotation/include/mitkProviderRegistry.h b/Modules/Annotation/include/mitkProviderRegistry.h new file mode 100644 index 0000000000..be974e2b81 --- /dev/null +++ b/Modules/Annotation/include/mitkProviderRegistry.h @@ -0,0 +1,23 @@ +#pragma once +#include "mitkCommon.h" +#include "mitkDataProvider.h" +#include + +namespace mitk +{ + class MITKANNOTATION_EXPORT ProviderRegistry : public itk::Object + { + public: + mitkClassMacroItkParent(ProviderRegistry, itk::Object); + + static Pointer GetInstance(); + + void AddDataProvider(DataProvider::Pointer provider); + void AddDataProviders(std::set providers); + std::unordered_set FetchDataProviders(std::unordered_set names); + + private: + itkFactorylessNewMacro(ProviderRegistry); + std::unordered_map m_Providers; + }; +} // namespace mitk diff --git a/Modules/Annotation/src/mitkAnnotationFactory.cpp b/Modules/Annotation/src/mitkAnnotationFactory.cpp index 2ff425017d..2ffb47c361 100644 --- a/Modules/Annotation/src/mitkAnnotationFactory.cpp +++ b/Modules/Annotation/src/mitkAnnotationFactory.cpp @@ -1,452 +1,177 @@ /*=================================================================== 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 "mitkAnnotationFactory.h" -#include "mitkAnnotationUtils.h" #include "mitkColorBarAnnotation.h" -#include "mitkDynamicAnnotation.h" #include "mitkLayoutAnnotationRenderer.h" #include "mitkLogoAnnotation.h" #include "mitkScaleLegendAnnotation.h" #include "mitkTemporoSpatialStringProperty.h" #include "mitkTextAnnotation2D.h" -#include -#include - -class PropertyDeletedCommand : public itk::Command -{ -public: - void Execute(Object *caller, const itk::EventObject &event) override - { - Execute(const_cast(caller), event); - }; - void Execute(const Object *caller, const itk::EventObject &event) override - { - MITK_INFO << "Property " << caller->GetNameOfClass() << " deleted"; - }; -}; - -class ItkRetrievalCommand : public itk::Command -{ - mitk::DynamicAnnotation::OnEventAction m_OnEventAction; - std::string m_AnnotationId; - -public: - ItkRetrievalCommand(mitk::DynamicAnnotation::OnEventAction dataRetrievalAction, std::string annotationId) - : m_OnEventAction(std::move(dataRetrievalAction)), m_AnnotationId(std::move(annotationId)){}; - void Execute(Object *caller, const itk::EventObject &event) override - { - Execute(const_cast(caller), event); - }; - void Execute(const Object *caller, const itk::EventObject &event) override - { - auto annotation = mitk::AnnotationUtils::GetAnnotation(m_AnnotationId); - if (annotation) - { - // TODO remove hardcoded 0 slice and time - auto updatedText = m_OnEventAction(caller, event, 0, 0); - MITK_INFO << "Annotation " << annotation->GetName() << " set to " << updatedText; - annotation->SetText(updatedText); - } - }; -}; -std::vector mitk::AnnotationFactory::JsonToAnnotationInfoVector( - boost::property_tree::ptree const &root) -{ - std::vector elements; - auto annotations = root.begin(); - if (annotations->first == "annotations") - { - for (const auto &window : annotations->second) - { - for (const auto &layout : window.second) - { - for (const auto &object : layout.second) - { - DynamicAnnotation annotation; - annotation.config[AnnotationConstants::WINDOW] = window.first; - annotation.config[AnnotationConstants::LAYOUT] = layout.first; - for (const auto &property : object.second) - { - annotation.config[property.first] = property.second.get_value(); - } - if (annotation.config[AnnotationConstants::DEFAULT].empty()) - { - annotation.config[AnnotationConstants::DEFAULT] = annotation.config[AnnotationConstants::NAME]; - } - elements.push_back(annotation); - } - } - } - } - return elements; -} +#include -mitk::LayoutAnnotationRenderer::Alignment mitk::AnnotationFactory::GetAlignment(const std::string &alignmentName) const +mitk::LayoutAnnotationRenderer::Alignment mitk::AnnotationFactory::GetAlignment(const std::string &alignmentName) { static const auto alignmentMap = std::unordered_map{ {"TopLeft", LayoutAnnotationRenderer::TopLeft}, {"Top", LayoutAnnotationRenderer::Top}, {"TopRight", LayoutAnnotationRenderer::TopRight}, {"Left", LayoutAnnotationRenderer::Left}, {"Right", LayoutAnnotationRenderer::Right}, {"BottomLeft", LayoutAnnotationRenderer::BottomLeft}, {"Bottom", LayoutAnnotationRenderer::Bottom}, {"BottomRight", LayoutAnnotationRenderer::BottomRight}}; const auto alignment = alignmentMap.find(alignmentName); - const auto &defaultFallback = m_Defaults.at(AnnotationConstants::LAYOUT); + const auto &defaultFallback = "TopLeft"; return alignment != std::end(alignmentMap) ? alignment->second : alignmentMap.at(defaultFallback); } -void mitk::AnnotationFactory::ObserveSliceChangedEvents(BaseRenderer *renderer) +auto PropertyChangeAction() { - if (!renderer) - return; - - auto name = renderer->GetName(); - if (m_SliceNavigationControllerTags.find(name) == std::end(m_SliceNavigationControllerTags)) - { - auto sliceCommand = itk::MemberCommand::New(); - sliceCommand->SetCallbackFunction(this, &AnnotationFactory::OnSliceChanged); - - auto sliceController = renderer->GetSliceNavigationController(); - auto tag = sliceController->AddObserver(SliceNavigationController::GeometrySliceEvent(nullptr, 0), sliceCommand); - m_SliceNavigationControllerTags.insert({name, tag}); - } -} - -bool mitk::AnnotationFactory::RegisterAnnotation(DynamicAnnotation dynamicAnnotation) -{ - const auto alignment = GetAlignment(dynamicAnnotation.config[AnnotationConstants::LAYOUT]); - - dynamicAnnotation.ptr->SetName(dynamicAnnotation.config[AnnotationConstants::NAME]); - dynamicAnnotation.ptr->SetText(dynamicAnnotation.config[AnnotationConstants::DEFAULT]); - MITK_INFO << dynamicAnnotation.config[AnnotationConstants::NAME]; + return [](const itk::Object *caller, // needed to listen to modified events on the property itself + const itk::EventObject &event, + std::unordered_map dependencyValues) { + // stored as additional information on provider creation + auto propName = boost::any_cast(dependencyValues["PropName"]); - const auto renderer = BaseRenderer::GetByName(dynamicAnnotation.config[AnnotationConstants::WINDOW]); - if (renderer) - { - ObserveSliceChangedEvents(renderer); - LayoutAnnotationRenderer::AddAnnotation(dynamicAnnotation.ptr, renderer, alignment); - m_Annotations.push_back(dynamicAnnotation); - // TODO wait so each annotation gets a unique id see T25927 - std::this_thread::sleep_for(std::chrono::seconds(2)); - if (dynamicAnnotation.config[AnnotationConstants::PROVIDER] == AnnotationConstants::SLICENAVIGATIONCONTROLLER) + // stored as part of provider dependency + if (dependencyValues["DataNodeProvider"].type() == typeid(mitk::DataNode::Pointer)) { - SetProviderForAnnotation(renderer->GetSliceNavigationController(), dynamicAnnotation); + auto node = boost::any_cast(dependencyValues["DataNodeProvider"]); + auto prop = node->GetProperty(propName.c_str()); + if (prop) + { + // in case the data node changed update it on "yourself" + auto provider = boost::any_cast(dependencyValues["Self"]); + provider->SetProviderObject(prop); + + return prop->GetValueAsString(); + } } - return true; - } - return false; + return std::string{}; + }; } - -void mitk::AnnotationFactory::OnSliceChanged(Object *caller, const itk::EventObject &event) +auto TemporoSpatialPropertyChangeAction() { - const auto geometryEvent = dynamic_cast(&event); - if (!geometryEvent) - return; - - m_Slice = geometryEvent->GetPos(); + return [](const itk::Object *caller, // needed to listen to modified events on the property itself + const itk::EventObject &event, + std::unordered_map dependencyValues) { + // stored as additional information on provider creation + auto propName = boost::any_cast(dependencyValues["PropName"]); - const auto sliceNavController = dynamic_cast(caller); - if (!sliceNavController) - return; + // stored as part of provider dependency + if (dependencyValues["DataNodeProvider"].type() == typeid(mitk::DataNode::Pointer)) + { + auto node = boost::any_cast(dependencyValues["DataNodeProvider"]); + auto prop = dynamic_cast(node->GetProperty(propName.c_str())); + if (prop) + { + // in case the data node changed update it on "yourself" + auto provider = boost::any_cast(dependencyValues["Self"]); + provider->SetProviderObject(prop); - const auto renderer = sliceNavController->GetRenderer(); - if (!renderer) - return; + // get slice data from provider dependency + auto slice = 0; + if (dependencyValues.find("SliceNumberProviderAxial") != std::end(dependencyValues)) + { + if (dependencyValues["SliceNumberProviderAxial"].type() == typeid(unsigned int)) + { + slice = boost::any_cast(dependencyValues["SliceNumberProviderAxial"]); + } + } - const auto rendererName = renderer->GetName(); - for (auto &annotation : m_Annotations) - { - if (annotation.config[AnnotationConstants::TYPE] == "property" && - annotation.config[AnnotationConstants::WINDOW] == rendererName) - { - annotation.SetText(annotation.providerInfo.dataRetrievalAction( - annotation.providerInfo.object, *annotation.providerInfo.dataChangedEvent, m_Slice, m_TimeStep)); + return prop->GetValueBySlice(slice); + } } - } -} - -mitk::AnnotationFactory::Pointer mitk::AnnotationFactory::GetServiceInstance() -{ - auto ctx = us::GetModuleContext(); - static auto m_Registration = ctx->RegisterService(new AnnotationFactory()); - return ctx->GetService(ctx->GetServiceReference()); + return std::string{"No data node selected"}; + }; } -void mitk::AnnotationFactory::CreateAnnotationsFromJson(const std::string &fileName, - ActionFunctionMap actionFunctionMap, - EventMap eventMap) +void mitk::AnnotationFactory::CreateAnnotationsFromConfiguration(AnnotationPtrVector &annotations) { - boost::property_tree::ptree root; - read_json(fileName, root); - - auto annotations = JsonToAnnotationInfoVector(root); - - for (auto annotation : annotations) + for (auto &annotation : annotations) { - const auto &type = annotation.config[AnnotationConstants::TYPE]; + const auto &type = annotation->ConfigEntry(AnnotationConstants::TYPE); if (type == "text") { - annotation.ptr = mitk::TextAnnotation2D::New(); + annotation->SetManagedAnnotation(mitk::TextAnnotation2D::New().GetPointer()); } else if (type == "property") { - annotation.ptr = mitk::TextAnnotation2D::New(); - annotation.providerInfo.dataChangedEvent = itk::ModifiedEvent().MakeObject(); - const auto &defaultName = annotation.config[AnnotationConstants::DEFAULT]; - annotation.providerInfo.dataRetrievalAction = - [defaultName](const itk::Object *caller, const itk::EventObject &, unsigned int slice, unsigned int timeStep) { - const auto dicomProperty = dynamic_cast(caller); - if (dicomProperty) - { - return dicomProperty->GetValueBySlice(slice); - } - const auto property = dynamic_cast(caller); - if (property) - { - return property->GetValueAsString(); - } - return defaultName + " (NA)"; - }; + annotation->SetManagedAnnotation(mitk::TextAnnotation2D::New().GetPointer()); + + auto propertyProvider = DataProvider::New("PropertyProvider", + nullptr, + itk::ModifiedEvent().MakeObject(), + PropertyChangeAction(), + {"DataNodeProvider"}, + {{"PropName", annotation->ConfigEntry(AnnotationConstants::PROVIDER)}}); + + annotation->SetDataProvider(propertyProvider); } - else if (type == "custom") + else if (type == "dicomProperty") { - if (annotation.config[AnnotationConstants::EVENT].empty() || - annotation.config[AnnotationConstants::ACTION].empty()) - { - throw std::logic_error("A custom annotation needs to have an event and an action specified"); - } - annotation.ptr = TextAnnotation2D::New(); - annotation.providerInfo.dataChangedEvent = eventMap[annotation.config[AnnotationConstants::EVENT]]; - annotation.providerInfo.dataRetrievalAction = actionFunctionMap[annotation.config[AnnotationConstants::ACTION]]; + annotation->SetManagedAnnotation(mitk::TextAnnotation2D::New().GetPointer()); + + auto propertyProvider = DataProvider::New("PropertyProvider", + nullptr, + itk::ModifiedEvent().MakeObject(), + TemporoSpatialPropertyChangeAction(), + {"DataNodeProvider", "SliceNumberProviderAxial", "TimeStepProvider"}, + {{"PropName", annotation->ConfigEntry(AnnotationConstants::PROVIDER)}}); + + annotation->SetDataProvider(propertyProvider); } else if (type == "colorBar") { - annotation.ptr = ColorBarAnnotation::New(); + annotation->SetManagedAnnotation(ColorBarAnnotation::New().GetPointer()); } else if (type == "scaleLegend") { - annotation.ptr = ScaleLegendAnnotation::New(); + annotation->SetManagedAnnotation(ScaleLegendAnnotation::New().GetPointer()); } else if (type == "logo") { auto logoAnnotation = LogoAnnotation::New(); - auto path = annotation.config[AnnotationConstants::PATH]; + auto path = annotation->ConfigEntry(AnnotationConstants::PATH); logoAnnotation->SetLogoImagePath(path); logoAnnotation->LoadLogoImageFromPath(); - annotation.ptr = logoAnnotation; + annotation->SetManagedAnnotation(logoAnnotation.GetPointer()); } else { throw std::logic_error("Unknown or missing annotation type"); } - if (!RegisterAnnotation(annotation)) - { - // Registration of annotation failed probably due to missing base renderer - // Store annotation for delayed registration - m_WaitingAnnotations.push_back(annotation); - } } } -void mitk::AnnotationFactory::SetProviderForAnnotation(itk::Object::Pointer provider, DynamicAnnotation &annotation) +void mitk::AnnotationFactory::RegisterAnnotations(const AnnotationPtrVector &annotations) { - if (provider.IsNotNull() && provider != annotation.providerInfo.object) + for (const auto &annotation : annotations) { - if (annotation.providerInfo.object) - { - for (const auto &tag : annotation.providerInfo.tags) - { - annotation.providerInfo.object->RemoveObserver(tag); - } - } - - annotation.providerInfo.object = provider; - - if (annotation.providerInfo.dataChangedEvent) - { - annotation.providerInfo.tags.push_back(annotation.providerInfo.object->AddObserver( - *annotation.providerInfo.dataChangedEvent, - new ItkRetrievalCommand(annotation.providerInfo.dataRetrievalAction, annotation.ptr->GetMicroserviceID()))); - - annotation.SetText(annotation.providerInfo.dataRetrievalAction( - annotation.providerInfo.object, *annotation.providerInfo.dataChangedEvent, 0, 0)); - } - if (annotation.config[AnnotationConstants::TYPE] == "property") - { - annotation.providerInfo.tags.push_back( - annotation.providerInfo.object->AddObserver(itk::DeleteEvent(), new PropertyDeletedCommand())); - } - } -} + const auto alignment = GetAlignment(annotation->ConfigEntry(AnnotationConstants::LAYOUT)); -void mitk::AnnotationFactory::AddSingleAnnotation(DynamicAnnotation annotation) -{ - if (annotation.ptr) - { - try - { - const auto id = annotation.ptr->GetMicroserviceID(); - if (AnnotationUtils::GetAnnotation(id)) - { - // The annotation has already been fully set up so just add it to the managed annotations - m_Annotations.push_back(annotation); - } - else - { - MITK_ERROR << "An annotation with a valid ID can't be fetched through the microservice registry. Not adding it " - "to the managed annotations"; - } - } - catch (const std::logic_error &e) + const auto renderer = BaseRenderer::GetByName(annotation->ConfigEntry(AnnotationConstants::WINDOW)); + if (renderer) { - MITK_ERROR << e.what() - << "\nWhen adding single annotations they should already be registered through a layout renderer. " - "Trying this for you now."; - // The annotation probably hasn't been registered yet so try it - if (!RegisterAnnotation(annotation)) - { - m_WaitingAnnotations.push_back(annotation); - } + LayoutAnnotationRenderer::AddAnnotation(annotation->GetManagedAnnotation(), renderer, alignment); + // TODO wait so each annotation gets a unique id see T25927 + std::this_thread::sleep_for(std::chrono::seconds(2)); } } } - -mitk::Annotation::Pointer mitk::AnnotationFactory::GetAnnotationByName(const std::string &name) -{ - for (const auto &annotation : m_Annotations) - { - if (annotation.ptr->GetName() == name) - { - return annotation.ptr; - } - } - return nullptr; -} - -void mitk::AnnotationFactory::SetProvider(const std::string &providerJsonKey, itk::Object::Pointer provider) -{ - for (auto &annotation : m_Annotations) - { - if (providerJsonKey == annotation.config[AnnotationConstants::PROVIDER]) - { - SetProviderForAnnotation(provider, annotation); - } - } -} - -void mitk::AnnotationFactory::SetProviderForAllPropertyAnnotations(BaseData *provider) -{ - Update(); // TODO temporary fix for delaying the registration of annotations until the render windows are present - - for (auto &annotation : m_Annotations) - { - if (annotation.config[AnnotationConstants::TYPE] != "property") - continue; - - // if provider has property - auto propertyName = annotation.config[AnnotationConstants::PROVIDER]; - auto property = provider->GetProperty(propertyName.c_str()); - if (property) - { - // set provider (in case the property exists but is empty state this in the annotations text - SetProviderForAnnotation(property.GetPointer(), annotation); - annotation.ptr->SetColor(0, 200, 0); - if (annotation.ptr->GetText().empty()) - { - annotation.ptr->SetColor(0, 0, 200); - annotation.SetText(annotation.config[AnnotationConstants::DEFAULT] + " (empty)"); - } - } - else - { - annotation.ptr->SetColor(200, 0, 0); - annotation.SetText(annotation.config[AnnotationConstants::DEFAULT] + " (NA)"); - } - } -} - -std::vector mitk::AnnotationFactory::GetManagedAnnotationIds() -{ - auto ids = std::vector{}; - for (const auto &annotation : m_Annotations) - { - ids.push_back(annotation.ptr->GetMicroserviceID()); - } - return ids; -} - -unsigned mitk::AnnotationFactory::GetAmountOfPendingAnnotations() const -{ - return m_WaitingAnnotations.size(); -} - -void mitk::AnnotationFactory::SaveConfigurationToJsonFile(const std::string &fileName) -{ - using boost::property_tree::ptree; - ptree root; - const auto createNodes = [&root](DynamicAnnotation &annotation) { - const auto &window = annotation.config[AnnotationConstants::WINDOW]; - const auto &layout = annotation.config[AnnotationConstants::LAYOUT]; - const ptree::path_type path{"annotations:" + window + ":" + layout, ':'}; - ptree object; - for (const auto &pair : annotation.config) - { - if (pair.first == AnnotationConstants::WINDOW || pair.first == AnnotationConstants::LAYOUT) - continue; - object.put(pair.first, pair.second); - } - - ptree layoutNode = root.get_child_optional(path).has_value() ? root.get_child(path) : ptree{}; - layoutNode.push_back(std::make_pair("", object)); - root.put_child(path, layoutNode); - }; - - std::for_each(std::begin(m_Annotations), std::end(m_Annotations), createNodes); - std::for_each(std::begin(m_WaitingAnnotations), std::end(m_WaitingAnnotations), createNodes); - - ofstream out; - out.open(fileName); - if (out.is_open()) - { - boost::property_tree::write_json(out, root); - } - else - { - MITK_ERROR << "Error opening file"; - } -} - -bool mitk::AnnotationFactory::Update() -{ - const auto firstElementToErase = std::remove_if(m_WaitingAnnotations.begin(), - m_WaitingAnnotations.end(), - [this](DynamicAnnotation da) { return RegisterAnnotation(da); }); - - m_WaitingAnnotations.erase(firstElementToErase, m_WaitingAnnotations.end()); - - return m_WaitingAnnotations.empty(); -} - -void mitk::AnnotationFactory::Reset() -{ - m_Annotations.clear(); - m_WaitingAnnotations.clear(); -} diff --git a/Modules/Annotation/src/mitkDataProvider.cpp b/Modules/Annotation/src/mitkDataProvider.cpp new file mode 100644 index 0000000000..1479e35380 --- /dev/null +++ b/Modules/Annotation/src/mitkDataProvider.cpp @@ -0,0 +1,125 @@ +/*=================================================================== + + 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 "mitkDataProvider.h" +#include "mitkProviderRegistry.h" + +mitk::DataProvider::DataProvider(std::string name, + itk::Object::Pointer object, + const itk::EventObject *event, + OnEventAction action, + AdditionalInfoMap info) + : DataProvider(name, + object, + event, + [action](const auto *caller, const auto &event, auto noDependencies) { return action(caller, event); }, + {}, + info) +{ +} + +mitk::DataProvider::DataProvider(std::string name, + Object::Pointer object, + const itk::EventObject *event, + DependentOnEventAction action, + std::vector dependencies, + AdditionalInfoMap info) + : m_Name(std::move(name)), m_DataChangedEvent(event), m_DataRetrievalAction(action) +{ + m_UnknownDependencies.insert(std::begin(dependencies), std::end(dependencies)); + m_DependencyResults.insert(std::begin(info), std::end(info)); + + m_DependencyResults["Self"] = this; + + FetchAndObserveDependencies(); + + SetProviderObject(object); +} + +void mitk::DataProvider::SetProviderObject(itk::Object::Pointer providerObject) +{ + if (m_Object == providerObject) + return; + + if (m_Object.IsNotNull()) + { + m_Object->RemoveObserver(m_ProviderTag); + m_ProviderTag = -1; + } + + m_Object = providerObject; + if (m_Object.IsNull()) + return; + + auto itkAction = itk::MemberCommand::New(); + itkAction->SetCallbackFunction(this, &DataProvider::OnEventTriggered); + m_ProviderTag = m_Object->AddObserver(*m_DataChangedEvent, itkAction); + + auto itkActionConst = itk::MemberCommand::New(); + itkActionConst->SetCallbackFunction(this, &DataProvider::OnEventTriggeredConstCaller); + //TODO don't override provider tag + m_ProviderTag = m_Object->AddObserver(*m_DataChangedEvent, itkActionConst); +} + +std::string mitk::DataProvider::GetName() const +{ + return m_Name; +} + +boost::any mitk::DataProvider::GetValue() const +{ + return m_Result; +} + +void mitk::DataProvider::OnEventTriggeredConstCaller(const itk::Object *caller, const itk::EventObject &event) +{ + FetchAndObserveDependencies(); + + m_Result = m_DataRetrievalAction(caller, event, m_DependencyResults); + + this->Modified(); +} + +void mitk::DataProvider::OnEventTriggered(itk::Object* caller, const itk::EventObject& event) +{ + this->OnEventTriggeredConstCaller(const_cast(caller), event); +} + +void mitk::DataProvider::OnDependencyModified(const itk::Object *caller, const itk::EventObject &event) +{ + auto provider = dynamic_cast(caller); + if (provider) + { + m_DependencyResults[provider->GetName()] = provider->GetValue(); + OnEventTriggered(m_Object,* m_DataChangedEvent); + } +} + +void mitk::DataProvider::FetchAndObserveDependencies() +{ + if (m_UnknownDependencies.empty()) + return; + + auto registry = ProviderRegistry::GetInstance(); + auto knownProviders = registry->FetchDataProviders(m_UnknownDependencies); + for (const auto &provider : knownProviders) + { + auto itkAction = itk::MemberCommand::New(); + itkAction->SetCallbackFunction(this, &DataProvider::OnDependencyModified); + m_DependencyTags[provider->GetName()] = provider->AddObserver(itk::ModifiedEvent(), itkAction); + m_UnknownDependencies.erase(provider->GetName()); + } +} diff --git a/Modules/Annotation/src/mitkDynamicAnnotation.cpp b/Modules/Annotation/src/mitkDynamicAnnotation.cpp new file mode 100644 index 0000000000..8b330c3351 --- /dev/null +++ b/Modules/Annotation/src/mitkDynamicAnnotation.cpp @@ -0,0 +1,103 @@ +/*=================================================================== + + 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 "mitkDynamicAnnotation.h" +#include "mitkProviderRegistry.h" + +mitk::DynamicAnnotation::DynamicAnnotation(std::unordered_map config, + Annotation::Pointer annotation) + : m_Ptr(annotation), m_Config(config), m_ProviderTag(-1) +{ + if (m_Config[AnnotationConstants::TYPE] != "text") + { + SetDataProvider(m_Config[AnnotationConstants::PROVIDER]); + } +} + +void mitk::DynamicAnnotation::SetDataProvider(DataProvider::Pointer provider) +{ + if (m_DataProvider == provider) + return; + + if (m_DataProvider.IsNotNull()) + { + m_DataProvider->RemoveObserver(m_ProviderTag); + m_ProviderTag = -1; + } + + m_DataProvider = provider; + if (m_DataProvider.IsNull()) + return; + + auto itkAction = itk::SimpleMemberCommand::New(); + itkAction->SetCallbackFunction(this, &DynamicAnnotation::OnProviderModified); + m_ProviderTag = m_DataProvider->AddObserver(itk::ModifiedEvent(), itkAction); +} + +void mitk::DynamicAnnotation::SetDataProvider(std::string provider) +{ + auto registry = ProviderRegistry::GetInstance(); + auto providers = registry->FetchDataProviders({provider}); + if (!providers.empty()) + { + this->SetDataProvider(*providers.begin()); + } +} + +void mitk::DynamicAnnotation::SetManagedAnnotation(Annotation::Pointer annotation) +{ + m_Ptr = annotation; +} + +mitk::Annotation::Pointer mitk::DynamicAnnotation::GetManagedAnnotation() +{ + return m_Ptr; +} + +void mitk::DynamicAnnotation::SetText(const std::string &text) +{ + const auto &prefix = this->m_Config[AnnotationConstants::PREFIX]; + const auto &suffix = this->m_Config[AnnotationConstants::SUFFIX]; + this->m_Ptr->SetText(prefix + text + suffix); +} + +std::string mitk::DynamicAnnotation::GetText() +{ + return m_Ptr.IsNotNull() ? m_Ptr->GetText() : "Is Nullptr"; +} + +std::string mitk::DynamicAnnotation::ConfigEntry(const std::string& key) +{ + const auto value = m_Config.find(key); + return value == std::end(m_Config) ? "No value for key " + key : value->second; +} + +mitk::DynamicAnnotation::ConfigMap mitk::DynamicAnnotation::GetConfiguration() +{ + return m_Config; +} + +void mitk::DynamicAnnotation::OnProviderModified() +{ + if (m_DataProvider.IsNotNull()) + { + auto result = m_DataProvider->GetValue(); + if (result.type() == typeid(std::string)) + { + this->SetText(boost::any_cast(result)); + } + } +} diff --git a/Modules/Annotation/src/mitkProviderRegistry.cpp b/Modules/Annotation/src/mitkProviderRegistry.cpp new file mode 100644 index 0000000000..8c68c97170 --- /dev/null +++ b/Modules/Annotation/src/mitkProviderRegistry.cpp @@ -0,0 +1,34 @@ +#include "mitkProviderRegistry.h" + +mitk::ProviderRegistry::Pointer mitk::ProviderRegistry::GetInstance() +{ + static Pointer singleton = ProviderRegistry::New(); + return singleton; +} + +void mitk::ProviderRegistry::AddDataProvider(DataProvider::Pointer provider) +{ + m_Providers[provider->GetName()] = provider; +} + +void mitk::ProviderRegistry::AddDataProviders(std::set providers) +{ + for (const auto &provider : providers) + { + AddDataProvider(provider); + } +} + +std::unordered_set mitk::ProviderRegistry::FetchDataProviders( + std::unordered_set names) +{ + auto matches = std::unordered_set{}; + for (const auto &provider : m_Providers) + { + if (names.find(provider.second->GetName()) != std::end(names)) + { + matches.insert(provider.second); + } + } + return matches; +} diff --git a/Plugins/org.mitk.annotations/files.cmake b/Plugins/org.mitk.annotations/files.cmake index e39dd3532a..df873bee1e 100644 --- a/Plugins/org.mitk.annotations/files.cmake +++ b/Plugins/org.mitk.annotations/files.cmake @@ -1,30 +1,31 @@ set(MOC_H_FILES src/internal/mitkAnnotationsActivator.h ) set(SRC_CPP_FILES ) set(INTERNAL_CPP_FILES mitkAnnotationsActivator.cpp ) set(CACHED_RESOURCE_FILES resources/annotations.json + plugin.xml ) set(QRC_FILES resources/resources.qrc ) set(CPP_FILES ) foreach(file ${SRC_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/${file}) endforeach(file ${SRC_CPP_FILES}) foreach(file ${INTERNAL_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/internal/${file}) endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Plugins/org.mitk.annotations/manifest_headers.cmake b/Plugins/org.mitk.annotations/manifest_headers.cmake index 9d339be2a7..50afacd5c8 100644 --- a/Plugins/org.mitk.annotations/manifest_headers.cmake +++ b/Plugins/org.mitk.annotations/manifest_headers.cmake @@ -1,6 +1,6 @@ set(Plugin-Name "MITK Annotations") set(Plugin-Version "0.1") set(Plugin-Vendor "DKFZ, Medical and Biological Informatics") set(Plugin-ContactAddress "http://www.mitk.org") set(Require-Plugin org.mitk.gui.common) -set(Plugin-ActivationPolicy eager) +#set(Plugin-ActivationPolicy eager) diff --git a/Plugins/org.mitk.annotations/plugin.xml b/Plugins/org.mitk.annotations/plugin.xml new file mode 100644 index 0000000000..ff04491689 --- /dev/null +++ b/Plugins/org.mitk.annotations/plugin.xml @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/Plugins/org.mitk.annotations/resources/annotations.json b/Plugins/org.mitk.annotations/resources/annotations.json index e227221e7e..50d09551ce 100644 --- a/Plugins/org.mitk.annotations/resources/annotations.json +++ b/Plugins/org.mitk.annotations/resources/annotations.json @@ -1,162 +1,153 @@ { "annotations" : { "stdmulti.widget3" : { "Right": [ { "name": "colorBar", "type": "colorBar" }, { "name": "scaleLegend", "type": "scaleLegend" } ], "BottomLeft": [ { "name": "logo", "type": "logo", "path": "D:/Arbeit/Programming/mitk_m/Plugins/org.mitk.annotations/resources/logoOverlay.png" } ] }, "stdmulti.widget1" : { "TopLeft": [ { "name": "Slice Number", "defaultValue": "Slice / MaxSlice", "prefix": "Im: ", - "type": "property", + "type": "dicomProperty", "provider": "DICOM.0020.0013" }, { "name": "Sequence Number", "defaultValue": "Sequence ?", "prefix": "Se: ", - "type": "property", + "type": "dicomProperty", "provider": "DICOM.0020.0011" - }, - { - "name": "SliceNavigationControllerNumber", - "defaultValue": "Slice Nr. ?", - "prefix": "Se: ", - "type": "custom", - "provider": "SliceNavigationController", - "event": "GeometrySliceEvent", - "action": "SliceNumberAction" } ], "Top": [{ "name": "A", "type": "text" }], "TopRight": [ { "name": "Patient Name", - "type": "property", + "type": "dicomProperty", "provider": "DICOM.0010.0010" }, { "name": "Patient ID", - "type": "property", + "type": "dicomProperty", "provider": "DICOM.0010.0020" }, { "name": "Patient Birth Date", - "type": "property", + "type": "dicomProperty", "provider": "DICOM.0010.0030" }, { "name": "Patient Sex", - "type": "property", + "type": "dicomProperty", "provider": "DICOM.0010.0040" }, { "name": "Institution Name", - "type": "property", + "type": "dicomProperty", "provider": "DICOM.0008.0080" }, { "name": "Study ID", - "type": "property", + "type": "dicomProperty", "provider": "DICOM.0020.0010" }, { "name": "Study Description", - "type": "property", + "type": "dicomProperty", "provider": "DICOM.0008.1030" }, { "name": "Series Description", - "type": "property", + "type": "dicomProperty", "provider": "DICOM.0008.103E" } ], "Left": [{ "name": "R", "type": "text" }], "Right":[{ "name": "L", "type": "text" }], "BottomLeft": [ { "name": "Pixel(x,y,Val)", "type": "text" }, { "name": "Slice Thickness", "defaultValue": "Unknown Thickness", "prefix": "T: ", "suffix": " mm", - "type": "property", + "type": "dicomProperty", "provider": "DICOM.0018.0050" }, { "name": "Image Z Position", "prefix": "L: ", - "type": "property", + "type": "dicomProperty", "provider": "DICOM.0020.0032" } ], "BottomRight": [ { "name": "FS: 1", "type": "text" }, { "name": "Repetition Time", "prefix": "TR: ", - "type": "property", + "type": "dicomProperty", "provider": "DICOM.0018.0080" }, { "name": "Echo Time", "prefix": "TE: ", - "type": "property", + "type": "dicomProperty", "provider": "DICOM.0018.0081" }, { "name": "Acquisition Date", - "type": "property", + "type": "dicomProperty", "provider": "DICOM.0008.0022" }, { "name": "Acquisition Time", - "type": "property", + "type": "dicomProperty", "provider": "DICOM.0008.0032" } ], "Bottom":[{ "name": "P", "type": "text" }] } } } \ No newline at end of file diff --git a/Plugins/org.mitk.annotations/resources/annotationsReduced.json b/Plugins/org.mitk.annotations/resources/annotationsReduced.json new file mode 100644 index 0000000000..ce773bf24e --- /dev/null +++ b/Plugins/org.mitk.annotations/resources/annotationsReduced.json @@ -0,0 +1,23 @@ +{ + "annotations" : { + "stdmulti.widget1" : { + "TopLeft": + [ + { + "name": "Slice Number", + "defaultValue": "Slice / MaxSlice", + "prefix": "Im: ", + "type": "dicomProperty", + "provider": "DICOM.0020.0013" + }, + { + "name": "Sequence Number", + "defaultValue": "Sequence ?", + "prefix": "Se: ", + "type": "dicomProperty", + "provider": "DICOM.0020.0011" + } + ] + } + } +} \ No newline at end of file diff --git a/Plugins/org.mitk.annotations/src/internal/mitkAnnotationsActivator.cpp b/Plugins/org.mitk.annotations/src/internal/mitkAnnotationsActivator.cpp index 38a761af7b..9202319eae 100644 --- a/Plugins/org.mitk.annotations/src/internal/mitkAnnotationsActivator.cpp +++ b/Plugins/org.mitk.annotations/src/internal/mitkAnnotationsActivator.cpp @@ -1,115 +1,150 @@ /*=================================================================== 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 "mitkAnnotationsActivator.h" -#include +#include "mitkAnnotationFactory.h" +#include "mitkAnnotationJsonReaderWriter.h" +#include "mitkDataNodeSelection.h" +#include "mitkProviderRegistry.h" #include "berryISelectionService.h" #include "berryPlatformUI.h" -#include "mitkAnnotationFactory.h" -#include "mitkDataNodeSelection.h" + +#include mitk::NodeSelectionListener::NodeSelectionListener(std::function callback) : m_Callback(std::move(callback)){}; void mitk::NodeSelectionListener::SelectionChanged(const berry::IWorkbenchPart::Pointer &part, const berry::ISelection::ConstPointer &selection) { const auto nodeSelection = dynamic_cast(selection.GetPointer()); if (nodeSelection) { auto nodes = nodeSelection->GetSelectedDataNodes(); if (!nodes.empty()) { m_Callback(nodes.front()); } } }; auto mitk::AnnotationsActivator::OnSelectionChangedCallback() { return [this](DataNode::Pointer node) { if (node.IsNotNull()) { - const auto baseData = node->GetData(); - this->m_AnnotationFactory->SetProviderForAllPropertyAnnotations(baseData); + m_SelectedDataNode = node; + this->Modified(); } }; } auto mitk::AnnotationsActivator::SliceNumberAction() const { - return [](const itk::Object *caller, const itk::EventObject &event, unsigned int slice, unsigned int timeStep) { + return [](const itk::Object *caller, const itk::EventObject &event) { const auto sliceEvent = dynamic_cast(&event); if (sliceEvent) { - return std::to_string(sliceEvent->GetPos()); + return sliceEvent->GetPos(); + } + return static_cast(0); + }; +} + +auto mitk::AnnotationsActivator::SelectionChangeAction() const +{ + return [](const itk::Object *caller, const itk::EventObject &event) { + const auto dataNodeProvider = dynamic_cast(caller); + if (dataNodeProvider) + { + return dataNodeProvider->GetSelectedDataNode(); } - return std::string(); + return itk::SmartPointer(nullptr); }; } void mitk::AnnotationsActivator::start(ctkPluginContext *context) { m_NodeSelectionListener = std::make_unique(OnSelectionChangedCallback()); - m_AnnotationFactory = AnnotationFactory::GetServiceInstance(); - - AnnotationFactory::ActionFunctionMap actions{{"SliceNumberAction", SliceNumberAction()}}; - AnnotationFactory::EventMap events{ - {"GeometrySliceEvent", SliceNavigationController::GeometrySliceEvent(nullptr, 0).MakeObject()}}; + ObserveSelectionChanges(); + + auto sliceNumberProvider = + DataProvider::New("SliceNumberProviderAxial", + BaseRenderer::GetByName("stdmulti.widget1")->GetSliceNavigationController(), + SliceNavigationController::GeometrySliceEvent(nullptr, 0).MakeObject(), + SliceNumberAction()); + auto timeStepProvider = DataProvider::New("TimeStepProvider", + BaseRenderer::GetByName("stdmulti.widget1")->GetSliceNavigationController(), + SliceNavigationController::TimeGeometryEvent(nullptr, 0).MakeObject(), + SliceNumberAction()); + auto dataNodeProvider = DataProvider::New("DataNodeProvider", this, SelectionChangeEvent(), SelectionChangeAction()); + + auto providers = ProviderRegistry::GetInstance(); + providers->AddDataProviders({sliceNumberProvider, timeStepProvider, dataNodeProvider}); + + // TODO remove hardcoded string + auto fileName = + std::string{R"(D:\Arbeit\Programming\mitk_m\Plugins\org.mitk.annotations\resources\annotationsReduced.json)"}; try { - m_AnnotationFactory->CreateAnnotationsFromJson( - R"(D:\Arbeit\Programming\mitk_m\Plugins\org.mitk.annotations\resources\annotationsSaved.json)", actions, events); + m_Annotations = AnnotationJsonReaderWriter::ReadAnnotationsFromJsonFile(fileName); } catch (const std::exception &e) { - MITK_ERROR << "Exception while loading json configuration. Probably no annotations were created"; + MITK_ERROR << "Exception while loading json configuration"; MITK_ERROR << e.what(); } + + AnnotationFactory::CreateAnnotationsFromConfiguration(m_Annotations); + + AnnotationFactory::RegisterAnnotations(m_Annotations); + try { - m_AnnotationFactory->SaveConfigurationToJsonFile( - R"(D:\Arbeit\Programming\mitk_m\Plugins\org.mitk.annotations\resources\annotationsSaved.json)"); + AnnotationJsonReaderWriter::WriteAnnotationsToJsonFile(fileName + "Saved", m_Annotations); } catch (const std::exception &e) { MITK_ERROR << "Exception while saving json configuration. The save file probably wasn't written or updated"; MITK_ERROR << e.what(); } - - // wait until the selection service is ready - m_AnnotationFuture = std::async(std::launch::async, [this]() { - std::this_thread::sleep_for(std::chrono::seconds(10)); - // m_AnnotationFactory->Update(); leads to crash in vtk (maybe threading issue) - ObserveSelectionChanges(); - }); } void mitk::AnnotationsActivator::stop(ctkPluginContext * /*context*/) {} +mitk::DataNode::Pointer mitk::AnnotationsActivator::GetSelectedDataNode() const +{ + return m_SelectedDataNode.IsExpired() ? nullptr : m_SelectedDataNode.Lock(); +} + void mitk::AnnotationsActivator::ObserveSelectionChanges() { auto windows = berry::PlatformUI::GetWorkbench()->GetWorkbenchWindows(); auto activeWindow = berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow(); auto selectionService = activeWindow->GetSelectionService(); if (m_NodeSelectionListener) { selectionService->AddPostSelectionListener(m_NodeSelectionListener.get()); } } + +const itk::EventObject *mitk::AnnotationsActivator::SelectionChangeEvent() +{ + return itk::ModifiedEvent().MakeObject(); +} diff --git a/Plugins/org.mitk.annotations/src/internal/mitkAnnotationsActivator.h b/Plugins/org.mitk.annotations/src/internal/mitkAnnotationsActivator.h index 65560e7b30..dead29fcc8 100644 --- a/Plugins/org.mitk.annotations/src/internal/mitkAnnotationsActivator.h +++ b/Plugins/org.mitk.annotations/src/internal/mitkAnnotationsActivator.h @@ -1,68 +1,81 @@ /*=================================================================== 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 MITKANNOTATIONSACTIVATOR_H_ #define MITKANNOTATIONSACTIVATOR_H_ #include "mitkAnnotationFactory.h" -#include -#include #include "mitkIRenderWindowPart.h" + #include "berryISelection.h" -#include "berryIWorkbenchPart.h" #include "berryISelectionListener.h" +#include "berryIWorkbenchPart.h" + +#include + +#include namespace mitk { class NodeSelectionListener : public berry::ISelectionListener { public: NodeSelectionListener(std::function callback); void SelectionChanged(const berry::IWorkbenchPart::Pointer &part, const berry::ISelection::ConstPointer &selection) override; + private: std::function m_Callback; }; /** * \ingroup org_mitk_annotations_internal * * \brief The plug-in activator for mitk annotations */ - class AnnotationsActivator : public QObject, public ctkPluginActivator + class AnnotationsActivator : public QObject, public ctkPluginActivator, public itk::Object { Q_OBJECT Q_PLUGIN_METADATA(IID "org.mitk.annotations") Q_INTERFACES(ctkPluginActivator) public: void start(ctkPluginContext *context) override; void stop(ctkPluginContext *context) override; + DataNode::Pointer GetSelectedDataNode() const; + private: // observers and callbacks void ObserveSelectionChanges(); auto OnSelectionChangedCallback(); + // events as defined in the json file + const itk::EventObject *SelectionChangeEvent(); + // action functions as defined in the json file auto SliceNumberAction() const; + auto SelectionChangeAction() const; + auto PropertyChangeAction() const; - AnnotationFactory::Pointer m_AnnotationFactory; + WeakPointer m_AnnotationFactory; std::future m_AnnotationFuture; std::unique_ptr m_NodeSelectionListener; + WeakPointer m_SelectedDataNode; + std::vector m_Annotations; }; -} +} // namespace mitk #endif /* MITKANNOTATIONSACTIVATOR_H_ */