diff --git a/Modules/Annotation/include/mitkAnnotationFactory.h b/Modules/Annotation/include/mitkAnnotationFactory.h index 28df8bff94..b9d2825281 100644 --- a/Modules/Annotation/include/mitkAnnotationFactory.h +++ b/Modules/Annotation/include/mitkAnnotationFactory.h @@ -1,91 +1,98 @@ #pragma once #include "MitkAnnotationExports.h" +#include "mitkAnnotation.h" #include "mitkCommon.h" -#include "mitkDynamicTextAnnotation2D.h" +#include "mitkLayoutAnnotationRenderer.h" #include #include #include +#include "DynamicAnnotationWrapper.h" +#include "mitkDynamicAnnotation.h" 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"; } // namespace AnnotationConstants + /* + * 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: - using DataRetrievalAction = - std::function; - private: - struct DynamicAnnotation - { - mitk::Annotation::Pointer ptr; - std::unordered_map config; - struct DataProvider - { - itk::Object::Pointer object; - DataRetrievalAction dataRetrievalAction; - const itk::EventObject *dataChangedEvent; - - unsigned long modifiedTag; - unsigned long deletedTag; - } providerInfo; - }; public: mitkClassMacroNoParent(AnnotationFactory); itkFactorylessNewMacro(AnnotationFactory); - using ActionFunctionMap = - std::unordered_map; + using ActionFunctionMap = std::unordered_map; using EventMap = std::unordered_map; void CreateAnnotationsFromJson(const std::string &fileName, ActionFunctionMap actionFunctionMap = ActionFunctionMap(), EventMap eventMap = EventMap()); - void SetProviderForAnnotation(itk::Object::Pointer provider, - DynamicAnnotation &annotation); - void AddSingleAnnotation(mitk::Annotation::Pointer annotation); + void SetProviderForAnnotation(itk::Object::Pointer provider, DynamicAnnotation &annotation); + void AddSingleAnnotation(DynamicAnnotation annotation); void SetProvider(const std::string &providerJsonKey, itk::Object::Pointer provider); - void SetPropertyProvider(BaseData *provider); + void SetProviderForAllPropertyAnnotations(BaseData *provider); std::vector GetManagedAnnotationIds(); + unsigned int GetAmountOfPendingAnnotations(); void SaveConfigurationToJsonFile(const std::string &fileName); bool Update(); - void SliceChanged(const std::string &rendererName, unsigned int slice); + void SliceChanged(const Object *caller, const itk::EventObject &event); + private: using AnnotationVector = std::vector; - std::vector JsonToAnnotationInfoVector(boost::property_tree::ptree const &root); + static std::vector JsonToAnnotationInfoVector( + boost::property_tree::ptree const &root); + LayoutAnnotationRenderer::Alignment GetAlignment(const std::string alignmentName) const; + void ObserveSliceChangedEvents(BaseRenderer* renderer); + bool RegisterAnnotation(DynamicAnnotation dynamicAnnotation); + void OnPropertyDeleted(const itk::Object *caller, const itk::EventObject &event); + void OnPropertyModified(const itk::Object *caller, const itk::EventObject &event); AnnotationVector m_Annotations; AnnotationVector m_WaitingAnnotations; std::unordered_map m_SliceNavigationControllerTags; + std::weak_ptr m_PropertyListOwner; + WeakPointer m_PropertyProvider; + unsigned m_Slice; + unsigned m_TimeStep; + 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 diff --git a/Modules/Annotation/include/mitkDynamicAnnotation.h b/Modules/Annotation/include/mitkDynamicAnnotation.h new file mode 100644 index 0000000000..d349e79dba --- /dev/null +++ b/Modules/Annotation/include/mitkDynamicAnnotation.h @@ -0,0 +1,136 @@ +#pragma once +#include "mitkAnnotation.h" +#include "mitkLogMacros.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 +{ + struct DynamicAnnotation + { + 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; + + OnEventAction dataRetrievalAction; + OnSliceChanged sliceChanged; + OnTimeStepChanged timeStepChanged; + + std::vector tags; + } providerInfo; + }; +} // namespace mitk \ No newline at end of file diff --git a/Modules/Annotation/src/mitkAnnotationFactory.cpp b/Modules/Annotation/src/mitkAnnotationFactory.cpp index a951719b07..b433457823 100644 --- a/Modules/Annotation/src/mitkAnnotationFactory.cpp +++ b/Modules/Annotation/src/mitkAnnotationFactory.cpp @@ -1,295 +1,366 @@ #include "mitkAnnotationFactory.h" +#include "DynamicAnnotationWrapper.h" #include "mitkAnnotationUtils.h" #include "mitkLayoutAnnotationRenderer.h" #include "mitkTemporoSpatialStringProperty.h" +#include "mitkTextAnnotation2D.h" #include #include +#include "mitkDynamicAnnotation.h" 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 SliceChangedCommand : public itk::Command -{ - mitk::AnnotationFactory::Pointer m_Factory; - std::string m_RendererName; - -public: - SliceChangedCommand(mitk::AnnotationFactory::Pointer factory, const std::string &rendererName) - : m_Factory(factory), m_RendererName(rendererName){}; - 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 geometryEvent = dynamic_cast(&event); - if (geometryEvent) - { - m_Factory->SliceChanged(m_RendererName, geometryEvent->GetPos()); - } - }; -}; - class ItkRetrievalCommand : public itk::Command { - mitk::AnnotationFactory::DataRetrievalAction m_DataRetrievalAction; + mitk::DynamicAnnotation::OnEventAction m_OnEventAction; std::string m_AnnotationId; public: - ItkRetrievalCommand(mitk::AnnotationFactory::DataRetrievalAction dataRetrievalAction, const std::string &annotationId) - : m_DataRetrievalAction(dataRetrievalAction), m_AnnotationId(annotationId){}; + ItkRetrievalCommand(mitk::DynamicAnnotation::OnEventAction dataRetrievalAction, const std::string &annotationId) + : m_OnEventAction(dataRetrievalAction), m_AnnotationId(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) { - auto rendererId = annotation->GetProperty(mitk::Annotation::US_PROPKEY_RENDERER_ID)->GetValueAsString(); - auto slice = mitk::BaseRenderer::GetByName(rendererId)->GetSlice(); - auto updatedText = m_DataRetrievalAction(caller, event, slice); + // 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( +std::vector mitk::AnnotationFactory::JsonToAnnotationInfoVector( boost::property_tree::ptree const &root) { std::vector elements; auto annotations = root.begin(); if (annotations->first == "annotations") { - for (auto window : annotations->second) + for (const auto &window : annotations->second) { - for (auto layout : window.second) + for (const auto &layout : window.second) { - for (auto object : layout.second) + for (const auto &object : layout.second) { DynamicAnnotation annotation; annotation.config[AnnotationConstants::WINDOW] = window.first; annotation.config[AnnotationConstants::LAYOUT] = layout.first; - for (auto property : object.second) + 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; } -void mitk::AnnotationFactory::SliceChanged(const std::string &rendererName, unsigned slice) +mitk::LayoutAnnotationRenderer::Alignment mitk::AnnotationFactory::GetAlignment(const std::string alignmentName) const { + 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); + + return alignment != std::end(alignmentMap) ? alignment->second : alignmentMap.at(defaultFallback); +} + +void mitk::AnnotationFactory::ObserveSliceChangedEvents(BaseRenderer *renderer) +{ + 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::SliceChanged); + + 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]; + + 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) + { + SetProviderForAnnotation(renderer->GetSliceNavigationController(), dynamicAnnotation); + } + return true; + } + return false; +} + +void mitk::AnnotationFactory::SliceChanged(const Object *caller, const itk::EventObject &event) +{ + auto geometryEvent = dynamic_cast(&event); + if (!geometryEvent) + return; + + m_Slice = geometryEvent->GetPos(); + + auto sliceNavController = dynamic_cast(caller); + if (!sliceNavController) + return; + + auto renderer = sliceNavController->GetRenderer(); + if (!renderer) + return; + + auto rendererName = renderer->GetName(); for (auto &annotation : m_Annotations) { if (annotation.config[AnnotationConstants::TYPE] == "property" && annotation.config[AnnotationConstants::WINDOW] == rendererName) { annotation.ptr->SetText(annotation.providerInfo.dataRetrievalAction( - annotation.providerInfo.object, *annotation.providerInfo.dataChangedEvent, slice)); + annotation.providerInfo.object, *annotation.providerInfo.dataChangedEvent, m_Slice, m_TimeStep)); } } } void mitk::AnnotationFactory::CreateAnnotationsFromJson(const std::string &fileName, ActionFunctionMap actionFunctionMap, EventMap eventMap) { boost::property_tree::ptree root; read_json(R"(D:\Arbeit\Programming\mitk_m\Plugins\org.mitk.annotations\resources\annotations.json)", root); auto annotations = JsonToAnnotationInfoVector(root); for (auto annotation : annotations) { auto type = annotation.config[AnnotationConstants::TYPE]; if (type == "text" || type == "property") { annotation.ptr = mitk::TextAnnotation2D::New(); annotation.providerInfo.dataChangedEvent = itk::ModifiedEvent().MakeObject(); + auto defaultName = annotation.config[AnnotationConstants::DEFAULT]; annotation.providerInfo.dataRetrievalAction = - [](const itk::Object *caller, const itk::EventObject &, unsigned int slice) { + [defaultName](const itk::Object *caller, const itk::EventObject &, unsigned int slice, unsigned int timeStep) { auto dicomProperty = dynamic_cast(caller); if (dicomProperty) { - MITK_INFO << "updating dicom property for slice " << slice; return dicomProperty->GetValueBySlice(slice); } auto property = dynamic_cast(caller); if (property) { return property->GetValueAsString(); } - return std::string("Not found"); + return defaultName + " (NA)"; }; } else if (type == "custom") { 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]]; } else { throw std::logic_error("Unknown or missing annotation type"); } - m_WaitingAnnotations.push_back(annotation); + if (!RegisterAnnotation(annotation)) + { + // Registration of annotation failed propbably due to missing base renderer + // Store annotation for delayed registration + m_WaitingAnnotations.push_back(annotation); + } } - this->Update(); } void mitk::AnnotationFactory::SetProviderForAnnotation(itk::Object::Pointer provider, DynamicAnnotation &annotation) { if (provider.IsNotNull() && provider != annotation.providerInfo.object) { if (annotation.providerInfo.object) { - annotation.providerInfo.object->RemoveObserver(annotation.providerInfo.modifiedTag); - annotation.providerInfo.object->RemoveObserver(annotation.providerInfo.deletedTag); + for (const auto &tag : annotation.providerInfo.tags) + { + annotation.providerInfo.object->RemoveObserver(tag); + } } annotation.providerInfo.object = provider; if (annotation.providerInfo.dataChangedEvent) { - annotation.providerInfo.modifiedTag = annotation.providerInfo.object->AddObserver( + annotation.providerInfo.tags.push_back(annotation.providerInfo.object->AddObserver( *annotation.providerInfo.dataChangedEvent, - new ItkRetrievalCommand(annotation.providerInfo.dataRetrievalAction, annotation.ptr->GetMicroserviceID())); + new ItkRetrievalCommand(annotation.providerInfo.dataRetrievalAction, annotation.ptr->GetMicroserviceID()))); } - annotation.providerInfo.deletedTag = - annotation.providerInfo.object->AddObserver(itk::DeleteEvent(), new PropertyDeletedCommand()); + if (annotation.config[AnnotationConstants::TYPE] == "property") + { + annotation.providerInfo.tags.push_back( + annotation.providerInfo.object->AddObserver(itk::DeleteEvent(), new PropertyDeletedCommand())); + } + } +} - annotation.ptr->SetText(annotation.providerInfo.dataRetrievalAction( - annotation.providerInfo.object, *annotation.providerInfo.dataChangedEvent, 0)); +void mitk::AnnotationFactory::AddSingleAnnotation(DynamicAnnotation annotation) +{ + if (annotation.ptr) + { + try + { + 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) + { + 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); + } + } } } 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::SetPropertyProvider(BaseData *provider) +void mitk::AnnotationFactory::SetProviderForAllPropertyAnnotations(BaseData *provider) { - Update(); - for (auto annotation : m_Annotations) + Update(); // TODO temporary semi fix + + if (provider) { - if (annotation.config[AnnotationConstants::TYPE] == "property") + if (!m_PropertyProvider.IsExpired()) { - annotation.ptr->SetColor(200, 0, 0); - annotation.ptr->SetText(annotation.config[AnnotationConstants::DEFAULT] + " (NA)"); - auto propertyName = annotation.config[AnnotationConstants::PROVIDER]; - auto property = provider->GetProperty(propertyName.c_str()); - if (property) + // TODO unsubscribe + } + m_PropertyProvider = provider->GetPropertyList(); + if (!m_PropertyProvider.IsExpired()) + { + m_PropertyProvider.Lock()->AddObserver(itk::ModifiedEvent(), new PropertyDeletedCommand()); + m_PropertyProvider.Lock()->AddObserver(itk::DeleteEvent(), new PropertyDeletedCommand()); + } + } + + 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); + annotation.ptr->SetText(annotation.providerInfo.dataRetrievalAction( + annotation.providerInfo.object, *annotation.providerInfo.dataChangedEvent, 0, 0)); + if (annotation.ptr->GetText().empty()) { - annotation.ptr->SetColor(0, 200, 0); - SetProviderForAnnotation(property.GetPointer(), annotation); - if (annotation.ptr->GetText().empty()) - { - annotation.ptr->SetColor(0, 0, 200); - annotation.ptr->SetText(annotation.config[AnnotationConstants::DEFAULT] + " (empty)"); - } + annotation.ptr->SetColor(0, 0, 200); + annotation.ptr->SetText(annotation.config[AnnotationConstants::DEFAULT] + " (empty)"); } } + else + { + annotation.ptr->SetColor(200, 0, 0); + annotation.ptr->SetText(annotation.config[AnnotationConstants::DEFAULT] + " (NA)"); + } } } -bool mitk::AnnotationFactory::Update() +std::vector mitk::AnnotationFactory::GetManagedAnnotationIds() { - auto endOfRemainingRange = std::remove_if( - m_WaitingAnnotations.begin(), m_WaitingAnnotations.end(), [this](DynamicAnnotation dynamicAnnotation) { - auto &data = dynamicAnnotation.config; - auto &annotation = dynamicAnnotation.ptr; - auto renderer = BaseRenderer::GetByName(data[AnnotationConstants::WINDOW]); + auto ids = std::vector{}; + for (const auto &annotation : m_Annotations) + { + ids.push_back(annotation.ptr->GetMicroserviceID()); + } + return ids; +} - if (renderer) - { - if (m_SliceNavigationControllerTags.find(data[AnnotationConstants::WINDOW]) == - m_SliceNavigationControllerTags.end()) - { - auto sliceNavController = renderer->GetSliceNavigationController(); - m_SliceNavigationControllerTags[data[AnnotationConstants::WINDOW]] = - sliceNavController->AddObserver(SliceNavigationController::GeometrySliceEvent(nullptr, 0), - new SliceChangedCommand(this, data[AnnotationConstants::WINDOW])); - } +unsigned mitk::AnnotationFactory::GetAmountOfPendingAnnotations() +{ + return m_WaitingAnnotations.size(); +} - m_Annotations.push_back(dynamicAnnotation); - LayoutAnnotationRenderer::Alignment alignment = LayoutAnnotationRenderer::TopLeft; - auto layout = data[AnnotationConstants::LAYOUT]; - if (layout.empty()) - { - alignment = LayoutAnnotationRenderer::TopLeft; - } - else if (layout == "TopRight") - { - alignment = LayoutAnnotationRenderer::TopRight; - } - else if (layout == "BottomLeft") - { - alignment = LayoutAnnotationRenderer::BottomLeft; - } - else if (layout == "BottomRight") - { - alignment = LayoutAnnotationRenderer::BottomRight; - } - else if (layout == "Top") - { - alignment = LayoutAnnotationRenderer::Top; - } - else if (layout == "Left") - { - alignment = LayoutAnnotationRenderer::Left; - } - else if (layout == "Right") - { - alignment = LayoutAnnotationRenderer::Right; - } - else if (layout == "Bottom") - { - alignment = LayoutAnnotationRenderer::Bottom; - } +bool mitk::AnnotationFactory::Update() +{ + const auto firstElementToErase = std::remove_if(m_WaitingAnnotations.begin(), + m_WaitingAnnotations.end(), + [this](DynamicAnnotation da) { return RegisterAnnotation(da); }); - annotation->SetName(data[AnnotationConstants::NAME]); - annotation->SetText(data[AnnotationConstants::DEFAULT]); - MITK_INFO << data[AnnotationConstants::NAME]; - LayoutAnnotationRenderer::AddAnnotation(annotation, renderer, alignment); - std::this_thread::sleep_for(std::chrono::seconds(2)); - return true; - } - return false; - }); - m_WaitingAnnotations.erase(endOfRemainingRange, m_WaitingAnnotations.end()); + m_WaitingAnnotations.erase(firstElementToErase, m_WaitingAnnotations.end()); return m_WaitingAnnotations.empty(); } diff --git a/Modules/Annotation/test/mitkAnnotationFactoryTest.cpp b/Modules/Annotation/test/mitkAnnotationFactoryTest.cpp index 78ed2a05b4..94f18a411c 100644 --- a/Modules/Annotation/test/mitkAnnotationFactoryTest.cpp +++ b/Modules/Annotation/test/mitkAnnotationFactoryTest.cpp @@ -1,62 +1,64 @@ /*=================================================================== 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 "mitkTestFixture.h" #include "mitkTestingMacros.h" #include "mitkTextAnnotation2D.h" +#include "mitkDynamicAnnotation.h" class mitkAnnotationFactoryTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkAnnotationFactoryTestSuite); MITK_TEST(ReadJsonConfiguration); MITK_TEST(ReactToPropertyChangedEvent); CPPUNIT_TEST_SUITE_END(); std::string m_AnnotationTestDataDir = R"(D:\Arbeit\Programming\mitk_m\Plugins\org.mitk.annotations\resources)"; std::string m_ValidJsonFile = "annotations.json"; mitk::AnnotationFactory::Pointer m_Factory; public: void setUp() override { m_Factory = mitk::AnnotationFactory::New(); }; void tearDown() override{}; void ReadJsonConfiguration() { m_Factory->CreateAnnotationsFromJson(m_AnnotationTestDataDir + m_ValidJsonFile); const auto amountOfRegisteredAnnotations = m_Factory->GetManagedAnnotationIds().size(); const auto amountOfPendingAnnotations = m_Factory->GetAmountOfPendingAnnotations(); CPPUNIT_ASSERT_EQUAL_MESSAGE("The amount of created annotations seems incorrect", static_cast(22), amountOfRegisteredAnnotations + amountOfPendingAnnotations); }; void ReactToPropertyChangedEvent() { - auto dynamicAnnotation = mitk::AnnotationFactory::DynamicAnnotation{}; - dynamicAnnotation.ptr = mitk::TextAnnotation2D::New(); - m_Factory->AddSingleAnnotation(dynamicAnnotation); + //auto dynamicAnnotation = mitk::DynamicAnnotation{}; + //dynamicAnnotation.ptr = mitk::TextAnnotation2D::New(); + //m_Factory->AddSingleAnnotation(dynamicAnnotation); }; void ReactToSliceNumberChangedEvent(); + }; MITK_TEST_SUITE_REGISTRATION(mitkAnnotationFactory) \ No newline at end of file diff --git a/Plugins/org.mitk.annotations/resources/annotations.json b/Plugins/org.mitk.annotations/resources/annotations.json index 66dd7e5dc1..fde0c1e59e 100644 --- a/Plugins/org.mitk.annotations/resources/annotations.json +++ b/Plugins/org.mitk.annotations/resources/annotations.json @@ -1,132 +1,141 @@ { "annotations" : { "stdmulti.widget1" : { "TopLeft": [ { "name": "Slice Number", "defaultValue": "Slice / MaxSlice", "prefix": "Im: ", "type": "property", "provider": "DICOM.0020.0013" }, { "name": "Sequence Number", "defaultValue": "Sequence ?", "prefix": "Se: ", "type": "property", "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", "provider": "DICOM.0010.0010" }, { "name": "Patient ID", "type": "property", "provider": "DICOM.0010.0020" }, { "name": "Patient Birth Date", "type": "property", "provider": "DICOM.0010.0030" }, { "name": "Patient Sex", "type": "property", "provider": "DICOM.0010.0040" }, { "name": "Institution Name", "type": "property", "provider": "DICOM.0008.0080" }, { "name": "Study ID", "type": "property", "provider": "DICOM.0020.0010" }, { "name": "Study Description", "type": "property", "provider": "DICOM.0008.1030" }, { "name": "Series Description", "type": "property", "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", "provider": "DICOM.0018.0050" }, { "name": "Image Z Position", "prefix": "L: ", "type": "property", "provider": "DICOM.0020.0032" } ], "BottomRight": [ { "name": "FS: 1", "type": "text" }, { "name": "Repetition Time", "prefix": "TR: ", "type": "property", "provider": "DICOM.0018.0080" }, { "name": "Echo Time", "prefix": "TE: ", "type": "property", "provider": "DICOM.0018.0081" }, { "name": "Acquisition Date", "type": "property", "provider": "DICOM.0008.0022" }, { "name": "Acquisition Time", "type": "property", "provider": "DICOM.0008.0032" } ], "Bottom":[{ "name": "P", "type": "text" }] } } } \ 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 38700858cf..33da8876f3 100644 --- a/Plugins/org.mitk.annotations/src/internal/mitkAnnotationsActivator.cpp +++ b/Plugins/org.mitk.annotations/src/internal/mitkAnnotationsActivator.cpp @@ -1,76 +1,77 @@ /*=================================================================== 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 "berryISelectionService.h" #include "berryPlatformUI.h" #include "mitkAnnotationFactory.h" #include "mitkDataNodeSelection.h" void mitk::AnnotationsActivator::start(ctkPluginContext * /*context*/) { m_AnnotationFactory = AnnotationFactory::New(); - auto sliceNumberAction = [](const itk::Object *caller, const itk::EventObject &event, unsigned int slice) { + auto sliceNumberAction = [](const itk::Object *caller, const itk::EventObject &event, unsigned int slice, unsigned int timeStep) { auto sliceEvent = dynamic_cast(&event); if (sliceEvent) { return std::to_string(sliceEvent->GetPos()); } return std::string(); }; AnnotationFactory::ActionFunctionMap actions{{"SliceNumberAction", sliceNumberAction}}; AnnotationFactory::EventMap events{ {"GeometrySliceEvent", SliceNavigationController::GeometrySliceEvent(nullptr, 0).MakeObject()}}; + //TODO throw comprehensible exceptions on parse error m_AnnotationFactory->CreateAnnotationsFromJson( R"(D:\Arbeit\Programming\mitk_m\Plugins\org.mitk.annotations\resources\annotations.json)", actions, events); 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) auto windows = berry::PlatformUI::GetWorkbench()->GetWorkbenchWindows(); auto activeWindow = berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow(); auto selectionService = activeWindow->GetSelectionService(); class NodeSelectionListener : public berry::ISelectionListener { public: NodeSelectionListener(AnnotationFactory *factory) : m_Factory(factory){}; void SelectionChanged(const berry::IWorkbenchPart::Pointer& part, const berry::ISelection::ConstPointer& selection) override { const auto nodeSelection = dynamic_cast(selection.GetPointer()); if (nodeSelection) { auto nodes = nodeSelection->GetSelectedDataNodes(); if (!nodes.empty()) { - m_Factory->SetPropertyProvider(nodes.front()->GetData()); + m_Factory->SetProviderForAllPropertyAnnotations(nodes.front()->GetData()); } } }; AnnotationFactory *m_Factory; }; selectionService->AddPostSelectionListener(new NodeSelectionListener(m_AnnotationFactory)); - + //RenderingManager::GetInstance()->RequestUpdateAll(); }); } void mitk::AnnotationsActivator::stop(ctkPluginContext * /*context*/) {}