diff --git a/Modules/SemanticRelations/files.cmake b/Modules/SemanticRelations/files.cmake index 84f58f3c85..3fbf24cd98 100644 --- a/Modules/SemanticRelations/files.cmake +++ b/Modules/SemanticRelations/files.cmake @@ -1,11 +1,12 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkControlPointManager.cpp mitkDICOMHelper.cpp + mitkLesionData.cpp mitkLesionManager.cpp mitkNodePredicates.cpp mitkRelationStorage.cpp mitkSemanticRelations.cpp mitkUIDGeneratorBoost.cpp ) diff --git a/Modules/SemanticRelations/include/mitkControlPointManager.h b/Modules/SemanticRelations/include/mitkControlPointManager.h index e1e5f3a474..3b2665e570 100644 --- a/Modules/SemanticRelations/include/mitkControlPointManager.h +++ b/Modules/SemanticRelations/include/mitkControlPointManager.h @@ -1,89 +1,87 @@ /*=================================================================== 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 MITKCONTROLPOINTMANAGER_H #define MITKCONTROLPOINTMANAGER_H // semantic relations module #include "mitkSemanticTypes.h" #include // mitk core #include namespace mitk { /** * @brief Provides helper functions that are needed to work with control points. * * These functions help to generate new control points, check for overlapping / containing control points or provide functionality * to find a fitting control point or even extend an already existing control point. */ /** * @brief Generates a control point from a given data node. * The date is extracted from the data node by using the 'DICOMHelper::GetDICOMDateFromDataNode'-function. - * 'GenerateControlPoint(const SemanticTypes::TimePoint&)' is used to generate a new control point from this extracted date. * * @par datanode A data node pointer, whose date should be included in the newly generated control point. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint GenerateControlPoint(const mitk::DataNode* datanode); /** * @brief Find and return a whole control point including its date given a specific control point UID. * * @param controlPointUID The control point UID as string. * @param allControlPoints All currently known control points of a specific case. * * @return The control point with its UID and the date. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint GetControlPointByUID(const SemanticTypes::ID& controlPointUID, const std::vector& allControlPoints); /** * @brief Returns a string that displays the given control point in the format "YYYY-MM-DD". * This function is used in the GUI to display the control point as header in the "information-type - control-point"-matrix. * * @par controlPoint The control point to convert into a string. */ MITKSEMANTICRELATIONS_EXPORT std::string GetControlPointAsString(const SemanticTypes::ControlPoint& controlPoint); /** * @brief Returns an already existing control point from the given vector of control points. This existing control point has the * the same date (year, month, day) as the given single control point. * If no existing control point can be found an empty control point is returned. * * @par controlPoint The control point to check for existence. * @par allControlPoints The vector of already existing control points. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint FindExistingControlPoint(const SemanticTypes::ControlPoint& controlPoint, const std::vector& allControlPoints); /** * @brief Returns an already existing close control point from the given vector of control points. This closest control point has a date * date that is within a certain distance-in-days to the given control point. * If no closest control point can be found within the distance threshold an empty control point is returned. * * @par controlPoint The control point to check for distance. * @par allControlPoints The vector of already existing control points. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ControlPoint FindClosestControlPoint(const SemanticTypes::ControlPoint& controlPoint, std::vector& allControlPoints); /** * @brief Returns the examination period to which the given control point belongs. * Each examination point holds a vector of control point UIDs so that the UID of the given control point can be compared against the UIDs of the vector. * An empty examination period is returned if, * - the given vector of examination periods is empty * - the examination periods do not contain any control point UIDs * - the UID of the given control point is not contained in any examination period */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ExaminationPeriod FindExaminationPeriod(const SemanticTypes::ControlPoint& controlPoint, const std::vector& allExaminationPeriods); - } // namespace mitk #endif // MITKCONTROLPOINTMANAGER_H diff --git a/Modules/SemanticRelations/include/mitkLesionData.h b/Modules/SemanticRelations/include/mitkLesionData.h new file mode 100644 index 0000000000..c67ded1481 --- /dev/null +++ b/Modules/SemanticRelations/include/mitkLesionData.h @@ -0,0 +1,72 @@ +/*=================================================================== + +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 MITKLESIONDATA_H +#define MITKLESIONDATA_H + +#include + +// mitk semantic relations module +#include "mitkSemanticTypes.h" + +// c++ +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4251) +#endif + +namespace mitk +{ + /** + * @brief This class holds the data of each lesion in the lesion tree view. + * + */ + class MITKSEMANTICRELATIONS_EXPORT LesionData + { + public: + /** + * @brief sets the data members to their initial values + */ + LesionData(const SemanticTypes::Lesion& lesion = SemanticTypes::Lesion()); + ~LesionData(); + + SemanticTypes::Lesion GetLesion() const { return m_Lesion; }; + SemanticTypes::ID GetLesionUID() const { return m_Lesion.UID; } + std::string GetLesionName() const { return m_Lesion.name; } + + const std::vector& GetLesionPresence() const { return m_LesionPresence; }; + const std::vector& GetLesionVolume() const { return m_LesionVolume; }; + + void SetLesion(const SemanticTypes::Lesion& lesion); + void SetLesionPresence(const std::vector& lesionPresence); + void SetLesionVolume(const std::vector& lesionVolume); + + private: + + SemanticTypes::Lesion m_Lesion; + std::vector m_LesionPresence; + std::vector m_LesionVolume; + + }; +} // end namespace + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif // MITKLESIONDATA_H diff --git a/Modules/SemanticRelations/include/mitkLesionManager.h b/Modules/SemanticRelations/include/mitkLesionManager.h index 7afab480e1..f637e7eccd 100644 --- a/Modules/SemanticRelations/include/mitkLesionManager.h +++ b/Modules/SemanticRelations/include/mitkLesionManager.h @@ -1,70 +1,80 @@ /*=================================================================== 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 MITKLESIONMANAGER_H #define MITKLESIONMANAGER_H +#include + // semantic relations module +#include "mitkSemanticRelations.h" #include "mitkSemanticTypes.h" -#include +#include "mitkLesionData.h" // mitk core #include /* * @brief Provides helper functions that are needed to work with lesions. * * These functions help to generate new lesions, check for existing lesions or provide functionality * to find existing lesion class types. */ namespace mitk { typedef std::vector LesionClassVector; /** * @brief Generate a new lesion and lesion class with UIDs and the given string as lesion class type. * * @param lesionClassType The lesion class type as string. Default parameter is "". */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::Lesion GenerateNewLesion(const std::string& lesionClassType = ""); /** * @brief Generate a new lesion class with UID and the given string as lesion class type. * * @param lesionClassType The lesion class type as string. Default parameter is "". */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionClass GenerateNewLesionClass(const std::string& lesionClassType = ""); /** * @brief Find and return a whole lesion including its lesion class given a specific lesion UID. * * @param lesionUID The lesion UID as string. * @param allLesions All currently known lesions of a specific case. * * @return The lesion with its UID and the lesion class. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::Lesion GetLesionByUID(const SemanticTypes::ID& lesionUID, const std::vector& allLesions); /** * @brief Find and return the whole lesion class including its UID given a specific lesion class type. * * @param lesionClassType The lesion class type as string. * @param allLesionClasses All currently known lesion classes of a specific case. * * @return The lesion class with its UID and the class type. */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::LesionClass FindExistingLesionClass(const std::string& lesionClassType, const std::vector& allLesionClasses); - + /** + * @brief Generate and store additional lesion data such as lesion presence and lesion volume for each control point. + * + * @param lesionData The lesion data that holds the lesion and will hold the additional lesion data. + * @param caseID The current case ID. + * @param semanticRelations An instance of the semantic relations to retrieve the additional data. + */ + MITKSEMANTICRELATIONS_EXPORT void GenerateAdditionalLesionData(LesionData& lesionData, const SemanticTypes::CaseID& caseID, std::shared_ptr semanticRelations); } // namespace mitk #endif // MITKLESIONMANAGER_H diff --git a/Modules/SemanticRelations/include/mitkRelationStorage.h b/Modules/SemanticRelations/include/mitkRelationStorage.h index 90630cc4b9..48175431a1 100644 --- a/Modules/SemanticRelations/include/mitkRelationStorage.h +++ b/Modules/SemanticRelations/include/mitkRelationStorage.h @@ -1,94 +1,94 @@ /*=================================================================== 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 MITKRELATIONSTORAGE_H #define MITKRELATIONSTORAGE_H // semantic relations module #include "mitkDICOMHelper.h" #include "mitkSemanticTypes.h" // mitk core #include #include namespace mitk { class RelationStorage { public: void SetDataStorage(DataStorage::Pointer dataStorage); std::vector GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID); SemanticTypes::Lesion GetRepresentedLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID); std::vector GetAllSegmentationsOfCase(const SemanticTypes::CaseID& caseID); std::vector mitk::RelationStorage::GetAllSegmentationIDsOfCase(const SemanticTypes::CaseID& caseID); - SemanticTypes::ControlPoint GetControlPointOfData(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& dataNodeID); + SemanticTypes::ControlPoint GetControlPointOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID); std::vector GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID); std::vector GetAllExaminationPeriodsOfCase(const SemanticTypes::CaseID& caseID); SemanticTypes::InformationType GetInformationTypeOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID); std::vector GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID); std::vector GetAllImagesOfCase(const SemanticTypes::CaseID& caseID); std::vector GetAllImageIDsOfCase(const SemanticTypes::CaseID& caseID); std::vector GetAllCaseIDs(); void AddCase(const SemanticTypes::CaseID& caseID); void AddImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageNodeID); void RemoveImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageNodeID); void AddSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationNodeID, const SemanticTypes::ID& parentDataNodeID); void RemoveSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationNodeID); void AddLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); void OverwriteLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); void LinkSegmentationToLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID, const SemanticTypes::Lesion& lesion); void UnlinkSegmentationFromLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID); void RemoveLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); void RemoveLesionClass(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& lesionClassID); void AddControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); void LinkDataToControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& dataNodeID, const SemanticTypes::ControlPoint& controlPoint); void UnlinkDataFromControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& dataNodeID); void RemoveControlPointFromCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); void AddExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod); void AddControlPointToExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod examinationPeriod); void RemoveControlPointFromExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod examinationPeriod); void RemoveExaminationPeriodFromCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod examinationPeriod); void AddInformationTypeToImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID, const SemanticTypes::InformationType informationType); void RemoveInformationTypeFromImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID); void RemoveInformationTypeFromCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType informationType); private: // access the storage and retrieve the case data, stored under the given case ID mitk::PropertyList::Pointer GetStorageData(const SemanticTypes::CaseID& caseID); mitk::SemanticTypes::Lesion GenerateLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& lesionID); DataStorage::Pointer m_DataStorage; }; } // namespace mitk #endif // MITKRELATIONSTORAGE_H diff --git a/Modules/SemanticRelations/include/mitkSemanticRelations.h b/Modules/SemanticRelations/include/mitkSemanticRelations.h index b2924156c6..5b9b285507 100644 --- a/Modules/SemanticRelations/include/mitkSemanticRelations.h +++ b/Modules/SemanticRelations/include/mitkSemanticRelations.h @@ -1,635 +1,638 @@ /*=================================================================== 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 MITKSEMANTICRELATIONS_H #define MITKSEMANTICRELATIONS_H #include // semantic relations module -#include "mitkControlPointManager.h" #include "mitkDICOMHelper.h" #include "mitkISemanticRelationsObservable.h" #include "mitkISemanticRelationsObserver.h" #include "mitkRelationStorage.h" #include "mitkSemanticTypes.h" // mitk core #include #include namespace mitk { /** * @brief The API provides functions to query and manipulate image relations and instances, * that are helpful during follow-up examination, like control-points (time period), * types of the images or lesions that may be visible on multiple images. * * The class is able to generate IDs from given data nodes using DICOM information. * These IDs are used to identify the corresponding instances of a specific case. * The case can also be directly identified by the given case ID. * * In the BlackSwan context the case is identified with the DICOM PatientID. * * In order for most functions to work the case ID has to be defined in the model. If not, * the functions do nothing. */ class MITKSEMANTICRELATIONS_EXPORT SemanticRelations : public ISemanticRelationsObservable { public: SemanticRelations(mitk::DataStorage::Pointer dataStorage); ~SemanticRelations(); using LesionVector = std::vector; using LesionClassVector = std::vector; using ControlPointVector = std::vector; using ExaminationPeriodVector = std::vector; using InformationTypeVector = std::vector; using DataNodeVector = std::vector ; /************************************************************************/ /* functions to implement the observer pattern */ /************************************************************************/ /** * @brief Adds the given concrete observer to the vector that holds all currently registered observer. * If the observer is already registered, it will not be added to the observer vector. * * @param observer The concrete observer to register. */ virtual void AddObserver(ISemanticRelationsObserver* observer) override; /** * @brief Removes the given concrete observer from the vector that holds all currently registered observer. * * @param observer The concrete observer to unregister. */ virtual void RemoveObserver(ISemanticRelationsObserver* observer) override; /************************************************************************/ /* functions to get instances / attributes */ /************************************************************************/ /** * @brief Returns a vector of all lesions that are currently available for the given case. * The lesions may be marked by a segmentation or may be empty - with no connection to a specific image / segmentation of the case data. * If no lesions are stored for the current case, an empty vector is returned. * * @param caseID The current case identifier is defined by the given string. * @return A vector of lesions. */ LesionVector GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID) const; /** * @brief * * */ LesionClassVector GetAllLesionClassesOfCase(const SemanticTypes::CaseID& caseID) const; /** * @brief Returns a vector of all lesions that are valid for the given case, given a specific control point. * * @param caseID The current case identifier is defined by the given string. * @param controlPoint A specific control point which has to be available at a returned (found) lesion: - * Only those lesions are returned for which the image of the associated segmentation is linked to the given control point. - * If the control point instance does not exist, an empty vector is returned. - * @return A vector of control points. + * Only those lesions are returned for which the image of the associated segmentation is linked to the given control point. + * If the control point instance does not exist, an empty vector is returned. + * @return A vector of control points. */ LesionVector GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const; /** * @brief Returns a vector of all lesions that are currently available for the current case and are connected to the given image (via a segmentation). * If no lesions are stored for the current case, an empty vector is returned. If no segmentation nodes are * connected with the image node, no lesions for the specific image will be found and an empty vector is returned. * * @pre The given image data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). * @pre The image node has to have associated segmentation nodes (child nodes) in order to reference a lesion. * * @param imageNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. * @return A vector of lesions. */ LesionVector GetAllLesionsInImage(const DataNode* imageNode) const; /** * @brief Returns the lesion that is defined by the given segmentation data. * * @pre The given segmentation data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given segmentation data node is invalid (==nullptr). * @pre The segmentation data node has to represent a lesion. If not, the retrieved lesion will be empty, which leads to an exception. * @throw SemanticRelationException, if the segmentation does not represent an existing lesion (this can be checked via 'IsRepresentingALesion'). * * @param segmentationNode The segmentation identifier is extracted from the given data node. - * @return The represented lesion. + * @return The represented lesion. */ SemanticTypes::Lesion GetRepresentedLesion(const DataNode* segmentationNode) const; /** * @brief Check if the given segmentation refers to an existing lesion instance. * This function can be used before calling 'GetRepresentedLesion' in order to avoid a possible exception. * * @param segmentationNode The segmentation identifier is extracted from the given data node. * @return True, if the segmentation refers to an existing lesion; false otherwise. */ bool IsRepresentingALesion(const DataNode* segmentationNode) const; /** - * @brief - * + * @brief Check if the given data node exists in the relation storage. + * The function receives the case- and the node-ID from the DICOM tags of the node itself. + * It uses node predicates to decide if the node is an image or a segmentation node and searches + * through the corresponding relations. * + * @param dataNode A data node to check. + * @return True, if the data node exists; false otherwise. */ bool InstanceExists(const DataNode* dataNode) const; /** * @brief Return a vector of all segmentations that are currently available for the given case. * The segmentations may be connected / not connected to a lesion of the case. * If no segmentations are stored for the current case, an empty vector is returned. * * @pre The data storage member has to be valid (!nullptr). * @throw SemanticRelationException, if the data storage member is invalid (==nullptr). * - * @param caseID The current case identifier is defined by the given string. - * @return A vector of data nodes representing segmentations. + * @param caseID The current case identifier is defined by the given string. + * @return A vector of data nodes representing segmentations. */ mitk::SemanticRelations::DataNodeVector GetAllSegmentationsOfCase(const SemanticTypes::CaseID& caseID) const; /** * @brief Return a vector of all segmentations that define the given lesion. These segmentations don't have to be linked to the same image. * If the lesion is not referred to by any segmentation, an empty vector is returned. * * @pre The data storage member has to be valid (!nullptr). * @throw SemanticRelationException, if the data storage member is invalid (==nullptr). * @pre The UID of the lesion has to exist for a lesion instance. * @throw SemanticRelationException, if UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param lesion A Lesion with a UID that identifies the corresponding lesion instance. - * @return A vector of data nodes representing segmentations that define the given lesion. + * @return A vector of data nodes representing segmentations that define the given lesion. */ DataNodeVector GetAllSegmentationsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const; /** * @brief Return a vector of all images that are currently available for the given case. * * @pre The data storage member has to be valid (!nullptr). * @throw SemanticRelationException, if the data storage member is invalid (==nullptr). * - * @param caseID The current case identifier is defined by the given string. - * @return A vector of data nodes representing images. + * @param caseID The current case identifier is defined by the given string. + * @return A vector of data nodes representing images. */ DataNodeVector GetAllImagesOfCase(const SemanticTypes::CaseID& caseID) const; /** * @brief Return a vector of all images that are connected to those segmentations that are linked to the given lesion. * If the lesion is not referred to by any segmentation, an empty vector is returned. * * @pre The UID of the lesion has to exist for a lesion instance. * @throw SemanticRelationException, if UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param lesion A Lesion with a UID that identifies the corresponding lesion instance. - * @return A vector of data nodes representing images on which the lesions are visible. + * @return A vector of data nodes representing images on which the lesions are visible. */ DataNodeVector GetAllImagesOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const; /** * @brief Check if the given lesion instance exists. * This function can be used before calling 'GetAllSegmentationsOfLesion' in order to avoid a possible exception. * This function can be used before calling 'AddLesionInstance' in order to avoid a possible exception. * * @param caseID The current case identifier is defined by the given string. * @param lesion A Lesion with a UID that identifies the corresponding lesion instance. - * @return True, if the lesion instance exists; false otherwise. + * @return True, if the lesion instance exists; false otherwise. */ bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const; /** * @brief Return a vector of all control points that are valid for the given case. * * @param caseID The current case identifier is defined by the given string. * @return A vector of control points. */ ControlPointVector GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID) const; /** * @brief Return a vector of all control points that are valid for the given case, given a specific lesion * * @param caseID The current case identifier is defined by the given string. * @param lesion A specific lesion which has to be available at a returned (found) control point: * Only those control points are returned for which an associated data has a segmentation that references the given lesion. * If the lesion does not exists, an empty vector is returned. * @return A vector of control points. */ ControlPointVector GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const; /** * @brief Return a vector of all control points that are valid for the given case, given a specific information type. * * @param caseID The current case identifier is defined by the given string. * @param informationType A specific information type which has to be available at a returned (found) control point: * Only those control points are returned for which an associated data has the given information type. * If the information type instance does not exists, an empty vector is returned. * @return A vector of control points. */ ControlPointVector GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) const; /** * @brief Return the control point of a data node. * If the data node is not linked to a control point or the data node refers to a non-existing control point, * a control point with an empty UID is returned. * - * @pre The given data node has to be valid (!nullptr). - * @throw SemanticRelationException, if the given data node is invalid (==nullptr). + * @pre The given image data node has to be valid (!nullptr). + * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). * * @param dataNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. * @return The control point of the given data node. */ SemanticTypes::ControlPoint GetControlPointOfData(const DataNode* dataNode) const; /** * @brief Return a vector of all image nodes that link to the given control point. * If the control point is not referred to by any data node, an empty vector is returned. * * @pre The UID of the control point has to exist for a control point instance. * @throw SemanticRelationException, if the UID of the control point does not exist for a control point instance (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param controlPoint A control point with a UID that identifies the corresponding control point instance. * @return A vector of image nodes that link to the given control point. */ DataNodeVector GetAllImagesOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const; /** * @brief Check if the given control point instance exists. * This function can be used before calling 'GetAllDataOfControlPoint' in order to avoid a possible exception. * This function can be used before adding, linking and unlinking control points to avoid a possible exception. * * @param caseID The current case identifier is defined by the given string. * @param controlPoint A control point with a UID that identifies the corresponding control point instance. * @return True, if the control point instance exists; false otherwise. */ bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const; /** * @brief Return a vector of all examination periods nodes that are valid for the given case. * * @param caseID The current case identifier is defined by the given string. * @return A vector of examination periods. */ ExaminationPeriodVector GetAllExaminationPeriodsOfCase(const SemanticTypes::CaseID& caseID) const; /** * @brief Check if the given examination period instance exists. * This function can be used before calling 'AddExaminationPeriod' in order to avoid a possible exception. * * @param caseID The current case identifier is defined by the given string. * @param examinationPeriod An examination period with a UID that identifies the corresponding examination period instance. * @return True, if the examination period instance exists; false otherwise. */ bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) const; /** * @brief Return a vector of all information types that are valid for the given case. * - * @param caseID The current case identifier is defined by the given string. - * @return A vector of information types. + * @param caseID The current case identifier is defined by the given string. + * @return A vector of information types. */ InformationTypeVector GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID) const; /** * @brief Return a vector of all information types that are valid for the given case, given a specific control point. * * @param caseID The current case identifier is defined by the given string. * @param controlPoint A specific control point which has to be available at a returned (found) information type: * Only those information types are returned for which an associated data is linked to the given control point. * If the control point instance does not exist, an empty vector is returned. * @return A vector of information types. */ InformationTypeVector GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const; /** * @brief Return the information type of the given image. * If the image does not contain any information type, an empty information type is returned. * * @pre The given image data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). * * @param imageNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. - * @return The information type of the given data node. + * @return The information type of the given data node. */ SemanticTypes::InformationType GetInformationTypeOfImage(const DataNode* imageNode) const; /** * @brief Return a vector of all image nodes that are defined with the given information type. * * @pre The information type has to exist for the given case (and is therefore used by at least one data node). * @throw SemanticRelationException, if the information type is not used by any data node (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param informationType An information type that identifies the corresponding information type instance. * @return A vector of image nodes that are defined with the given information type. */ DataNodeVector GetAllImagesOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) const; /** * @brief Return a vector of all image nodes that are defined with the given information type and with the given control point. * * @pre The UID of the control point has to exist for a control point instance. * The information type has to exist for the given case (and is therefore used by at least one data node). * @throw SemanticRelationException, if the UID of the control point does not exist for a control point instance (this can be checked via 'InstanceExists') or * if the information type is not used by any data node (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param controlPoint A control point with a UID that identifies the corresponding control point instance. * @param informationType An information type that identifies the corresponding information type instance. * @return A vector of image nodes that are defined with the given information type with the given control point. */ DataNodeVector GetAllSpecificImages(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const; /** * @brief Return a vector of all segmentation nodes that are defined with the given information type and with the given control point. * The function uses the 'GetAllSpecificImages'-function to retrieve the specific images and then searches for the derived nodes (segmentation child nodes). * * @pre The UID of the control point has to exist for a control point instance. * The information type has to exist for the given case (and is therefore used by at least one data node). * @throw SemanticRelationException, if the UID of the control point does not exist for a control point instance (this can be checked via 'InstanceExists') or * if the information type is not used by any data node (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param controlPoint A control point with a UID that identifies the corresponding control point instance. * @param informationType An information type that identifies the corresponding information type instance. * @return A vector of segmentation nodes that are defined with the given information type with the given control point. */ DataNodeVector GetAllSpecificSegmentations(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const; /** * @brief Check if the given information type exists. * This function can be used before calling 'GetAllDataOfInformationType' in order to avoid a possible exception. * * @param caseID The current case identifier is defined by the given string. * @param informationType An information type * @return True, if the information type exists; false otherwise. */ bool InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) const; /** * @brief Return a vector of all CaseIDs that are currently available. * * @return A vector of CaseIDs as strings. */ std::vector GetAllCaseIDs() const; /************************************************************************/ /* functions to add / remove instances / attributes */ /************************************************************************/ /** * @brief Add the given image to the set of already existing images. * The date is extracted from the DICOM data of the image node and is compared to already existing control points in the semantic relations model. * The function tries to find a fitting control point or to extend an already existing control point, if the extracted control point is close to * any other, already existing control point. * Finally, the image is linked to the correct control point. * * @pre The given image data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). * * @param imageNode The current case identifier and node identifier is extracted from the given image data node, which contains DICOM information about the case and the node. */ void AddImage(const DataNode* imageNode); /** * @brief Remove the given image from the set of already existing images. * * @pre The given image data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). * * @param imageNode The current case identifier and node identifier is extracted from the given image data node, which contains DICOM information about the case and the node. */ void RemoveImage(const DataNode* imageNode); /** * @brief Add a newly created lesion to the set of already existing lesions - with no connection to a specific image / segmentation of the case data. * * @pre The UID of the lesion must not already exist for a lesion instance. * @throw SemanticRelationException, it the UID of the lesion already exists for a lesion instance (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param lesion The lesion instance to add. */ void AddLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); /** * @brief Overwrite an already existing lesion instance (this may be useful to overwrite the lesion with a different lesion class). * * @pre The UID of the lesion has to exist for a lesion instance. * @throw SemanticRelationException, if the UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param lesion The lesion instance that overwrites an existing lesion. */ void OverwriteLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); /** * @brief Add a newly created lesion to the set of already existing lesions. The lesion is added and a reference to * the lesion is added to the segmentation data. If the segmentation is already linked to a lesion, the * old linkage is overwritten (this can be checked via 'IsRepresentingALesion'). * * @pre The given segmentation data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given segmentation data node is invalid (==nullptr). * @pre The UID of the lesion must not already exist for a lesion instance. * @throw SemanticRelationException, if the UID of the lesion already exists for a lesion instance (this can be checked via 'InstanceExists'). * * @param segmentationNode The segmentation identifier is extracted from the given data node. The segmentation node has DICOM information from its parent node. * @param lesion The lesion instance to add and link. */ void AddLesionAndLinkSegmentation(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion); /** * @brief Remove the given lesion from the set of already existing lesions. * * @pre The UID of the lesion has to exist for a lesion instance. * @throw SemanticRelationException, if the UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). * @pre The function needs to assure that no segmentation is still representing (linked to) this lesion. * @throw SemanticRelationException, if the lesion instance to remove is still linked to by any segmentation (this can be checked via 'GetAllSegmentationsOfLesion'). * * @param caseID The current case identifier is defined by the given string. * @param lesion The lesion instance to remove. */ void RemoveLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion); /** * @brief Add a segmentation instance to the set of already existing segmentations - with no connection to a specific lesion. * * @param segmentationNode The segmentation identifier is extracted from the given data node. The segmentation node has DICOM information from its parent node. * @param parentNode The node identifier of the parent node is extracted from the given parent data node. */ void AddSegmentation(const DataNode* segmentationNode, const DataNode* parentNode); /** * @brief Link the given segmentation instance to an an already existing lesion instance. If the segmentation is already linked to a lesion instance, the * old linkage is overwritten (this can be checked via 'IsRepresentingALesion'). * * @pre The given segmentation data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given segmentation data node is invalid (==nullptr). * @pre The UID of the lesion has to exist for a lesion instance. * @throw SemanticRelationException, if the UID of the lesion does not exist for a lesion instance (this can be checked via 'InstanceExists'). * * @param segmentationNode The segmentation identifier is extracted from the given data node. The segmentation node has DICOM information from its parent node. * @param lesion The lesion instance to link. */ void LinkSegmentationToLesion(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion); /** * @brief Unlink the given segmentation instance from the linked lesion instance. * The lesion may stay unlinked to any segmentation. * * @pre The given segmentation data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given segmentation data node is invalid (==nullptr). * * @param segmentationNode The segmentation identifier is extracted from the given data node. The segmentation node has DICOM information from its parent node. */ void UnlinkSegmentationFromLesion(const DataNode* segmentationNode); /** * @brief Remove the given segmentation from the set of already existing segmentations. * * @pre The given segmentation data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given segmentation data node is invalid (==nullptr). * * @param segmentationNode The segmentation identifier is extracted from the given data node. The segmentation node has DICOM information from its parent node. */ void RemoveSegmentation(const DataNode* segmentationNode); /** * @brief Set the control point for the given data node. * * @pre The given data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given data node is invalid (==nullptr). */ void SetControlPointOfData(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint); /** * @brief Add a newly created control point to the set of already existing control points. A reference to the control point is added to the given data. * This function combines adding a control point and linking it, since a control point with no associated data is not allowed. * * @pre The given data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given data node is invalid (==nullptr). * @pre The UID of the control point must not already exist for a control point instance. * @throw SemanticRelationException, if the UID of the control point already exists for a control point instance (this can be checked via 'InstanceExists'). * @pre The given control point must not already be contained in an existing control point interval. * @throw SemanticRelationException, if the given control point is already contained in an existing control point interval (this can be checked via 'CheckContainingControlPoint'). * @pre The given control point must contain the date of the given data node (if parameter 'checkConsistence = true'). * @throw SemanticRelationException, if the given control point does not contain the date of the given data node and 'checkConsistence = true' (this can be checked via 'ControlPointManager::InsideControlPoint'). * * @param dataNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. * @param controlPoint The control point instance to add. For a newly added control point always has "startDate = endDate". * @param checkConsistence If true, the function checks, whether the date of the data node actually lies inside the control point to link. */ void AddControlPointAndLinkData(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence = true); /** * @brief Link the given data to an already existing control point. * * @pre The given data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given data node is invalid (==nullptr). * @pre The UID of the control point has to exist for a control point instance. * @throw SemanticRelationException, if the UID of the control point does not exists for a control point instance (this can be checked via 'InstanceExists'). * @pre The given control point must contain the date of the given data node (if parameter 'checkConsistence = true'). * @throw SemanticRelationException, if the given control point does not contain the date of the given data node and 'checkConsistence = true' (this can be checked via 'ControlPointManager::InsideControlPoint'). * * @param dataNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. * @param controlPoint The control point instance to link. * @param checkConsistence If true, the function checks, whether the date of the data node actually lies inside the control point to link. */ void LinkDataToControlPoint(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence = true); /** * @brief Unlink the given image from the linked control point. * If data is unlinked from a control point, the function needs to check whether the control point is still linked to any other data: * - if not, the control point instance will be removed (has to be removed since a control point with no associated data is not allowed). * - if so, the function has to make sure that the control point instance is shortened to its minimum time period (e.g. moving the end point to an earlier date). * * @param dataNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. */ void UnlinkDataFromControlPoint(const DataNode* dataNode); /** * @brief Add an examination period instance to the set of already existing examination periods - with no connection to a specific control point. * * @pre The UID of the examination period must not already exist for an examination period instance. * @throw SemanticRelationException, if the UID of the examination period already exists for a examination period instance (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param examinationPeriod The examination period to add. */ void AddExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod); /** * @brief Add a control point to the vector of control point UIDs of an existing examination period. * * @pre The UID of the control point has to exist for a control point instance. * @throw SemanticRelationException, if the UID of the control point does not exists for a control point instance (this can be checked via 'InstanceExists'). * @pre The UID of the examination period must not already exist for an examination period instance. * @throw SemanticRelationException, if the UID of the examination period already exists for a examination period instance (this can be checked via 'InstanceExists'). * * @param caseID The current case identifier is defined by the given string. * @param controlPoint The control point instance to add to the examination period. * @param examinationPeriod The examination period to which the control point should be added. */ void AddControlPointToExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod& examinationPeriod); /** * @brief Set (and possibly overwrite) the information type of the given image. * An already associated information type might be removed if is not referenced by any other image: * * @pre The given image data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). * @post If the information type instance did not exist before, it is now added. * * @param imageNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. * @param informationType An information type that identifies the corresponding information type instance. */ void SetInformationType(const DataNode* imageNode, const SemanticTypes::InformationType& informationType); /** * @brief Set the information type of the given image. * * @pre The given image data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). * @post If the information type instance did not exist before, it is now added. * * @param imageNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. * @param informationType An information type that identifies the corresponding information type instance. */ void AddInformationTypeToImage(const DataNode* imageNode, const SemanticTypes::InformationType& informationType); /** * @brief Remove the information type of the given image. * If the information type is removed, the function needs to check whether the information type is referenced by any other image: * - if not, the information type instance can be removed (has to be removed since an information type with no associated image is not allowed). * - if so, the information type is just removed from the given image. * * @pre The given image data node has to be valid (!nullptr). * @throw SemanticRelationException, if the given image data node is invalid (==nullptr). * * @param imageNode The current case identifier is extracted from the given data node, which contains DICOM information about the case. */ void RemoveInformationTypeFromImage(const DataNode* imageNode); private: // the relation storage serves as a storage accessor and can be sub-classed for custom demands std::shared_ptr m_RelationStorage; DataStorage::Pointer m_DataStorage; /** * @brief A vector that stores the currently registered observer of this observable subject. */ static std::vector m_ObserverVector; /** * @brief The SemanticRelations, as an example of an observable subject, notifies (updates) the observer with a given case ID. * The view's caseID was set before in the GUI. The parts of the view that observe changes in the semantic relations are only updated, * if the given case ID is equal to the observer's current caseID and thus the observer currently shows the semantic information of the given case. * * @param caseID The caseID that identifies the currently active patient / case. */ virtual void NotifyObserver(const mitk::SemanticTypes::CaseID& caseID) const override; /** * @brief Determine if the given control point contains images, which are connected to segmentations that represent the given lesion. * If the lesion or the control point are not correctly stored, the function returns false. * * @param caseID The current case identifier is defined by the given string. * @param lesion A Lesion with a UID that identifies the corresponding lesion instance. * @param controlPoint A control point with a UID that identifies the corresponding control point instance. * * @return True, if the given control point contains data that is related to the given lesion; false otherwise. */ bool ControlPointContainsLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ControlPoint& controlPoint) const; /** * @brief Determine if the given control point contains images, which refer to the given information type. * If the information type or the control point are not correctly stored, the function returns false. * * @param caseID The current case identifier is defined by the given string. * @param informationType An information type that identifies the corresponding information type instance. * @param controlPoint A control point with a UID that identifies the corresponding control point instance. * * @return True, if the given control point contains data that is related to the given information type; false otherwise. */ bool ControlPointContainsInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType, const SemanticTypes::ControlPoint& controlPoint) const; /** * @brief Remove all control points from the storage that are not referenced by any image anymore. * This might happen if an image has been removed (and unlinked from the corresponding control point) * or if the user sets a new control point for an image manually in the GUI. * * @param caseID The current case identifier is defined by the given string. */ void ClearControlPoints(const SemanticTypes::CaseID& caseID); }; } // namespace mitk #endif // MITKSEMANTICRELATIONS_H diff --git a/Modules/SemanticRelations/src/mitkControlPointManager.cpp b/Modules/SemanticRelations/src/mitkControlPointManager.cpp index b2ad849b09..740c35b5cf 100644 --- a/Modules/SemanticRelations/src/mitkControlPointManager.cpp +++ b/Modules/SemanticRelations/src/mitkControlPointManager.cpp @@ -1,179 +1,180 @@ /*=================================================================== 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. ===================================================================*/ // semantic relations module #include "mitkControlPointManager.h" #include "mitkDICOMHelper.h" #include "mitkUIDGeneratorBoost.h" // mitk core #include double CalculateDistanceInDays(const mitk::SemanticTypes::ControlPoint& leftControlPoint, const mitk::SemanticTypes::ControlPoint& rightControlPoint); -mitk::SemanticTypes::ControlPoint mitk::GenerateControlPoint(const mitk::DataNode* datanode) +mitk::SemanticTypes::ControlPoint mitk::GenerateControlPoint(const DataNode* datanode) { SemanticTypes::ControlPoint controlPoint = GetDICOMDateFromDataNode(datanode); controlPoint.UID = UIDGeneratorBoost::GenerateUID(); return controlPoint; } mitk::SemanticTypes::ControlPoint mitk::GetControlPointByUID(const SemanticTypes::ID& controlPointUID, const std::vector& allControlPoints) { auto lambda = [&controlPointUID](const SemanticTypes::ControlPoint& currentControlPoint) { return currentControlPoint.UID == controlPointUID; }; const auto existingControlPoint = std::find_if(allControlPoints.begin(), allControlPoints.end(), lambda); mitk::SemanticTypes::ControlPoint controlPoint; if (existingControlPoint != allControlPoints.end()) { controlPoint = *existingControlPoint; } return controlPoint; } std::string mitk::GetControlPointAsString(const SemanticTypes::ControlPoint& controlPoint) { std::stringstream controlPointAsString; controlPointAsString << std::to_string(controlPoint.year) << "-" << std::setfill('0') << std::setw(2) << std::to_string(controlPoint.month) << "-" << std::setfill('0') << std::setw(2) << std::to_string(controlPoint.day); return controlPointAsString.str(); } mitk::SemanticTypes::ControlPoint mitk::FindExistingControlPoint(const SemanticTypes::ControlPoint& controlPoint, const std::vector& allControlPoints) { for (const auto& currentControlPoint : allControlPoints) { if (controlPoint == currentControlPoint) { return currentControlPoint; } } return SemanticTypes::ControlPoint(); } + mitk::SemanticTypes::ControlPoint mitk::FindClosestControlPoint(const SemanticTypes::ControlPoint& controlPoint, std::vector& allControlPoints) { if (allControlPoints.empty()) { return SemanticTypes::ControlPoint(); } // sort the vector of control points for easier lookup std::sort(allControlPoints.begin(), allControlPoints.end()); // new control point does not match an existing control point // check if the control point is close to an already existing control point std::vector::const_iterator it; for (it = allControlPoints.begin(); it != allControlPoints.end(); ++it) { if (controlPoint < *it) { break; } } SemanticTypes::ControlPoint nextControlPoint; SemanticTypes::ControlPoint previousControlPoint; if (it == allControlPoints.begin()) { // new date is smaller ("older") than the smallest already existing control point nextControlPoint = *it; } else if (it != allControlPoints.end()) { // new date is greater ("newer") than an already existing control point, // but smaller ("older") than another already existing control point nextControlPoint = *it; previousControlPoint = *(--it); } else { // new date is greater ("newer") than the greatest already existing control point previousControlPoint = *(--it); } // test distance to next and previous time period double distanceToNextExaminationPeriod = CalculateDistanceInDays(nextControlPoint, controlPoint); double distanceToPreviousExaminationPeriod = CalculateDistanceInDays(previousControlPoint, controlPoint); SemanticTypes::ControlPoint closestControlPoint; double closestDistanceInDays = 0.0; if (distanceToNextExaminationPeriod < distanceToPreviousExaminationPeriod) { // control point is closer to the next control point closestControlPoint = nextControlPoint; closestDistanceInDays = distanceToNextExaminationPeriod; } else { // control point is closer to the previous control point closestControlPoint = previousControlPoint; closestDistanceInDays = distanceToPreviousExaminationPeriod; } double THRESHOLD_DISTANCE_IN_DAYS = 30.0; if (std::abs(closestDistanceInDays) < THRESHOLD_DISTANCE_IN_DAYS) { return closestControlPoint; } return SemanticTypes::ControlPoint(); } mitk::SemanticTypes::ExaminationPeriod mitk::FindExaminationPeriod(const SemanticTypes::ControlPoint& controlPoint, const std::vector& allExaminationPeriods) { for (const auto& examinationPeriod : allExaminationPeriods) { for (const auto& UID : examinationPeriod.controlPointIDs) { if (controlPoint.UID == UID) { return examinationPeriod; } } } return SemanticTypes::ExaminationPeriod(); } double CalculateDistanceInDays(const mitk::SemanticTypes::ControlPoint& leftControlPoint, const mitk::SemanticTypes::ControlPoint& rightControlPoint) { std::tm leftTimeStructure = { 0, 0, 0, leftControlPoint.day, leftControlPoint.month - 1, leftControlPoint.year - 1900 }; std::tm rightTimeStructure = { 0, 0, 0, rightControlPoint.day, rightControlPoint.month - 1, rightControlPoint.year - 1900 }; time_t leftTime = mktime(&leftTimeStructure); time_t rightTime = mktime(&rightTimeStructure); if (leftTime == -1 || rightTime == -1) { // date is not initialized, no difference can be computed return std::numeric_limits::max(); } // compute distance here double secondsPerDay = 60 * 60 * 24; double timeDifferenceInDays = std::difftime(leftTime, rightTime) / secondsPerDay; return timeDifferenceInDays; } diff --git a/Modules/SemanticRelations/src/mitkLesionData.cpp b/Modules/SemanticRelations/src/mitkLesionData.cpp new file mode 100644 index 0000000000..172b364f65 --- /dev/null +++ b/Modules/SemanticRelations/src/mitkLesionData.cpp @@ -0,0 +1,43 @@ +/*=================================================================== + +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. + +===================================================================*/ + +// semantic relations module +#include "mitkLesionData.h" + +mitk::LesionData::LesionData(const SemanticTypes::Lesion& lesion/* = SemanticTypes::Lesion()*/) +{ + m_Lesion = lesion; +} + +mitk::LesionData::~LesionData() +{ + // nothing here +} + +void mitk::LesionData::SetLesion(const SemanticTypes::Lesion& lesion) +{ + m_Lesion = lesion; +} + +void mitk::LesionData::SetLesionPresence(const std::vector& lesionPresence) +{ + m_LesionPresence = lesionPresence; +} + +void mitk::LesionData::SetLesionVolume(const std::vector& lesionVolume) +{ + m_LesionVolume = lesionVolume; +} diff --git a/Modules/SemanticRelations/src/mitkLesionManager.cpp b/Modules/SemanticRelations/src/mitkLesionManager.cpp index a98c5797f5..507812262d 100644 --- a/Modules/SemanticRelations/src/mitkLesionManager.cpp +++ b/Modules/SemanticRelations/src/mitkLesionManager.cpp @@ -1,83 +1,144 @@ /*=================================================================== 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. ===================================================================*/ // semantic relations module #include "mitkLesionManager.h" -#include +#include "mitkSemanticRelationException.h" +#include "mitkUIDGeneratorBoost.h" -// mitk core -#include -#include +bool GetLesionPresence(const mitk::SemanticTypes::CaseID& caseID, std::shared_ptr semanticRelations, const mitk::SemanticTypes::Lesion& lesion, const mitk::SemanticTypes::ControlPoint& controlPoint); -// c++ -#include +double GetLesionVolume(const mitk::SemanticTypes::CaseID& caseID, std::shared_ptr semanticRelations, const mitk::SemanticTypes::Lesion& lesion, const mitk::SemanticTypes::ControlPoint& controlPoint); mitk::SemanticTypes::Lesion mitk::GenerateNewLesion(const std::string& lesionClassType/* = ""*/) { mitk::SemanticTypes::Lesion lesion; lesion.UID = mitk::UIDGeneratorBoost::GenerateUID(); - lesion.name = lesion.UID; + lesion.name = "New lesion"; lesion.lesionClass = mitk::SemanticTypes::LesionClass(); lesion.lesionClass.UID = mitk::UIDGeneratorBoost::GenerateUID(); lesion.lesionClass.classType = lesionClassType; return lesion; } mitk::SemanticTypes::LesionClass mitk::GenerateNewLesionClass(const std::string& lesionClassType/* = ""*/) { mitk::SemanticTypes::LesionClass lesionClass; lesionClass.UID = mitk::UIDGeneratorBoost::GenerateUID(); lesionClass.classType = lesionClassType; return lesionClass; } mitk::SemanticTypes::Lesion mitk::GetLesionByUID(const SemanticTypes::ID& lesionUID, const std::vector& allLesions) { auto lambda = [&lesionUID](const SemanticTypes::Lesion& currentLesion) { return currentLesion.UID == lesionUID; }; const auto existingLesion = std::find_if(allLesions.begin(), allLesions.end(), lambda); mitk::SemanticTypes::Lesion lesion; if (existingLesion != allLesions.end()) { lesion = *existingLesion; } return lesion; } mitk::SemanticTypes::LesionClass mitk::FindExistingLesionClass(const std::string& lesionClassType, const std::vector& allLesionClasses) { auto lambda = [&lesionClassType](const SemanticTypes::LesionClass& currentLesionClass) { return currentLesionClass.classType == lesionClassType; }; const auto existingLesionClass = std::find_if(allLesionClasses.begin(), allLesionClasses.end(), lambda); mitk::SemanticTypes::LesionClass lesionClass; if (existingLesionClass != allLesionClasses.end()) { lesionClass = *existingLesionClass; } return lesionClass; } + +void mitk::GenerateAdditionalLesionData(LesionData& lesionData, const SemanticTypes::CaseID& caseID, std::shared_ptr semanticRelations) +{ + std::vector lesionPresence; + std::vector lesionVolume; + SemanticTypes::Lesion lesion = lesionData.GetLesion(); + bool presence = false; + double volume = 0.0; + try + { + std::vector controlPoints = semanticRelations->GetAllControlPointsOfCase(caseID); + for (const auto& controlPoint : controlPoints) + { + presence = GetLesionPresence(caseID, semanticRelations, lesion, controlPoint); + lesionPresence.push_back(presence); + + volume = GetLesionVolume(caseID, semanticRelations, lesion, controlPoint); + lesionVolume.push_back(volume); + } + } + catch (const mitk::SemanticRelationException&) + { + return; + } + + lesionData.SetLesionPresence(lesionPresence); + lesionData.SetLesionVolume(lesionVolume); +} + +bool GetLesionPresence(const mitk::SemanticTypes::CaseID& caseID, std::shared_ptr semanticRelations, const mitk::SemanticTypes::Lesion& lesion, const mitk::SemanticTypes::ControlPoint& controllPoint) +{ + try + { + mitk::SemanticRelations::DataNodeVector allImagesOfLesion = semanticRelations->GetAllImagesOfLesion(caseID, lesion); + for (const auto& image : allImagesOfLesion) + { + auto imageControlPoint = semanticRelations->GetControlPointOfData(image); + if (imageControlPoint == controllPoint) + { + return true; + } + } + } + catch (const mitk::SemanticRelationException&) + { + return false; + } + + return false; +} + +double GetLesionVolume(const mitk::SemanticTypes::CaseID& caseID, std::shared_ptr semanticRelations, const mitk::SemanticTypes::Lesion& lesion, const mitk::SemanticTypes::ControlPoint& controlPoint) +{ + bool presence = GetLesionPresence(caseID, semanticRelations, lesion, controlPoint); + if (presence) + { + return 1.0; + } + else + { + return 0.0; + } +} diff --git a/Modules/SemanticRelations/src/mitkRelationStorage.cpp b/Modules/SemanticRelations/src/mitkRelationStorage.cpp index bdc4dc9fb5..b07eeaeb79 100644 --- a/Modules/SemanticRelations/src/mitkRelationStorage.cpp +++ b/Modules/SemanticRelations/src/mitkRelationStorage.cpp @@ -1,1359 +1,1359 @@ /*=================================================================== 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. ===================================================================*/ // semantic relations module #include "mitkRelationStorage.h" #include "mitkNodePredicates.h" // multi label module #include // mitk core #include #include // c++ #include #include void mitk::RelationStorage::SetDataStorage(DataStorage::Pointer dataStorage) { if (m_DataStorage != dataStorage) { // set the new data storage m_DataStorage = dataStorage; } } std::vector mitk::RelationStorage::GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return std::vector(); } // retrieve a vector property that contains the valid lesion-IDs for the current case mitk::VectorProperty* vectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); if (nullptr == vectorProperty) { MITK_INFO << "Could not find any lesion in the storage."; return std::vector(); } std::vector vectorValue = vectorProperty->GetValue(); std::vector allLesionsOfCase; for (const auto& lesionID : vectorValue) { SemanticTypes::Lesion generatedLesion = GenerateLesion(caseID, lesionID); if (!generatedLesion.UID.empty()) { allLesionsOfCase.push_back(generatedLesion); } } return allLesionsOfCase; } mitk::SemanticTypes::Lesion mitk::RelationStorage::GetRepresentedLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return SemanticTypes::Lesion(); } // retrieve a vector property that contains the referenced ID of a segmentation (0. image ID 1. lesion ID) mitk::VectorProperty* segmentationVectorProperty = dynamic_cast*>(propertyList->GetProperty(segmentationID)); if (nullptr == segmentationVectorProperty) { MITK_INFO << "Could not find the segmentation node " << segmentationID << " in the storage."; return SemanticTypes::Lesion(); } std::vector segmentationVectorValue = segmentationVectorProperty->GetValue(); // the lesion ID of a segmentation is the second value in the vector if (segmentationVectorValue.size() != 2) { MITK_INFO << "Incorrect segmentation storage. Not two (2) IDs stored."; return SemanticTypes::Lesion(); } else { std::string lesionID = segmentationVectorValue[1]; return GenerateLesion(caseID, lesionID); } } std::vector mitk::RelationStorage::GetAllSegmentationsOfCase(const SemanticTypes::CaseID& caseID) { if (m_DataStorage.IsNull()) { MITK_INFO << "No valid data storage found in the mitkPersistenceService-class. Segmentations of the current case can not be retrieved."; return std::vector(); } std::vector allSegmentationIDsOfCase = GetAllSegmentationIDsOfCase(caseID); std::vector allSegmentationsOfCase; // get all segmentation nodes of the current data storage // only those nodes are respected, that are currently held in the data storage DataStorage::SetOfObjects::ConstPointer segmentationNodes = m_DataStorage->GetSubset(NodePredicates::GetSegmentationPredicate()); for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) { DataNode* segmentationNode = it->Value(); try { // find the corresponding segmentation node for the given segmentation ID std::string nodeCaseID = GetCaseIDFromDataNode(segmentationNode); std::string nodeSegmentationID = GetIDFromDataNode(segmentationNode); if (nodeCaseID == caseID && (std::find(allSegmentationIDsOfCase.begin(), allSegmentationIDsOfCase.end(), nodeSegmentationID) != allSegmentationIDsOfCase.end())) { // found current image node in the storage, add it to the return vector allSegmentationsOfCase.push_back(segmentationNode); } } catch (const std::exception&) { // found a segmentation node that is not stored in the semantic relations // this segmentation node does not have any DICOM information --> exception thrown // continue with the next segmentation to compare IDs continue; } } return allSegmentationsOfCase; } std::vector mitk::RelationStorage::GetAllSegmentationIDsOfCase(const SemanticTypes::CaseID& caseID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return std::vector(); } // retrieve a vector property that contains the valid segmentation-IDs for the current case mitk::VectorProperty* allSegmentationsVectorProperty = dynamic_cast*>(propertyList->GetProperty("segmentations")); if (nullptr == allSegmentationsVectorProperty) { MITK_INFO << "Could not find any segmentation in the storage."; return std::vector(); } return allSegmentationsVectorProperty->GetValue(); } -mitk::SemanticTypes::ControlPoint mitk::RelationStorage::GetControlPointOfData(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& dataNodeID) +mitk::SemanticTypes::ControlPoint mitk::RelationStorage::GetControlPointOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return SemanticTypes::ControlPoint(); } - // retrieve a vector property that contains the information type and the referenced ID of a data node (0. information type 1. control point ID) - mitk::VectorProperty* dataNodeVectorProperty = dynamic_cast*>(propertyList->GetProperty(dataNodeID)); + // retrieve a vector property that contains the information type and the referenced ID of a control point (0. information type 1. control point ID) + mitk::VectorProperty* dataNodeVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); if (nullptr == dataNodeVectorProperty) { - MITK_INFO << "Could not find the data node " << dataNodeID << " in the storage."; + MITK_INFO << "Could not find the data node " << imageID << " in the storage."; return SemanticTypes::ControlPoint(); } std::vector dataNodeVectorValue = dataNodeVectorProperty->GetValue(); SemanticTypes::ControlPoint controlPoint; // an image node has to have exactly two values (the information type and the ID of the control point) if (dataNodeVectorValue.size() != 2) { MITK_INFO << "Incorrect data storage. Not two (2) values stored."; return SemanticTypes::ControlPoint(); } else { // the second value of the data node vector is the ID of the referenced control point std::string controlPointID = dataNodeVectorValue[1]; // retrieve a vector property that contains the integer values of the date of a control point (0. year 1. month 2. day) mitk::VectorProperty* controlPointVectorProperty = dynamic_cast*>(propertyList->GetProperty(controlPointID)); if (nullptr == controlPointVectorProperty) { MITK_INFO << "Could not find the control point " << controlPointID << " in the storage."; return SemanticTypes::ControlPoint(); } std::vector controlPointVectorValue = controlPointVectorProperty->GetValue(); // a control point has to have exactly three integer values (year, month and day) if (controlPointVectorValue.size() != 3) { MITK_INFO << "Incorrect control point storage. Not three (3) values of the date are stored."; return SemanticTypes::ControlPoint(); } else { // set the values of the control point controlPoint.UID = controlPointID; controlPoint.year = controlPointVectorValue[0]; controlPoint.month = controlPointVectorValue[1]; controlPoint.day = controlPointVectorValue[2]; } } return controlPoint; } std::vector mitk::RelationStorage::GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return std::vector(); } // retrieve a vector property that contains the valid control point-IDs for the current case mitk::VectorProperty* vectorProperty = dynamic_cast*>(propertyList->GetProperty("controlpoints")); if (nullptr == vectorProperty) { MITK_INFO << "Could not find any control points in the storage."; return std::vector(); } std::vector vectorValue = vectorProperty->GetValue(); std::vector allControlPoints; for (const auto& controlPointID : vectorValue) { // retrieve a vector property that contains the integer values of the date of a control point (0. year 1. month 2. day) mitk::VectorProperty* controlPointVectorProperty = dynamic_cast*>(propertyList->GetProperty(controlPointID)); if (nullptr == controlPointVectorProperty) { MITK_INFO << "Could not find the control point " << controlPointID << " in the storage."; continue; } std::vector controlPointVectorValue = controlPointVectorProperty->GetValue(); // a control point has to have exactly three integer values (year, month and day) if (controlPointVectorValue.size() != 3) { MITK_INFO << "Incorrect control point storage. Not three (3) values of the date are stored."; continue; } else { // set the values of the control point SemanticTypes::ControlPoint generatedControlPoint; generatedControlPoint.UID = controlPointID; generatedControlPoint.year = controlPointVectorValue[0]; generatedControlPoint.month = controlPointVectorValue[1]; generatedControlPoint.day = controlPointVectorValue[2]; allControlPoints.push_back(generatedControlPoint); } } return allControlPoints; } std::vector mitk::RelationStorage::GetAllExaminationPeriodsOfCase(const SemanticTypes::CaseID& caseID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return std::vector(); } // retrieve a vector property that contains the valid examination period UIDs for the current case mitk::VectorProperty::Pointer vectorProperty = dynamic_cast*>(propertyList->GetProperty("examinationperiods")); if (nullptr == vectorProperty) { MITK_INFO << "Could not find any examination periods in the storage."; return std::vector(); } std::vector vectorValue = vectorProperty->GetValue(); std::vector allExaminationPeriods; for (const auto& examinationPeriodID : vectorValue) { // retrieve a vector property that contains the represented control point-IDs mitk::VectorProperty::Pointer examinationPeriodVectorProperty = dynamic_cast*>(propertyList->GetProperty(examinationPeriodID)); if (nullptr == examinationPeriodVectorProperty) { MITK_INFO << "Could not find the examination period " << examinationPeriodID << " in the storage."; continue; } std::vector examinationPeriodVectorValue = examinationPeriodVectorProperty->GetValue(); /* // an examination period has an arbitrary number of control points (at least one) if (examinationPeriodVectorValue.size() < 1) { MITK_INFO << "Incorrect examination period storage. At least one (1) control point ID has to be stored."; continue; } else { */ // set the values of the control point SemanticTypes::ExaminationPeriod generatedExaminationPeriod; generatedExaminationPeriod.UID = examinationPeriodID; for (const auto& controlpointID : examinationPeriodVectorValue) { generatedExaminationPeriod.controlPointIDs.push_back(controlpointID); } allExaminationPeriods.push_back(generatedExaminationPeriod); //} } return allExaminationPeriods; } mitk::SemanticTypes::InformationType mitk::RelationStorage::GetInformationTypeOfImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return SemanticTypes::InformationType(); } // retrieve a vector property that contains the information type and the referenced ID of an image data node (0. information type 1. control point ID) mitk::VectorProperty* dataNodeVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); if (nullptr == dataNodeVectorProperty) { MITK_INFO << "Could not find the image " << imageID << " in the storage."; return SemanticTypes::InformationType(); } std::vector dataNodeVectorValue = dataNodeVectorProperty->GetValue(); // an image node has to have exactly two values (the information type and the ID of the control point) if (dataNodeVectorValue.size() != 2) { MITK_INFO << "Incorrect data storage. Not two (2) values stored."; return SemanticTypes::InformationType(); } else { // the first value of the data node vector is the information type return dataNodeVectorValue[0]; } } std::vector mitk::RelationStorage::GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return std::vector(); } // retrieve a vector property that contains the valid information types of the current case mitk::VectorProperty* informationTypeVectorProperty = dynamic_cast*>(propertyList->GetProperty("informationtypes")); if (nullptr == informationTypeVectorProperty) { MITK_INFO << "Could not find any information types in the storage."; return std::vector(); } return informationTypeVectorProperty->GetValue(); } std::vector mitk::RelationStorage::GetAllImagesOfCase(const SemanticTypes::CaseID& caseID) { if (m_DataStorage.IsNull()) { MITK_INFO << "No valid data storage found in the mitkPersistenceService-class. Images of the current case can not be retrieved."; return std::vector(); } std::vector allImageIDsOfCase = GetAllImageIDsOfCase(caseID); std::vector allImagesOfCase; // get all image nodes of the current data storage // only those nodes are respected, that are currently held in the data storage DataStorage::SetOfObjects::ConstPointer imageNodes = m_DataStorage->GetSubset(NodePredicates::GetImagePredicate()); for (auto it = imageNodes->Begin(); it != imageNodes->End(); ++it) { DataNode* imageNode = it->Value(); // find the corresponding image node for the given segmentation ID std::string nodeCaseID = GetCaseIDFromDataNode(imageNode); std::string nodeImageID = GetIDFromDataNode(imageNode); if (nodeCaseID == caseID && (std::find(allImageIDsOfCase.begin(), allImageIDsOfCase.end(), nodeImageID) != allImageIDsOfCase.end())) { // found current image node in the storage, add it to the return vector allImagesOfCase.push_back(imageNode); } } return allImagesOfCase; } std::vector mitk::RelationStorage::GetAllImageIDsOfCase(const SemanticTypes::CaseID& caseID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return std::vector(); } // retrieve a vector property that contains the valid image-IDs of the current case mitk::VectorProperty* allImagesVectorProperty = dynamic_cast*>(propertyList->GetProperty("images")); if (nullptr == allImagesVectorProperty) { MITK_INFO << "Could not find any image in the storage."; return std::vector(); } return allImagesVectorProperty->GetValue(); } std::vector mitk::RelationStorage::GetAllCaseIDs() { PERSISTENCE_GET_SERVICE_MACRO if (nullptr == persistenceService) { MITK_INFO << "Persistence service could not be loaded"; return std::vector(); } // the property list is valid for a certain scenario and contains all the case IDs of the radiological user's MITK session std::string listIdentifier = "caseIDs"; mitk::PropertyList::Pointer propertyList = persistenceService->GetPropertyList(listIdentifier); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << listIdentifier << " for the current MITK workbench / session."; return std::vector(); } // retrieve a vector property that contains all case IDs mitk::VectorProperty* caseIDsVectorProperty = dynamic_cast*>(propertyList->GetProperty(listIdentifier)); if (nullptr == caseIDsVectorProperty) { MITK_INFO << "Could not find the property " << listIdentifier << " for the " << listIdentifier << " property list."; return std::vector(); } return caseIDsVectorProperty->GetValue(); } void mitk::RelationStorage::AddCase(const SemanticTypes::CaseID& caseID) { PERSISTENCE_GET_SERVICE_MACRO if (nullptr == persistenceService) { MITK_INFO << "Persistence service could not be loaded"; return; } // the property list is valid for a certain scenario and contains all the case IDs of the radiological user's MITK session std::string listIdentifier = "caseIDs"; mitk::PropertyList::Pointer propertyList = persistenceService->GetPropertyList(listIdentifier); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << listIdentifier << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains all case IDs mitk::VectorProperty::Pointer caseIDsVectorProperty = dynamic_cast*>(propertyList->GetProperty(listIdentifier)); std::vector caseIDsVectorValue; if (nullptr == caseIDsVectorProperty) { caseIDsVectorProperty = mitk::VectorProperty::New(); } else { caseIDsVectorValue = caseIDsVectorProperty->GetValue(); } auto existingCase = std::find(caseIDsVectorValue.begin(), caseIDsVectorValue.end(), caseID); if (existingCase != caseIDsVectorValue.end()) { return; } else { // add case to the "caseIDs" property list caseIDsVectorValue.push_back(caseID); caseIDsVectorProperty->SetValue(caseIDsVectorValue); propertyList->SetProperty(listIdentifier, caseIDsVectorProperty); } } void mitk::RelationStorage::AddImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageNodeID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid image-IDs for the current case mitk::VectorProperty::Pointer allImagesVectorProperty = dynamic_cast*>(propertyList->GetProperty("images")); std::vector allImagesIDs; if (nullptr == allImagesVectorProperty) { allImagesVectorProperty = mitk::VectorProperty::New(); } else { allImagesIDs = allImagesVectorProperty->GetValue(); } auto existingImage = std::find(allImagesIDs.begin(), allImagesIDs.end(), imageNodeID); if (existingImage != allImagesIDs.end()) { return; } else { // add image to the "images" property list allImagesIDs.push_back(imageNodeID); allImagesVectorProperty->SetValue(allImagesIDs); propertyList->SetProperty("images", allImagesVectorProperty); // add the image itself mitk::VectorProperty::Pointer imageNodeVectorProperty = mitk::VectorProperty::New(); // an image node has to have exactly two values (the information type and the ID of the control point) std::vector imageNodeVectorValue(2); imageNodeVectorProperty->SetValue(imageNodeVectorValue); propertyList->SetProperty(imageNodeID, imageNodeVectorProperty); } } void mitk::RelationStorage::RemoveImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageNodeID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid image-IDs for the current case mitk::VectorProperty::Pointer allImagesVectorProperty = dynamic_cast*>(propertyList->GetProperty("images")); if (nullptr == allImagesVectorProperty) { MITK_INFO << "Could not find any images in the storage."; return; } // remove the image reference from the list of all images of the current case std::vector allImagesIDs = allImagesVectorProperty->GetValue(); allImagesIDs.erase(std::remove(allImagesIDs.begin(), allImagesIDs.end(), imageNodeID), allImagesIDs.end()); if (allImagesIDs.size() == 0) { // no more images stored -> remove the images property list propertyList->DeleteProperty("images"); } else { // or store the modified vector value allImagesVectorProperty->SetValue(allImagesIDs); } // remove the image instance itself propertyList->DeleteProperty(imageNodeID); } void mitk::RelationStorage::AddSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationNodeID, const SemanticTypes::ID& parentNodeID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid segmentation-IDs for the current case mitk::VectorProperty::Pointer allSegmentationsVectorProperty = dynamic_cast*>(propertyList->GetProperty("segmentations")); std::vector allSegmentationsIDs; if (nullptr == allSegmentationsVectorProperty) { allSegmentationsVectorProperty = mitk::VectorProperty::New(); } else { allSegmentationsIDs = allSegmentationsVectorProperty->GetValue(); } auto existingImage = std::find(allSegmentationsIDs.begin(), allSegmentationsIDs.end(), segmentationNodeID); if (existingImage != allSegmentationsIDs.end()) { return; } else { // add segmentation to the "segmentations" property list allSegmentationsIDs.push_back(segmentationNodeID); allSegmentationsVectorProperty->SetValue(allSegmentationsIDs); propertyList->SetProperty("segmentations", allSegmentationsVectorProperty); // add the segmentation itself mitk::VectorProperty::Pointer segmentationNodeVectorProperty = mitk::VectorProperty::New(); // a segmentation node has to have exactly two values (the ID of the referenced image and the ID of the referenced lesion) std::vector segmentationNodeVectorValue(2); segmentationNodeVectorValue[0] = parentNodeID; segmentationNodeVectorProperty->SetValue(segmentationNodeVectorValue); propertyList->SetProperty(segmentationNodeID, segmentationNodeVectorProperty); } } void mitk::RelationStorage::RemoveSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationNodeID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid segmentation-IDs for the current case mitk::VectorProperty::Pointer allSegmentationsVectorProperty = dynamic_cast*>(propertyList->GetProperty("segmentations")); if (nullptr == allSegmentationsVectorProperty) { MITK_INFO << "Could not find any segmentation in the storage."; return; } // remove the lesion reference from the list of all lesions of the current case std::vector allSegmentationsIDs = allSegmentationsVectorProperty->GetValue(); allSegmentationsIDs.erase(std::remove(allSegmentationsIDs.begin(), allSegmentationsIDs.end(), segmentationNodeID), allSegmentationsIDs.end()); if (allSegmentationsIDs.size() == 0) { // no more segmentations stored -> remove the segmentations property list propertyList->DeleteProperty("segmentations"); } else { // or store the modified vector value allSegmentationsVectorProperty->SetValue(allSegmentationsIDs); } // remove the lesion instance itself propertyList->DeleteProperty(segmentationNodeID); } void mitk::RelationStorage::AddLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid lesion-IDs for the current case mitk::VectorProperty::Pointer lesionsVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); std::vector lesionsVectorValue; if (nullptr == lesionsVectorProperty) { lesionsVectorProperty = mitk::VectorProperty::New(); } else { lesionsVectorValue = lesionsVectorProperty->GetValue(); } const auto& existingIndex = std::find(lesionsVectorValue.begin(), lesionsVectorValue.end(), lesion.UID); if (existingIndex != lesionsVectorValue.end()) { return; } else { // add the new lesion id from the given lesion to the vector of all current lesion IDs lesionsVectorValue.push_back(lesion.UID); // overwrite the current vector property with the new, extended string vector lesionsVectorProperty->SetValue(lesionsVectorValue); propertyList->SetProperty("lesions", lesionsVectorProperty); // add the lesion with the lesion UID as the key and the lesion information as value std::vector lesionData; lesionData.push_back(lesion.name); lesionData.push_back(lesion.lesionClass.UID); mitk::VectorProperty::Pointer newLesionVectorProperty = mitk::VectorProperty::New(); newLesionVectorProperty->SetValue(lesionData); propertyList->SetProperty(lesion.UID, newLesionVectorProperty); // add the lesion class with the lesion class UID as key and the class type as value std::string lesionClassType = lesion.lesionClass.classType; propertyList->SetStringProperty(lesion.lesionClass.UID.c_str(), lesionClassType.c_str()); } } void mitk::RelationStorage::OverwriteLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid lesion-IDs for the current case mitk::VectorProperty* lesionVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); if (nullptr == lesionVectorProperty) { MITK_INFO << "Could not find any lesion in the storage."; return; } std::vector lesionVectorValue = lesionVectorProperty->GetValue(); const auto existingLesion = std::find(lesionVectorValue.begin(), lesionVectorValue.end(), lesion.UID); if (existingLesion != lesionVectorValue.end()) { // overwrite the referenced lesion class UID with the new, given lesion class data std::vector lesionData; lesionData.push_back(lesion.name); lesionData.push_back(lesion.lesionClass.UID); mitk::VectorProperty::Pointer newLesionVectorProperty = mitk::VectorProperty::New(); newLesionVectorProperty->SetValue(lesionData); propertyList->SetProperty(lesion.UID, newLesionVectorProperty); // overwrite the lesion class with the lesion class UID as key and the new, given class type as value std::string lesionClassType = lesion.lesionClass.classType; propertyList->SetStringProperty(lesion.lesionClass.UID.c_str(), lesionClassType.c_str()); } else { MITK_INFO << "Could not find lesion " << lesion.UID << " in the storage. Cannot overwrite the lesion."; } } void mitk::RelationStorage::LinkSegmentationToLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID, const SemanticTypes::Lesion& lesion) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid lesion-IDs for the current case mitk::VectorProperty* lesionVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); if (nullptr == lesionVectorProperty) { MITK_INFO << "Could not find any lesion property in the storage."; return; } std::vector lesionVectorValue = lesionVectorProperty->GetValue(); const auto existingLesion = std::find(lesionVectorValue.begin(), lesionVectorValue.end(), lesion.UID); if (existingLesion != lesionVectorValue.end()) { // set / overwrite the lesion reference of the given segmentation // retrieve a vector property that contains the referenced ID of a segmentation (0. image ID 1. lesion ID) mitk::VectorProperty* segmentationVectorProperty = dynamic_cast*>(propertyList->GetProperty(segmentationID)); if (nullptr == segmentationVectorProperty) { MITK_INFO << "Could not find the segmentation node " << segmentationID << " in the storage. Cannot link segmentation to lesion."; return; } std::vector segmentationVectorValue = segmentationVectorProperty->GetValue(); if (segmentationVectorValue.size() != 2) { MITK_INFO << "Incorrect segmentation storage. Not two (2) IDs stored."; return; } else { // the lesion ID of a segmentation is the second value in the vector segmentationVectorValue[1] = lesion.UID; segmentationVectorProperty->SetValue(segmentationVectorValue); } } else { MITK_INFO << "Could not find lesion " << lesion.UID << " in the storage. Cannot link segmentation to lesion."; } } void mitk::RelationStorage::UnlinkSegmentationFromLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& segmentationID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the referenced ID of a segmentation (0. image ID 1. lesion ID) mitk::VectorProperty* segmentationVectorProperty = dynamic_cast*>(propertyList->GetProperty(segmentationID)); if (nullptr == segmentationVectorProperty) { MITK_INFO << "Could not find the segmentation node " << segmentationID << " in the storage. Cannot unlink lesion from segmentation."; return; } std::vector segmentationVectorValue = segmentationVectorProperty->GetValue(); // a segmentation has to have exactly two values (the ID of the linked image and the ID of the lesion) if (segmentationVectorValue.size() != 2) { MITK_INFO << "Incorrect data storage. Not two (2) values stored."; return; } else { // the second value of the data node vector is the ID of the referenced lesion // set the lesion reference to an empty string for removal segmentationVectorValue[1] = ""; segmentationVectorProperty->SetValue(segmentationVectorValue); } } void mitk::RelationStorage::RemoveLesion(const mitk::SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid lesions of the current case mitk::VectorProperty* lesionVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); if (nullptr == lesionVectorProperty) { MITK_INFO << "Could not find any lesion property in the storage."; return; } // remove the lesion reference from the list of all lesions of the current case std::vector lesionVectorValue = lesionVectorProperty->GetValue(); lesionVectorValue.erase(std::remove(lesionVectorValue.begin(), lesionVectorValue.end(), lesion.UID), lesionVectorValue.end()); if (lesionVectorValue.size() == 0) { // no more lesions stored -> remove the lesions property list propertyList->DeleteProperty("lesions"); } else { // or store the modified vector value lesionVectorProperty->SetValue(lesionVectorValue); } // remove the lesion instance itself // the lesion data is stored under the lesion ID mitk::VectorProperty* lesionDataProperty = dynamic_cast*>(propertyList->GetProperty(lesion.UID)); if (nullptr == lesionDataProperty) { MITK_INFO << "Lesion " << lesion.UID << " not found (already removed?). Cannot remove the lesion."; return; } std::vector lesionData = lesionDataProperty->GetValue(); // a lesion date has to have exactly two values (the name of the lesion and the UID of the lesion class) if (lesionData.size() != 2) { MITK_INFO << "Incorrect lesion data storage. Not two (2) strings of the lesion UID and the lesion name are stored."; } else { std::string lesionClassID = lesionData[1]; RemoveLesionClass(caseID, lesionClassID); } propertyList->DeleteProperty(lesion.UID); } void mitk::RelationStorage::RemoveLesionClass(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& lesionClassID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the lesion class mitk::StringProperty* lesionClassProperty = dynamic_cast(propertyList->GetProperty(lesionClassID)); if (nullptr == lesionClassProperty) { MITK_INFO << "Lesion class " << lesionClassID << " not found (already removed?). Cannot remove the lesion class."; return; } // retrieve a vector property that contains the valid lesions of the current case mitk::VectorProperty* lesionVectorProperty = dynamic_cast*>(propertyList->GetProperty("lesions")); if (nullptr == lesionVectorProperty) { return; } // check if the lesion class ID is referenced by any other lesion std::vector lesionVectorValue = lesionVectorProperty->GetValue(); const auto existingLesionClass = std::find_if(lesionVectorValue.begin(), lesionVectorValue.end(), [&propertyList, &lesionClassID](const std::string& lesionID) { mitk::VectorProperty* lesionDataProperty = dynamic_cast*>(propertyList->GetProperty(lesionID)); if (nullptr == lesionDataProperty) { return false; } else { std::vector lesionData = lesionDataProperty->GetValue(); // a lesion date has to have exactly two values (the name of the lesion and the UID of the lesion class) if (lesionData.size() != 2) { return false; } else { return lesionData[1] == lesionClassID; } } }); if (existingLesionClass == lesionVectorValue.end()) { // lesion class ID not referenced; remove lesion class propertyList->DeleteProperty(lesionClassID); } } void mitk::RelationStorage::AddControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid controlPoint UIDs for the current case mitk::VectorProperty::Pointer controlPointVectorProperty = dynamic_cast*>(propertyList->GetProperty("controlpoints")); std::vector controlPointVectorValue; if (nullptr == controlPointVectorProperty) { controlPointVectorProperty = mitk::VectorProperty::New(); } else { controlPointVectorValue = controlPointVectorProperty->GetValue(); } const auto existingControlPoint = std::find(controlPointVectorValue.begin(), controlPointVectorValue.end(), controlPoint.UID); if (existingControlPoint != controlPointVectorValue.end()) { return; } else { // add the new control point UID from the given control point to the vector of all current control point UIDs controlPointVectorValue.push_back(controlPoint.UID); // overwrite the current vector property with the new, extended string vector controlPointVectorProperty->SetValue(controlPointVectorValue); propertyList->SetProperty("controlpoints", controlPointVectorProperty); // store the control point values (the three integer values of a date) std::vector controlPointDate; controlPointDate.push_back(controlPoint.year); controlPointDate.push_back(controlPoint.month); controlPointDate.push_back(controlPoint.day); mitk::VectorProperty::Pointer newControlPointVectorProperty = mitk::VectorProperty::New(); newControlPointVectorProperty->SetValue(controlPointDate); propertyList->SetProperty(controlPoint.UID, newControlPointVectorProperty); } } void mitk::RelationStorage::LinkDataToControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& dataNodeID, const SemanticTypes::ControlPoint& controlPoint) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid controlPoint UIDs for the current case mitk::VectorProperty* controlPointVectorProperty = dynamic_cast*>(propertyList->GetProperty("controlpoints")); if (nullptr == controlPointVectorProperty) { MITK_INFO << "Could not find any control point property in the storage."; return; } std::vector controlPointVectorValue = controlPointVectorProperty->GetValue(); const auto existingControlPoint = std::find(controlPointVectorValue.begin(), controlPointVectorValue.end(), controlPoint.UID); if (existingControlPoint != controlPointVectorValue.end()) { // set / overwrite the control point reference of the given data // retrieve a vector property that contains the referenced ID of a data node (0. information type 1. control point ID) mitk::VectorProperty* dataNodeVectorProperty = dynamic_cast*>(propertyList->GetProperty(dataNodeID)); if (nullptr == dataNodeVectorProperty) { MITK_INFO << "Could not find the data node " << dataNodeID << " in the storage. Cannot link data to control point."; return; } std::vector dataNodeVectorValue = dataNodeVectorProperty->GetValue(); // an image node has to have exactly two values (the information type and the ID of the control point) if (dataNodeVectorValue.size() != 2) { MITK_INFO << "Incorrect data storage. Not two (2) values stored."; return; } else { // the second value of the data node vector is the ID of the referenced control point dataNodeVectorValue[1] = controlPoint.UID; dataNodeVectorProperty->SetValue(dataNodeVectorValue); } } else { MITK_INFO << "Could not find control point " << controlPoint.UID << " in the storage. Cannot link data to control point."; } } void mitk::RelationStorage::UnlinkDataFromControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& dataNodeID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the referenced ID of a date (0. information type 1. control point ID) mitk::VectorProperty* dataNodeVectorProperty = dynamic_cast*>(propertyList->GetProperty(dataNodeID)); if (nullptr == dataNodeVectorProperty) { MITK_INFO << "Could not find the date " << dataNodeID << " in the storage. Cannot unlink control point from date."; return; } std::vector dataNodeVectorValue = dataNodeVectorProperty->GetValue(); // a data node has to have exactly two values (the information type and the ID of the control point) if (dataNodeVectorValue.size() != 2) { MITK_INFO << "Incorrect data storage. Not two (2) values stored."; return; } else { // the second value of the data node vector is the ID of the referenced control point // set the control point reference to an empty string for removal dataNodeVectorValue[1] = ""; dataNodeVectorProperty->SetValue(dataNodeVectorValue); } } void mitk::RelationStorage::RemoveControlPointFromCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid controlPoint UIDs for the current case mitk::VectorProperty* allControlPointsVectorProperty = dynamic_cast*>(propertyList->GetProperty("controlpoints")); if (nullptr == allControlPointsVectorProperty) { MITK_INFO << "Could not find any control point property in the storage."; return; } // remove the control point reference from the list of all control points of the current case std::vector currentControlPointVectorValue = allControlPointsVectorProperty->GetValue(); currentControlPointVectorValue.erase(std::remove(currentControlPointVectorValue.begin(), currentControlPointVectorValue.end(), controlPoint.UID), currentControlPointVectorValue.end()); allControlPointsVectorProperty->SetValue(currentControlPointVectorValue); // remove the control point instance itself propertyList->DeleteProperty(controlPoint.UID); } void mitk::RelationStorage::AddExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid examination period UIDs for the current case mitk::VectorProperty::Pointer vectorProperty = dynamic_cast*>(propertyList->GetProperty("examinationperiods")); std::vector examinationPeriodsVectorValue; if (nullptr == vectorProperty) { vectorProperty = mitk::VectorProperty::New(); } else { examinationPeriodsVectorValue = vectorProperty->GetValue(); } const auto& existingIndex = std::find(examinationPeriodsVectorValue.begin(), examinationPeriodsVectorValue.end(), examinationPeriod.UID); if (existingIndex != examinationPeriodsVectorValue.end()) { return; } else { // add the new examination period id from the given examination period to the vector of all current examination period IDs examinationPeriodsVectorValue.push_back(examinationPeriod.UID); // overwrite the current vector property with the new, extended string vector vectorProperty->SetValue(examinationPeriodsVectorValue); propertyList->SetProperty("examinationperiods", vectorProperty); // add the examination period with the UID as the key and an empty control point UIDs vector as value std::vector controlPointUIDs; mitk::VectorProperty::Pointer newExaminationPeriodVectorProperty = mitk::VectorProperty::New(); newExaminationPeriodVectorProperty->SetValue(controlPointUIDs); propertyList->SetProperty(examinationPeriod.UID, newExaminationPeriodVectorProperty); } } void mitk::RelationStorage::AddControlPointToExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod examinationPeriod) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the represented control point UIDs of the given examination period mitk::VectorProperty* controlPointUIDsVectorProperty = dynamic_cast*>(propertyList->GetProperty(examinationPeriod.UID)); if (nullptr == controlPointUIDsVectorProperty) { MITK_INFO << "Could not find examination period " << examinationPeriod.UID << " in the storage. Cannot add the control point to the examination period."; return; } std::vector controlPointUIDsVectorValue = controlPointUIDsVectorProperty->GetValue(); // store the control point UID controlPointUIDsVectorValue.push_back(controlPoint.UID); controlPointUIDsVectorProperty->SetValue(controlPointUIDsVectorValue); } void mitk::RelationStorage::RemoveControlPointFromExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod examinationPeriod) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the represented control point UIDs of the given examination period mitk::VectorProperty* controlPointUIDsVectorProperty = dynamic_cast*>(propertyList->GetProperty(examinationPeriod.UID)); if (nullptr == controlPointUIDsVectorProperty) { MITK_INFO << "Could not find examination period " << examinationPeriod.UID << " in the storage. Cannot add the control point to the examination period."; return; } std::vector controlPointUIDsVectorValue = controlPointUIDsVectorProperty->GetValue(); controlPointUIDsVectorValue.erase(std::remove(controlPointUIDsVectorValue.begin(), controlPointUIDsVectorValue.end(), controlPoint.UID), controlPointUIDsVectorValue.end()); // store the modified vector value controlPointUIDsVectorProperty->SetValue(controlPointUIDsVectorValue); } void mitk::RelationStorage::RemoveExaminationPeriodFromCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod examinationPeriod) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid examination period UIDs for the current case mitk::VectorProperty::Pointer vectorProperty = dynamic_cast*>(propertyList->GetProperty("examinationperiods")); if (nullptr == vectorProperty) { MITK_INFO << "Could not find any examination periods in the storage."; return; } std::vector examinationPeriodVectorValue = vectorProperty->GetValue(); examinationPeriodVectorValue.erase(std::remove(examinationPeriodVectorValue.begin(), examinationPeriodVectorValue.end(), examinationPeriod.UID), examinationPeriodVectorValue.end()); if (examinationPeriodVectorValue.size() == 0) { // no more examination periods stored -> remove the examination period property list propertyList->DeleteProperty("examinationperiods"); } else { // or store the modified vector value vectorProperty->SetValue(examinationPeriodVectorValue); } } void mitk::RelationStorage::AddInformationTypeToImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID, const SemanticTypes::InformationType informationType) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid information types of the current case mitk::VectorProperty::Pointer informationTypeVectorProperty = dynamic_cast*>(propertyList->GetProperty("informationtypes")); std::vector informationTypeVectorValue; if (nullptr == informationTypeVectorProperty) { informationTypeVectorProperty = mitk::VectorProperty::New(); } else { informationTypeVectorValue = informationTypeVectorProperty->GetValue(); } const auto existingInformationType = std::find(informationTypeVectorValue.begin(), informationTypeVectorValue.end(), informationType); if (existingInformationType == informationTypeVectorValue.end()) { // at first: add the information type to the storage informationTypeVectorValue.push_back(informationType); informationTypeVectorProperty->SetValue(informationTypeVectorValue); propertyList->SetProperty("informationtypes", informationTypeVectorProperty); } // set / overwrite the information type of the given data // retrieve a vector property that contains the referenced ID of an image (0. information type 1. control point ID) mitk::VectorProperty* imageNodeVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); if (nullptr == imageNodeVectorProperty) { MITK_INFO << "Could not find the image " << imageID << " in the storage. Cannot add information type to image."; return; } std::vector imageNodeVectorValue = imageNodeVectorProperty->GetValue(); // an image node has to have exactly two values (the information type and the ID of the control point) if (imageNodeVectorValue.size() != 2) { MITK_INFO << "Incorrect data storage. Not two (2) values stored."; return; } else { // the first value of the data node vector is the information type imageNodeVectorValue[0] = informationType; imageNodeVectorProperty->SetValue(imageNodeVectorValue); } } void mitk::RelationStorage::RemoveInformationTypeFromImage(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& imageID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the referenced ID of an image (0. information type 1. control point ID) mitk::VectorProperty* imageNodeVectorProperty = dynamic_cast*>(propertyList->GetProperty(imageID)); if (nullptr == imageNodeVectorProperty) { MITK_INFO << "Could not find the image " << imageID << " in the storage. Cannot remove information type from image."; return; } std::vector imageNodeVectorValue = imageNodeVectorProperty->GetValue(); // an image node has to have exactly two values (the information type and the ID of the control point) if (imageNodeVectorValue.size() != 2) { MITK_INFO << "Incorrect data storage. Not two (2) values stored."; return; } else { // the first value of the data node vector is the information type // set the information type to an empty string for removal imageNodeVectorValue[0] = ""; imageNodeVectorProperty->SetValue(imageNodeVectorValue); } } void mitk::RelationStorage::RemoveInformationTypeFromCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType informationType) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return; } // retrieve a vector property that contains the valid information types of the current case mitk::VectorProperty* informationTypeVectorProperty = dynamic_cast*>(propertyList->GetProperty("informationtypes")); if (nullptr == informationTypeVectorProperty) { MITK_INFO << "Could not find any information type property in the storage."; return; } std::vector informationTypeVectorValue = informationTypeVectorProperty->GetValue(); informationTypeVectorValue.erase(std::remove(informationTypeVectorValue.begin(), informationTypeVectorValue.end(), informationType), informationTypeVectorValue.end()); if (informationTypeVectorValue.size() == 0) { // no more information types stored -> remove the information types property list propertyList->DeleteProperty("informationtypes"); } else { // or store the modified vector value informationTypeVectorProperty->SetValue(informationTypeVectorValue); } } mitk::PropertyList::Pointer mitk::RelationStorage::GetStorageData(const SemanticTypes::CaseID& caseID) { // access the storage PERSISTENCE_GET_SERVICE_MACRO if (nullptr == persistenceService) { MITK_INFO << "Persistence service could not be loaded"; return nullptr; } // the property list is valid for a whole case and contains all the properties for the current case // the persistence service may create a new property list with the given ID, if no property list is found return persistenceService->GetPropertyList(const_cast(caseID)); } mitk::SemanticTypes::Lesion mitk::RelationStorage::GenerateLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::ID& lesionID) { mitk::PropertyList::Pointer propertyList = GetStorageData(caseID); if (nullptr == propertyList) { MITK_INFO << "Could not find the property list " << caseID << " for the current MITK workbench / session."; return SemanticTypes::Lesion(); } mitk::VectorProperty* lesionDataProperty = dynamic_cast*>(propertyList->GetProperty(lesionID)); if (nullptr == lesionDataProperty) { MITK_INFO << "Lesion " << lesionID << " not found. Lesion can not be retrieved."; return SemanticTypes::Lesion(); } std::vector lesionData = lesionDataProperty->GetValue(); // a lesion date has to have exactly two values (the name of the lesion and the UID of the lesion class) if (lesionData.size() != 2) { MITK_INFO << "Incorrect lesion data storage. Not two (2) strings of the lesion name and the lesion UID are stored."; return SemanticTypes::Lesion(); } // the lesion class ID is stored as the second property std::string lesionClassID = lesionData[1]; mitk::StringProperty* lesionClassProperty = dynamic_cast(propertyList->GetProperty(lesionClassID)); if (nullptr != lesionClassProperty) { SemanticTypes::LesionClass generatedLesionClass; generatedLesionClass.UID = lesionClassID; generatedLesionClass.classType = lesionClassProperty->GetValue(); SemanticTypes::Lesion generatedLesion; generatedLesion.UID = lesionID; generatedLesion.name = lesionData[0]; generatedLesion.lesionClass = generatedLesionClass; return generatedLesion; } else { MITK_INFO << "Incorrect lesion class storage. Lesion " << lesionID << " can not be retrieved."; return SemanticTypes::Lesion(); } } diff --git a/Modules/SemanticRelations/src/mitkSemanticRelations.cpp b/Modules/SemanticRelations/src/mitkSemanticRelations.cpp index f5489a6095..a3e207e8f7 100644 --- a/Modules/SemanticRelations/src/mitkSemanticRelations.cpp +++ b/Modules/SemanticRelations/src/mitkSemanticRelations.cpp @@ -1,1002 +1,1003 @@ /*=================================================================== 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. ===================================================================*/ // semantic relations module +#include "mitkControlPointManager.h" +#include "mitkNodePredicates.h" #include "mitkSemanticRelations.h" #include "mitkSemanticRelationException.h" -#include "mitkNodePredicates.h" #include "mitkUIDGeneratorBoost.h" // multi label module #include // c++ #include #include std::vector mitk::SemanticRelations::m_ObserverVector; mitk::SemanticRelations::SemanticRelations(DataStorage::Pointer dataStorage) : m_DataStorage(dataStorage) { m_RelationStorage = std::make_shared(); m_RelationStorage->SetDataStorage(m_DataStorage); } mitk::SemanticRelations::~SemanticRelations() { // nothing here } void mitk::SemanticRelations::AddObserver(ISemanticRelationsObserver* observer) { std::vector::iterator existingObserver = std::find(m_ObserverVector.begin(), m_ObserverVector.end(), observer); if (existingObserver != m_ObserverVector.end()) { // no need to add the already existing observer return; } m_ObserverVector.push_back(observer); } void mitk::SemanticRelations::RemoveObserver(ISemanticRelationsObserver* observer) { m_ObserverVector.erase(std::remove(m_ObserverVector.begin(), m_ObserverVector.end(), observer), m_ObserverVector.end()); } /************************************************************************/ /* functions to get instances / attributes */ /************************************************************************/ mitk::SemanticRelations::LesionVector mitk::SemanticRelations::GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID) const { return m_RelationStorage->GetAllLesionsOfCase(caseID); } mitk::SemanticRelations::LesionVector mitk::SemanticRelations::GetAllLesionsOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const { LesionVector allLesions = GetAllLesionsOfCase(caseID); // filter the lesions: use only those, where the associated data is connected to image data that refers to the given control point using a lambda function auto lambda = [&caseID, &controlPoint, this](const SemanticTypes::Lesion& lesion) { return !ControlPointContainsLesion(caseID, lesion, controlPoint); }; allLesions.erase(std::remove_if(allLesions.begin(), allLesions.end(), lambda), allLesions.end()); return allLesions; } mitk::SemanticRelations::LesionClassVector mitk::SemanticRelations::GetAllLesionClassesOfCase(const SemanticTypes::CaseID& caseID) const { LesionVector allLesionsOfCase = GetAllLesionsOfCase(caseID); LesionClassVector allLesionClassesOfCase; for (const auto& lesion : allLesionsOfCase) { allLesionClassesOfCase.push_back(lesion.lesionClass); } // remove duplicate entries auto lessThan = [](const SemanticTypes::LesionClass& lesionClassL, const SemanticTypes::LesionClass& lesionClassR) { return lesionClassL.UID < lesionClassR.UID; }; auto equal = [](const SemanticTypes::LesionClass& lesionClassL, const SemanticTypes::LesionClass& lesionClassR) { return lesionClassL.UID == lesionClassR.UID; }; std::sort(allLesionClassesOfCase.begin(), allLesionClassesOfCase.end(), lessThan); allLesionClassesOfCase.erase(std::unique(allLesionClassesOfCase.begin(), allLesionClassesOfCase.end(), equal), allLesionClassesOfCase.end()); return allLesionClassesOfCase; } mitk::SemanticRelations::LesionVector mitk::SemanticRelations::GetAllLesionsInImage(const DataNode* imageNode) const { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } if (m_DataStorage.IsNull()) { mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } LesionVector allLesionsInImage; // get child nodes of the current node with the segmentation predicate DataStorage::SetOfObjects::ConstPointer segmentationNodes = m_DataStorage->GetDerivations(imageNode, NodePredicates::GetSegmentationPredicate(), false); for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) { DataNode* segmentationNode = it->Value(); try { SemanticTypes::Lesion representedLesion = GetRepresentedLesion(segmentationNode); allLesionsInImage.push_back(representedLesion); } catch (const SemanticRelationException&) { continue; } } return allLesionsInImage; } mitk::SemanticTypes::Lesion mitk::SemanticRelations::GetRepresentedLesion(const DataNode* segmentationNode) const { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); SemanticTypes::ID segmentationID = GetIDFromDataNode(segmentationNode); SemanticTypes::Lesion representedLesion = m_RelationStorage->GetRepresentedLesion(caseID, segmentationID); if (representedLesion.UID.empty()) { mitkThrowException(SemanticRelationException) << "Could not find a represented lesion instance for the given segmentation node " << segmentationNode->GetName(); } else { return representedLesion; } } bool mitk::SemanticRelations::IsRepresentingALesion(const DataNode* segmentationNode) const { try { SemanticTypes::Lesion representedLesion = GetRepresentedLesion(segmentationNode); return true; } catch (const Exception&) { return false; } } bool mitk::SemanticRelations::InstanceExists(const DataNode* dataNode) const { try { SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); SemanticTypes::ID dataNodeID = GetIDFromDataNode(dataNode); if (mitk::NodePredicates::GetImagePredicate()->CheckNode(dataNode)) { std::vector allImageIDsOfCase = m_RelationStorage->GetAllImageIDsOfCase(caseID); return std::find(allImageIDsOfCase.begin(), allImageIDsOfCase.end(), dataNodeID) != allImageIDsOfCase.end(); } else if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { std::vector allSegmentationIDsOfCase = m_RelationStorage->GetAllSegmentationIDsOfCase(caseID); return std::find(allSegmentationIDsOfCase.begin(), allSegmentationIDsOfCase.end(), dataNodeID) != allSegmentationIDsOfCase.end(); } else { return false; } } catch (const SemanticRelationException&) { return false; } } mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllSegmentationsOfCase(const SemanticTypes::CaseID& caseID) const { if (m_DataStorage.IsNull()) { mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } return m_RelationStorage->GetAllSegmentationsOfCase(caseID); } mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllSegmentationsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const { if (m_DataStorage.IsNull()) { mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } if (InstanceExists(caseID, lesion)) { // lesion exists, retrieve all case segmentations from the storage DataNodeVector allSegmentationsOfLesion = GetAllSegmentationsOfCase(caseID); // filter all segmentations: check for semantic relation with the given lesion using a lambda function auto lambda = [&lesion, this](DataNode::Pointer segmentation) { try { SemanticTypes::Lesion representedLesion = GetRepresentedLesion(segmentation); return lesion.UID != representedLesion.UID; } catch (const SemanticRelationException&) { return true; } }; allSegmentationsOfLesion.erase(std::remove_if(allSegmentationsOfLesion.begin(), allSegmentationsOfLesion.end(), lambda), allSegmentationsOfLesion.end()); return allSegmentationsOfLesion; } else { mitkThrowException(SemanticRelationException) << "Could not find an existing lesion instance for the given caseID " << caseID << " and lesion " << lesion.UID << "."; } } mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllImagesOfCase(const SemanticTypes::CaseID& caseID) const { if (m_DataStorage.IsNull()) { mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } return m_RelationStorage->GetAllImagesOfCase(caseID); } mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllImagesOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const { if (m_DataStorage.IsNull()) { mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } DataNodeVector allImagesOfLesion; // 1. get all segmentations that define the lesion // 2. retrieve the parent node (source) of the found segmentation node DataNodeVector allSegmentationsOfLesion = GetAllSegmentationsOfLesion(caseID, lesion); for (const auto& segmentationNode : allSegmentationsOfLesion) { // get parent node of the current segmentation node with the node predicate DataStorage::SetOfObjects::ConstPointer parentNodes = m_DataStorage->GetSources(segmentationNode, NodePredicates::GetImagePredicate(), false); for (auto it = parentNodes->Begin(); it != parentNodes->End(); ++it) { DataNode::Pointer dataNode = it->Value(); allImagesOfLesion.push_back(it->Value()); } } std::sort(allImagesOfLesion.begin(), allImagesOfLesion.end()); allImagesOfLesion.erase(std::unique(allImagesOfLesion.begin(), allImagesOfLesion.end()), allImagesOfLesion.end()); return allImagesOfLesion; } bool mitk::SemanticRelations::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const { LesionVector allLesions = GetAllLesionsOfCase(caseID); // filter all lesions: check for equality with the given lesion using a lambda function auto lambda = [&lesion](const SemanticTypes::Lesion& currentLesion) { return currentLesion.UID == lesion.UID; }; const auto existingLesion = std::find_if(allLesions.begin(), allLesions.end(), lambda); if (existingLesion != allLesions.end()) { return true; } else { return false; } } mitk::SemanticRelations::ControlPointVector mitk::SemanticRelations::GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID) const { return m_RelationStorage->GetAllControlPointsOfCase(caseID); } mitk::SemanticRelations::ControlPointVector mitk::SemanticRelations::GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const { ControlPointVector allControlPoints = GetAllControlPointsOfCase(caseID); // filter the control points: use only those, where the associated image data has a segmentation that refers to the given lesion using a lambda function auto lambda = [&caseID, &lesion, this](const SemanticTypes::ControlPoint& controlPoint) { return !ControlPointContainsLesion(caseID, lesion, controlPoint); }; allControlPoints.erase(std::remove_if(allControlPoints.begin(), allControlPoints.end(), lambda), allControlPoints.end()); return allControlPoints; } mitk::SemanticRelations::ControlPointVector mitk::SemanticRelations::GetAllControlPointsOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) const { ControlPointVector allControlPoints = GetAllControlPointsOfCase(caseID); // filter the control points: use only those, where the associated image data refers to the given information type using a lambda function auto lambda = [&caseID, &informationType, this](const SemanticTypes::ControlPoint& controlPoint) { return !ControlPointContainsInformationType(caseID, informationType, controlPoint); }; allControlPoints.erase(std::remove_if(allControlPoints.begin(), allControlPoints.end(), lambda), allControlPoints.end()); return allControlPoints; } -mitk::SemanticTypes::ControlPoint mitk::SemanticRelations::GetControlPointOfData(const DataNode* dataNode) const +mitk::SemanticTypes::ControlPoint mitk::SemanticRelations::GetControlPointOfData(const DataNode* imageNode) const { - if (nullptr == dataNode) + if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } - SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); - SemanticTypes::ID dataNodeID = GetIDFromDataNode(dataNode); - return m_RelationStorage->GetControlPointOfData(caseID, dataNodeID); + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); + SemanticTypes::ID dataNodeID = GetIDFromDataNode(imageNode); + return m_RelationStorage->GetControlPointOfImage(caseID, dataNodeID); } mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllImagesOfControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const { if (m_DataStorage.IsNull()) { mitkThrow() << "Not a valid data storage."; } if (InstanceExists(caseID, controlPoint)) { // control point exists, retrieve all images from the storage DataNodeVector allImagesOfControlPoint = GetAllImagesOfCase(caseID); // filter all images to remove the ones with a different control point using a lambda function auto lambda = [&controlPoint, this](DataNode::Pointer imageNode) { return controlPoint.UID != GetControlPointOfData(imageNode).UID; }; allImagesOfControlPoint.erase(std::remove_if(allImagesOfControlPoint.begin(), allImagesOfControlPoint.end(), lambda), allImagesOfControlPoint.end()); return allImagesOfControlPoint; } else { mitkThrowException(SemanticRelationException) << "Could not find an existing control point instance for the given caseID " << caseID << " and control point " << controlPoint.UID << "."; } } bool mitk::SemanticRelations::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const { ControlPointVector allControlPoints = GetAllControlPointsOfCase(caseID); // filter all control points: check for equality with the given control point using a lambda function auto lambda = [&controlPoint](const SemanticTypes::ControlPoint& currentControlPoint) { return currentControlPoint.UID == controlPoint.UID; }; const auto existingControlPoint = std::find_if(allControlPoints.begin(), allControlPoints.end(), lambda); if (existingControlPoint != allControlPoints.end()) { return true; } else { return false; } } mitk::SemanticRelations::ExaminationPeriodVector mitk::SemanticRelations::GetAllExaminationPeriodsOfCase(const SemanticTypes::CaseID& caseID) const { return m_RelationStorage->GetAllExaminationPeriodsOfCase(caseID); } bool mitk::SemanticRelations::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) const { ExaminationPeriodVector allExaminationPeriods = GetAllExaminationPeriodsOfCase(caseID); // filter all examination periods: check for equality with the given examination period using a lambda function auto lambda = [&examinationPeriod](const SemanticTypes::ExaminationPeriod& currentExaminationPeriod) { return currentExaminationPeriod.UID == examinationPeriod.UID; }; const auto existingExaminationPeriod = std::find_if(allExaminationPeriods.begin(), allExaminationPeriods.end(), lambda); if (existingExaminationPeriod != allExaminationPeriods.end()) { return true; } else { return false; } } mitk::SemanticRelations::InformationTypeVector mitk::SemanticRelations::GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID) const { return m_RelationStorage->GetAllInformationTypesOfCase(caseID); } mitk::SemanticRelations::InformationTypeVector mitk::SemanticRelations::GetAllInformationTypesOfCase(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) const { InformationTypeVector allInformationTypes = GetAllInformationTypesOfCase(caseID); // filter the information types: use only those, where the associated data refers to the given control point using a lambda function auto lambda = [&caseID, &controlPoint, this](const SemanticTypes::InformationType& informationType) { return !ControlPointContainsInformationType(caseID, informationType, controlPoint); }; allInformationTypes.erase(std::remove_if(allInformationTypes.begin(), allInformationTypes.end(), lambda), allInformationTypes.end()); return allInformationTypes; } mitk::SemanticTypes::InformationType mitk::SemanticRelations::GetInformationTypeOfImage(const DataNode* imageNode) const { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); return m_RelationStorage->GetInformationTypeOfImage(caseID, imageID); } mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllImagesOfInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) const { if (m_DataStorage.IsNull()) { mitkThrow() << "Not a valid data storage."; } if (InstanceExists(caseID, informationType)) { // information type exists, retrieve all images from the storage DataNodeVector allImagesOfInformationType = GetAllImagesOfCase(caseID); // filter all images to remove the ones with a different information type using a lambda function auto lambda = [&informationType, this](DataNode::Pointer imageNode) { return informationType != GetInformationTypeOfImage(imageNode); }; allImagesOfInformationType.erase(std::remove_if(allImagesOfInformationType.begin(), allImagesOfInformationType.end(), lambda), allImagesOfInformationType.end()); return allImagesOfInformationType; } else { mitkThrowException(SemanticRelationException) << "Could not find an existing information type for the given caseID " << caseID << " and information type " << informationType; } } mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllSpecificImages(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const { if (m_DataStorage.IsNull()) { mitkThrow() << "Not a valid data storage."; } if (InstanceExists(caseID, controlPoint)) { if (InstanceExists(caseID, informationType)) { // control point exists, information type exists, retrieve all images from the storage DataNodeVector allImagesOfCase = GetAllImagesOfCase(caseID); // filter all images to remove the ones with a different control point and information type using a lambda function auto lambda = [&controlPoint, &informationType, this](DataNode::Pointer imageNode) { return (informationType != GetInformationTypeOfImage(imageNode)) || (controlPoint != GetControlPointOfData(imageNode)); }; allImagesOfCase.erase(std::remove_if(allImagesOfCase.begin(), allImagesOfCase.end(), lambda), allImagesOfCase.end()); return allImagesOfCase; } else { mitkThrowException(SemanticRelationException) << "Could not find an existing information type for the given caseID " << caseID << " and information type " << informationType; } } else { mitkThrowException(SemanticRelationException) << "Could not find an existing control point for the given caseID " << caseID << " and control point " << controlPoint.UID; } } mitk::SemanticRelations::DataNodeVector mitk::SemanticRelations::GetAllSpecificSegmentations(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const { if (m_DataStorage.IsNull()) { mitkThrow() << "Not a valid data storage."; } DataNodeVector allSpecificImages = GetAllSpecificImages(caseID, controlPoint, informationType); DataNodeVector allSpecificSegmentations; for (const auto& imageNode : allSpecificImages) { DataStorage::SetOfObjects::ConstPointer segmentationNodes = m_DataStorage->GetDerivations(imageNode, NodePredicates::GetSegmentationPredicate(), false); for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) { allSpecificSegmentations.push_back(it->Value()); } } return allSpecificSegmentations; } bool mitk::SemanticRelations::InstanceExists(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType) const { InformationTypeVector allInformationTypes = GetAllInformationTypesOfCase(caseID); // filter all information types: check for equality with the given information type using a lambda function auto lambda = [&informationType](const SemanticTypes::InformationType& currentInformationType) { return currentInformationType == informationType; }; const auto existingInformationType = std::find_if(allInformationTypes.begin(), allInformationTypes.end(), lambda); if (existingInformationType != allInformationTypes.end()) { return true; } else { return false; } } std::vector mitk::SemanticRelations::GetAllCaseIDs() const { return m_RelationStorage->GetAllCaseIDs(); } /************************************************************************/ /* functions to add / remove instances / attributes */ /************************************************************************/ void mitk::SemanticRelations::AddImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } // continue with a valid data node SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); SemanticTypes::ID nodeID = GetIDFromDataNode(imageNode); m_RelationStorage->AddCase(caseID); m_RelationStorage->AddImage(caseID, nodeID); SemanticTypes::InformationType informationType = GetDICOMModalityFromDataNode(imageNode); AddInformationTypeToImage(imageNode, informationType); // set the correct control point for this image SemanticTypes::ControlPoint controlPoint = GenerateControlPoint(imageNode); SetControlPointOfData(imageNode, controlPoint); } void mitk::SemanticRelations::RemoveImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } // continue with a valid data node SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); SemanticTypes::ID nodeID = GetIDFromDataNode(imageNode); RemoveInformationTypeFromImage(imageNode); UnlinkDataFromControlPoint(imageNode); m_RelationStorage->RemoveImage(caseID, nodeID); NotifyObserver(caseID); } void mitk::SemanticRelations::AddLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { if (InstanceExists(caseID, lesion)) { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to add already exists for the given case."; } else { m_RelationStorage->AddLesion(caseID, lesion); NotifyObserver(caseID); } } void mitk::SemanticRelations::OverwriteLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { if (InstanceExists(caseID, lesion)) { m_RelationStorage->OverwriteLesion(caseID, lesion); NotifyObserver(caseID); } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to overwrite does not exist for the given case."; } } void mitk::SemanticRelations::AddLesionAndLinkSegmentation(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); AddLesion(caseID, lesion); LinkSegmentationToLesion(segmentationNode, lesion); NotifyObserver(caseID); } void mitk::SemanticRelations::RemoveLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) { if (InstanceExists(caseID, lesion)) { DataNodeVector allSegmentationsOfLesion = GetAllSegmentationsOfLesion(caseID, lesion); if (allSegmentationsOfLesion.empty()) { // no more segmentations are linked to the specific lesion // the lesion can be removed from the storage m_RelationStorage->RemoveLesion(caseID, lesion); NotifyObserver(caseID); } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to remove is still referred to by a segmentation node. Lesion will not be removed."; } } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to remove does not exist for the given case."; } } void mitk::SemanticRelations::AddSegmentation(const DataNode* segmentationNode, const DataNode* parentNode) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } if (nullptr == parentNode) { mitkThrowException(SemanticRelationException) << "Not a valid parent data node."; } // continue with a valid data node SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); SemanticTypes::ID segmentationNodeID = GetIDFromDataNode(segmentationNode); SemanticTypes::ID parentNodeID = GetIDFromDataNode(parentNode); m_RelationStorage->AddSegmentation(caseID, segmentationNodeID, parentNodeID); NotifyObserver(caseID); } void mitk::SemanticRelations::LinkSegmentationToLesion(const DataNode* segmentationNode, const SemanticTypes::Lesion& lesion) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); if (InstanceExists(caseID, lesion)) { SemanticTypes::ID segmentationID = GetIDFromDataNode(segmentationNode); m_RelationStorage->LinkSegmentationToLesion(caseID, segmentationID, lesion); NotifyObserver(caseID); } else { mitkThrowException(SemanticRelationException) << "The lesion " << lesion.UID << " to link does not exist for the given case."; } } void mitk::SemanticRelations::UnlinkSegmentationFromLesion(const DataNode* segmentationNode) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); SemanticTypes::ID segmentationID = GetIDFromDataNode(segmentationNode); m_RelationStorage->UnlinkSegmentationFromLesion(caseID, segmentationID); NotifyObserver(caseID); } void mitk::SemanticRelations::RemoveSegmentation(const DataNode* segmentationNode) { if (nullptr == segmentationNode) { mitkThrowException(SemanticRelationException) << "Not a valid segmentation data node."; } // continue with a valid data node SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(segmentationNode); SemanticTypes::ID segmentationNodeID = GetIDFromDataNode(segmentationNode); m_RelationStorage->RemoveSegmentation(caseID, segmentationNodeID); NotifyObserver(caseID); } void mitk::SemanticRelations::SetControlPointOfData(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint) { if (nullptr == dataNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); ControlPointVector allControlPoints = GetAllControlPointsOfCase(caseID); // need to check if an already existing control point fits/contains the user control point SemanticTypes::ControlPoint existingControlPoint = FindExistingControlPoint(controlPoint, allControlPoints); if (!existingControlPoint.UID.empty()) { try { // found an already existing control point LinkDataToControlPoint(dataNode, existingControlPoint, false); } catch (const SemanticRelationException&) { mitkThrowException(SemanticRelationException) << "The data can not be linked. Inconsistency in the semantic relations storage assumed."; } } else { try { AddControlPointAndLinkData(dataNode, controlPoint, false); // added a new control point // find closest control point to add the new control point to the correct examination period SemanticTypes::ControlPoint closestControlPoint = FindClosestControlPoint(controlPoint, allControlPoints); ExaminationPeriodVector allExaminationPeriods = GetAllExaminationPeriodsOfCase(caseID); SemanticTypes::ExaminationPeriod examinationPeriod = FindExaminationPeriod(closestControlPoint, allExaminationPeriods); if (examinationPeriod.UID.empty()) { // no closest control point (exceed threshold) or no examination period found // create a new examination period for this control point and add it to the storage examinationPeriod.UID = UIDGeneratorBoost::GenerateUID(); AddExaminationPeriod(caseID, examinationPeriod); } // add the control point to the (newly created or found / close) examination period AddControlPointToExaminationPeriod(caseID, controlPoint, examinationPeriod); } catch (const SemanticRelationException&) { mitkThrowException(SemanticRelationException) << "The data can not be linked. Inconsistency in the semantic relations storage assumed."; } } ClearControlPoints(caseID); NotifyObserver(caseID); } void mitk::SemanticRelations::AddControlPointAndLinkData(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence) { if (nullptr == dataNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); if (InstanceExists(caseID, controlPoint)) { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to add already exists for the given case. \n Use 'LinkDataToControlPoint' instead."; } m_RelationStorage->AddControlPoint(caseID, controlPoint); LinkDataToControlPoint(dataNode, controlPoint, checkConsistence); } void mitk::SemanticRelations::LinkDataToControlPoint(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence) { if (nullptr == dataNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); if (InstanceExists(caseID, controlPoint)) { SemanticTypes::ID dataID = GetIDFromDataNode(dataNode); m_RelationStorage->LinkDataToControlPoint(caseID, dataID, controlPoint); } else { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to link does not exist for the given case."; } } void mitk::SemanticRelations::UnlinkDataFromControlPoint(const DataNode* dataNode) { if (nullptr == dataNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); SemanticTypes::ID dataID = GetIDFromDataNode(dataNode); - SemanticTypes::ControlPoint controlPoint = m_RelationStorage->GetControlPointOfData(caseID, dataID); + SemanticTypes::ControlPoint controlPoint = m_RelationStorage->GetControlPointOfImage(caseID, dataID); m_RelationStorage->UnlinkDataFromControlPoint(caseID, dataID); ClearControlPoints(caseID); } void mitk::SemanticRelations::AddExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ExaminationPeriod& examinationPeriod) { if (InstanceExists(caseID, examinationPeriod)) { mitkThrowException(SemanticRelationException) << "The examination period " << examinationPeriod.UID << " to add already exists for the given case."; } else { m_RelationStorage->AddExaminationPeriod(caseID, examinationPeriod); } } void mitk::SemanticRelations::AddControlPointToExaminationPeriod(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::ExaminationPeriod& examinationPeriod) { if (!InstanceExists(caseID, controlPoint)) { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to add does not exist for the given case."; } if (!InstanceExists(caseID, examinationPeriod)) { mitkThrowException(SemanticRelationException) << "The examination period " << examinationPeriod.UID << " does not exist for the given case. \n Use 'AddExaminationPeriod' before."; } m_RelationStorage->AddControlPointToExaminationPeriod(caseID, controlPoint, examinationPeriod); } void mitk::SemanticRelations::SetInformationType(const DataNode* imageNode, const SemanticTypes::InformationType& informationType) { RemoveInformationTypeFromImage(imageNode); AddInformationTypeToImage(imageNode, informationType); SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); NotifyObserver(caseID); } void mitk::SemanticRelations::AddInformationTypeToImage(const DataNode* imageNode, const SemanticTypes::InformationType& informationType) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); m_RelationStorage->AddInformationTypeToImage(caseID, imageID, informationType); } void mitk::SemanticRelations::RemoveInformationTypeFromImage(const DataNode* imageNode) { if (nullptr == imageNode) { mitkThrowException(SemanticRelationException) << "Not a valid image data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(imageNode); SemanticTypes::ID imageID = GetIDFromDataNode(imageNode); SemanticTypes::InformationType originalInformationType = m_RelationStorage->GetInformationTypeOfImage(caseID, imageID); m_RelationStorage->RemoveInformationTypeFromImage(caseID, imageID); // check for further references to the removed information type std::vector allImageIDsVectorValue = m_RelationStorage->GetAllImageIDsOfCase(caseID); for (const auto otherImageID : allImageIDsVectorValue) { SemanticTypes::InformationType otherInformationType = m_RelationStorage->GetInformationTypeOfImage(caseID, otherImageID); if (otherInformationType == originalInformationType) { // found the information type in another image -> cannot remove the information type from the case return; } } // given information type was not referred by any other image of the case -> the information type can be removed from the case m_RelationStorage->RemoveInformationTypeFromCase(caseID, originalInformationType); } /************************************************************************/ /* private functions */ /************************************************************************/ void mitk::SemanticRelations::NotifyObserver(const SemanticTypes::CaseID& caseID) const { for (auto& observer : m_ObserverVector) { observer->Update(caseID); } } bool mitk::SemanticRelations::ControlPointContainsLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion, const SemanticTypes::ControlPoint& controlPoint) const { DataNodeVector allImagesOfLesion; try { allImagesOfLesion = GetAllImagesOfLesion(caseID, lesion); } catch (const SemanticRelationException&) { // error retrieving image data; lesion has to be outside the control point return false; } DataNodeVector allImagesOfControlPoint; try { allImagesOfControlPoint = GetAllImagesOfControlPoint(caseID, controlPoint); } catch (const SemanticRelationException&) { // error retrieving control point data; lesion has to be outside the control point return false; } std::sort(allImagesOfLesion.begin(), allImagesOfLesion.end()); std::sort(allImagesOfControlPoint.begin(), allImagesOfControlPoint.end()); DataNodeVector allImagesIntersection; // set intersection removes duplicated nodes, since 'GetAllImagesOfControlPoint' only contains at most one of each node std::set_intersection(allImagesOfLesion.begin(), allImagesOfLesion.end(), allImagesOfControlPoint.begin(), allImagesOfControlPoint.end(), std::back_inserter(allImagesIntersection)); // if the vector of intersecting data is empty, the control point does not contain the lesion return !allImagesIntersection.empty(); } bool mitk::SemanticRelations::ControlPointContainsInformationType(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType, const SemanticTypes::ControlPoint& controlPoint) const { DataNodeVector allImagesIntersection = GetAllSpecificImages(caseID, controlPoint, informationType); return !allImagesIntersection.empty(); } void mitk::SemanticRelations::ClearControlPoints(const SemanticTypes::CaseID& caseID) { ControlPointVector allControlPointsOfCase; try { allControlPointsOfCase = GetAllControlPointsOfCase(caseID); } catch (const SemanticRelationException&) { // error retrieving control point data return; } DataNodeVector allImagesOfCase; try { allImagesOfCase = GetAllImagesOfCase(caseID); } catch (const SemanticRelationException&) { // error retrieving image data return; } ControlPointVector allControlPoints; for (const auto& image : allImagesOfCase) { try { allControlPoints.push_back(GetControlPointOfData(image)); } catch (const SemanticRelationException&) { // error retrieving control point of data continue; } } std::sort(allControlPointsOfCase.begin(), allControlPointsOfCase.end()); std::sort(allControlPoints.begin(), allControlPoints.end()); ControlPointVector allControlPointsDifference; std::set_difference(allControlPointsOfCase.begin(), allControlPointsOfCase.end(), allControlPoints.begin(), allControlPoints.end(), std::inserter(allControlPointsDifference, allControlPointsDifference.begin())); auto allExaminationPeriods = GetAllExaminationPeriodsOfCase(caseID); for (const auto& controlPoint : allControlPointsDifference) { const auto& examinationPeriod = FindExaminationPeriod(controlPoint, allExaminationPeriods); m_RelationStorage->RemoveControlPointFromExaminationPeriod(caseID, controlPoint, examinationPeriod); m_RelationStorage->RemoveControlPointFromCase(caseID, controlPoint); } } diff --git a/Modules/SemanticRelationsUI/files.cmake b/Modules/SemanticRelationsUI/files.cmake index dc27298082..fada33de90 100644 --- a/Modules/SemanticRelationsUI/files.cmake +++ b/Modules/SemanticRelationsUI/files.cmake @@ -1,26 +1,29 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkModuleActivator.cpp QmitkAbstractSemanticRelationsStorageModel.cpp QmitkControlPointDialog.cpp QmitkLesionTextDialog.cpp + QmitkLesionTreeItem.cpp + QmitkLesionTreeModel.cpp QmitkPatientTableHeaderView.cpp QmitkPatientTableInspector.cpp QmitkPatientTableModel.cpp QmitkSemanticRelationsUIHelper.cpp ) set(MOC_H_FILES include/QmitkAbstractSemanticRelationsStorageInspector.h include/QmitkAbstractSemanticRelationsStorageModel.h include/QmitkControlPointDialog.h include/QmitkLesionTextDialog.h + include/QmitkLesionTreeModel.h include/QmitkPatientTableHeaderView.h include/QmitkPatientTableInspector.h include/QmitkPatientTableModel.h ) set(UI_FILES src/QmitkPatientTableInspector.ui ) diff --git a/Modules/SemanticRelationsUI/include/QmitkAbstractSemanticRelationsStorageModel.h b/Modules/SemanticRelationsUI/include/QmitkAbstractSemanticRelationsStorageModel.h index abf0253036..a69a42bd56 100644 --- a/Modules/SemanticRelationsUI/include/QmitkAbstractSemanticRelationsStorageModel.h +++ b/Modules/SemanticRelationsUI/include/QmitkAbstractSemanticRelationsStorageModel.h @@ -1,104 +1,107 @@ /*=================================================================== 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 QMITKABSTRACTSEMANTICRELATIONSSTORAGEMODEL_H #define QMITKABSTRACTSEMANTICRELATIONSSTORAGEMODEL_H +// mitk semantic relations UI +#include "MitkSemanticRelationsUIExports.h" + // semantic relations module #include #include #include // qt widgets module #include "QmitkAbstractDataStorageModel.h" /* * @brief The QmitkAbstractSemanticRelationsStorageModel is a subclass of 'QmitkAbstractDataStorageModel' and provides additional * functionality to set and store a semantic relations instance, the current case ID and the current lesion. */ -class QmitkAbstractSemanticRelationsStorageModel : public QmitkAbstractDataStorageModel, public mitk::ISemanticRelationsObserver +class MITKSEMANTICRELATIONSUI_EXPORT QmitkAbstractSemanticRelationsStorageModel : public QmitkAbstractDataStorageModel, public mitk::ISemanticRelationsObserver { Q_OBJECT public: QmitkAbstractSemanticRelationsStorageModel(QObject* parent = nullptr); virtual ~QmitkAbstractSemanticRelationsStorageModel(); /* * @brief Updates this model with the data from the semantic relations. * * Overridden from 'ISemanticRelationsObserver'. * In order for the Update-function to be called, this model has to be added as a observer of SemanticRelation * (e.g. m_SemanticRelations->AddObserver(m_SemanticRelationsStorageModel);) * * @par caseID The current case ID to identify the currently active patient / case. */ virtual void Update(const mitk::SemanticTypes::CaseID& caseID) override; std::shared_ptr GetSemanticRelations() const { return m_SemanticRelations; } /** * @brief Sets the current case ID which is needed to access the semantic relations storage. * * @param caseID A case ID as string */ void SetCaseID(const mitk::SemanticTypes::CaseID& caseID); const mitk::SemanticTypes::CaseID& GetCaseID() const { return m_CaseID; } /** * @brief Sets the current lesion which can be used to show on which images the lesion is visible. * * @param lesion The selected lesion */ void SetLesion(const mitk::SemanticTypes::Lesion& lesion); const mitk::SemanticTypes::Lesion& GetLesion() const { return m_Lesion; } /* * @brief Updates the semantic relations storage model with the current data from the semantic relations model, * if the case ID is equal to the currently selected case ID of the table model. */ void UpdateModelData(const mitk::SemanticTypes::CaseID& caseID); /* * @brief Updates the semantic relations storage model with the current data from the semantic relations model * and the current case ID. */ void UpdateModelData(); Q_SIGNALS: void ModelUpdated(); protected: /** * @brief Creates a new 'SemanticRelations' instance with the new data storage and updates the model data. * This functions is called inside the 'SetDataStorage'-function from the parent class. */ virtual void DataStorageChanged() override; /** * @brief This function is called if the model data is updated. It can be used by subclasses to define * the way the data of a specific model is generated. It typically consists of access to the * semantic relations storage to retrieve certain information. */ virtual void SetData() = 0; std::shared_ptr m_SemanticRelations; mitk::SemanticTypes::CaseID m_CaseID; mitk::SemanticTypes::Lesion m_Lesion; }; #endif // QMITKABSTRACTSEMANTICRELATIONSSTORAGEMODEL_H diff --git a/Modules/SemanticRelationsUI/include/QmitkLesionTreeItem.h b/Modules/SemanticRelationsUI/include/QmitkLesionTreeItem.h new file mode 100644 index 0000000000..2cd05eec35 --- /dev/null +++ b/Modules/SemanticRelationsUI/include/QmitkLesionTreeItem.h @@ -0,0 +1,123 @@ +/*=================================================================== + +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 QMITKLESIONTREEITEM_H +#define QMITKLESIONTREEITEM_H + +// mitk semantic relations UI +#include "mitkLesionData.h" + +// mitk semantic relations +#include + +// qt +#include + +// c++ +#include +#include + +/* +* @brief +*/ +class QmitkLesionTreeItem : public std::enable_shared_from_this +{ +public: + + using ChildPointer = std::shared_ptr; + using ChildConstPointer = std::shared_ptr; + using ParentPointer = std::weak_ptr; + + QmitkLesionTreeItem(mitk::LesionData lesionData = mitk::LesionData()); + ~QmitkLesionTreeItem(); + + /** + * @brief Return the child of this item at a specific position. + * + * @param row Determines the position of a child item to return. + * + * @return The child of this item at a specific position. + */ + ChildPointer GetChildInRow(int row) const { return m_Children.at(row); }; + /** + * @brief Return the parent item. + * + * @return The parent item as std::weak_ptr. + */ + ParentPointer GetParent() const { return m_ParentItem; }; + /** + * @brief Set the parent item of this item. + * + * @param parent The new parent item of this item. + */ + void SetParent(ParentPointer parent); + /** + * @brief Return the item data, which contains ... + * + * see mitk::LesionItemData + */ + mitk::LesionData& GetData() { return m_ItemData; }; + /** + * @brief Get the row of this item relative to its parent item using 'GetRowOfChild'. + * + * @return The row of this item relative to its parent item. + */ + int GetRow() const; + /** + * @brief Get the row of the given child item relative to this item. + * + * @param child The child item whose row is to be determined. + * + * @return The row of the child item. + */ + int GetRowOfChild(ChildConstPointer child) const; + /** + * @brief Return the number of child items. + * + * @return Number of child items. + */ + size_t ChildCount() const { return m_Children.size(); }; + /** + * @brief Add a new child to the list of children of this item if it is not already a child item. + * + * @param child The child item to add to this item. + */ + void AddChild(ChildPointer child); + /** + * @brief Remove a child from the list of children of this item. + * + * @param child The child item to remove from this item. + */ + void RemoveChild(ChildPointer child); + /** + * @brief Set the item data of this item. + * + * @param value LesionData that provides information about this item. + */ + void SetData(const mitk::LesionData& lesionData); + +private: + + ParentPointer m_ParentItem; + std::vector m_Children; + mitk::LesionData m_ItemData; + +}; + +Q_DECLARE_METATYPE(QmitkLesionTreeItem) +Q_DECLARE_METATYPE(QmitkLesionTreeItem*) + +#endif // QMITKLESIONTREEITEM_H diff --git a/Modules/SemanticRelationsUI/include/QmitkLesionTreeModel.h b/Modules/SemanticRelationsUI/include/QmitkLesionTreeModel.h new file mode 100644 index 0000000000..85b9bdd53a --- /dev/null +++ b/Modules/SemanticRelationsUI/include/QmitkLesionTreeModel.h @@ -0,0 +1,85 @@ +/*=================================================================== + +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 QMITKLESIONTREEMODEL_H +#define QMITKLESIONTREEMODEL_H + +// mitk semantic relations UI +#include "MitkSemanticRelationsUIExports.h" +#include "QmitkAbstractSemanticRelationsStorageModel.h" +#include "QmitkLesionTreeItem.h" + +// c++ +#include + +/* +* @brief +*/ +class MITKSEMANTICRELATIONSUI_EXPORT QmitkLesionTreeModel : public QmitkAbstractSemanticRelationsStorageModel +{ + Q_OBJECT + +public: + + /** + * @brief Initialize the root item of the model. The root item does not have a parent item. + */ + QmitkLesionTreeModel(QObject* parent = nullptr); + ~QmitkLesionTreeModel(); + + ////////////////////////////////////////////////////////////////////////// + // overridden virtual functions from QAbstractItemModel + ////////////////////////////////////////////////////////////////////////// + virtual QModelIndex index(int row, int column, const QModelIndex& itemIndex = QModelIndex()) const override; + virtual QModelIndex parent(const QModelIndex& itemIndex) const override; + + virtual int rowCount(const QModelIndex& itemIndex = QModelIndex()) const override; + virtual int columnCount(const QModelIndex& itemIndex = QModelIndex()) const override; + + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + ////////////////////////////////////////////////////////////////////////// + // end override + ////////////////////////////////////////////////////////////////////////// + +protected: + + // the following functions have to be overridden but are not implemented in this model + virtual void NodePredicateChanged() override; + virtual void NodeAdded(const mitk::DataNode* node) override; + virtual void NodeChanged(const mitk::DataNode* node) override; + virtual void NodeRemoved(const mitk::DataNode* node) override; + /** + * @brief Overridden from 'QmitkAbstractSemanticRelationsStorageModel': This function retrieves all control points + * of the current case and stores them to define the header of the tree. + * Furthermore all lesions are retrieved and the lesion data is stored and show in the tree view. + */ + virtual void SetData() override; + +private: + + void SetLesionData(); + void AddLesion(const mitk::SemanticTypes::Lesion& lesion); + + QmitkLesionTreeItem* GetItemByIndex(const QModelIndex& index) const; + + std::shared_ptr m_RootItem; + std::vector m_ControlPoints; + +}; + +#endif // QMITKLESIONTREEMODEL_H diff --git a/Modules/SemanticRelationsUI/include/QmitkPatientTableInspector.h b/Modules/SemanticRelationsUI/include/QmitkPatientTableInspector.h index 54b62b526e..9d9cc16e0c 100644 --- a/Modules/SemanticRelationsUI/include/QmitkPatientTableInspector.h +++ b/Modules/SemanticRelationsUI/include/QmitkPatientTableInspector.h @@ -1,88 +1,88 @@ /*=================================================================== 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 QMITKPATIENTTABLEINSPECTOR_H #define QMITKPATIENTTABLEINSPECTOR_H // semantic relations UI module #include "MitkSemanticRelationsUIExports.h" -#include -#include +#include "QmitkAbstractSemanticRelationsStorageInspector.h" +#include "QmitkPatientTableModel.h" #include "ui_QmitkPatientTableInspector.h" // qt widgets module -#include "QmitkEnums.h" +#include // qt #include /* * @brief The QmitkPatientTableInspector is a QmitkAbstractSemanticRelationsStorageInspector that shows the currently * available data of the semantic relations storage model in a control-point - information type matrix. * * The QmitkPatientTableInspector uses the QmitkSemanticRelationsStorageModel, a QmitkAbstractDataStorageModel that * presents the semantic relations data as a table, showing a QPixmap as thumbnail for the data nodes. */ class MITKSEMANTICRELATIONSUI_EXPORT QmitkPatientTableInspector : public QmitkAbstractSemanticRelationsStorageInspector { Q_OBJECT public: QmitkPatientTableInspector(QWidget* parent = nullptr); virtual QAbstractItemView* GetView() override; virtual const QAbstractItemView* GetView() const override; virtual void SetSelectionMode(SelectionMode mode) override; virtual SelectionMode GetSelectionMode() const override; virtual void SetCaseID(const mitk::SemanticTypes::CaseID& caseID) override; virtual void SetLesion(const mitk::SemanticTypes::Lesion& lesion) override; QItemSelectionModel* GetSelectionModel(); Q_SIGNALS: void DataNodeDoubleClicked(const mitk::DataNode*); void OnContextMenuRequested(const QPoint&); void OnNodeRemoved(const mitk::DataNode*); private Q_SLOTS: void OnModelUpdated(); void OnNodeButtonClicked(const QString&); void OnItemDoubleClicked(const QModelIndex&); protected: virtual void Initialize() override; private: void SetUpConnections(); virtual void keyPressEvent(QKeyEvent* e) override; Ui::QmitkPatientTableInspector m_Controls; QmitkPatientTableModel* m_StorageModel; mitk::DataNode* m_SelectedDataNode; }; #endif // QMITKPATIENTTABLEINSPECTOR_H diff --git a/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h b/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h index c787fc44ce..6168490b83 100644 --- a/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h +++ b/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h @@ -1,125 +1,124 @@ /*=================================================================== 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 QMITKPATIENTTABLEMODEL_H #define QMITKPATIENTTABLEMODEL_H +// semantic relations UI module +#include "QmitkAbstractSemanticRelationsStorageModel.h" + // semantic relations module #include -// semantic relations UI module -#include - // mitk core #include // qt #include #include /* * @brief The QmitkPatientTableModel is a subclass of the QmitkAbstractSemanticRelationsStorageModel and holds the semantic relations data of the currently selected case. * * The QmitkPatientTableModel uses the 'data' function to return either the data node of a table cell or the thumbnail of the underlying image. * The horizontal header of the table shows the control points of the current case and the vertical header of the table shows the information types of the current case. * Using the 'GetFilteredData'-function of the SemanticRelations-class the model is able to retrieve the correct data node for each table entry. * * Additionally the model creates and holds the QPixmaps of the known data nodes in order to return a thumbnail, if needed. */ class QmitkPatientTableModel : public QmitkAbstractSemanticRelationsStorageModel { Q_OBJECT public: QmitkPatientTableModel(QObject* parent = nullptr); ~QmitkPatientTableModel(); ////////////////////////////////////////////////////////////////////////// // overridden functions from QAbstractItemModel ////////////////////////////////////////////////////////////////////////// - virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; - virtual QModelIndex parent(const QModelIndex &child) const override; + virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; + virtual QModelIndex parent(const QModelIndex& child) const override; - virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; - virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override; + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; + virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override; - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - virtual Qt::ItemFlags flags(const QModelIndex &index) const override; + virtual Qt::ItemFlags flags(const QModelIndex& index) const override; ////////////////////////////////////////////////////////////////////////// /// end override ///////////////////////////////////////////////////////////////////////// void SetNodeType(const std::string& nodeType); protected: // the following functions have to be overridden but are not implemented in this model virtual void NodePredicateChanged() override; virtual void NodeAdded(const mitk::DataNode* node) override; virtual void NodeChanged(const mitk::DataNode* node) override; virtual void NodeRemoved(const mitk::DataNode* node) override; /** * @brief Overridden from 'QmitkAbstractSemanticRelationsStorageModel': This function retrieves all control points * and information types of the current case and stores them to define the header of the table. * Furthermore all images are retrieved and the pixmap of the images are generated and stored. */ virtual void SetData() override; - void SetDataNodes(); - private: + void SetDataNodes(); void SetHeaderModel(); /** * @brief The function uses the ID of the node to see if a pixmap was already set. If not, the given pixmap * is used and stored inside a member variable. If the pixmap was already set, it will be overwritten. * Using 'nullptr' as a pixmap will erase the entry for the given data node. * * @param dataNode The data node whose pixmap should be set * @param pixmapFromImage The pixmap that shows an image of the content of the data node */ void SetPixmapOfNode(const mitk::DataNode* dataNode, QPixmap* pixmapFromImage); void SetLesionPresence(const mitk::DataNode* dataNode, bool lesionPresence); bool IsLesionPresentOnDataNode(const mitk::DataNode* dataNode) const; /* * @brief Returns the data node that is associated with the given table entry (index). * * The function uses the SemanticRelations-class and the current control point data and information type data to * filter the nodes of the current case. * The index is used to access the correct row in the table (information type) and the correct column in the table (control point). * * @par index The QModelIndex of the table entry */ mitk::DataNode* GetCurrentDataNode(const QModelIndex &index) const; std::map m_PixmapMap; std::map m_LesionPresence; std::vector m_InformationTypes; std::vector m_ControlPoints; std::string m_SelectedNodeType; QStandardItemModel* m_HeaderModel; }; #endif // QMITKPATIENTTABLEMODEL_H diff --git a/Modules/SemanticRelationsUI/src/QmitkLesionTreeItem.cpp b/Modules/SemanticRelationsUI/src/QmitkLesionTreeItem.cpp new file mode 100644 index 0000000000..a698270871 --- /dev/null +++ b/Modules/SemanticRelationsUI/src/QmitkLesionTreeItem.cpp @@ -0,0 +1,86 @@ +/*=================================================================== + +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. + +===================================================================*/ + +// semantic relations UI module +#include "QmitkLesionTreeItem.h" + +// qt +#include + +QmitkLesionTreeItem::QmitkLesionTreeItem(mitk::LesionData lesionData/* = mitk::LesionTreeItemData()*/) +{ + m_ItemData = lesionData; +} + +QmitkLesionTreeItem::~QmitkLesionTreeItem() +{ + // nothing here +} + +void QmitkLesionTreeItem::SetParent(ParentPointer parent) +{ + m_ParentItem = parent; +} + +int QmitkLesionTreeItem::GetRow() const +{ + if (m_ParentItem.expired()) + { + return 0; + } + + auto parentItem = m_ParentItem.lock(); + return parentItem->GetRowOfChild(this->shared_from_this()); +} + +int QmitkLesionTreeItem::GetRowOfChild(ChildConstPointer child) const +{ + auto it = std::find(m_Children.begin(), m_Children.end(), child); + if (it == m_Children.end()) + { + return -1; + } + else + { + return std::distance(m_Children.begin(), it); + } +} + +void QmitkLesionTreeItem::AddChild(ChildPointer child) +{ + auto it = std::find(m_Children.begin(), m_Children.end(), child); + if (it == m_Children.end()) + { + // child does not already exist; add to vector of children + m_Children.push_back(child); + // add parent item + child->SetParent(this->shared_from_this()); + } +} + +void QmitkLesionTreeItem::RemoveChild(ChildPointer child) +{ + auto it = std::find(m_Children.begin(), m_Children.end(), child); + if (it != m_Children.end()) + { + m_Children.erase(it); + } +} + +void QmitkLesionTreeItem::SetData(const mitk::LesionData& lesionData) +{ + m_ItemData = lesionData; +} diff --git a/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp b/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp new file mode 100644 index 0000000000..19005030f1 --- /dev/null +++ b/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp @@ -0,0 +1,258 @@ +/*=================================================================== + +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. + +===================================================================*/ + +// semantic relations UI module +#include "QmitkLesionTreeModel.h" + +// semantic relations module +#include +#include +#include + +QmitkLesionTreeModel::QmitkLesionTreeModel(QObject* parent/* = nullptr*/) + : QmitkAbstractSemanticRelationsStorageModel(parent) + , m_RootItem(std::make_shared(mitk::LesionData())) +{ + // nothing here +} + +QmitkLesionTreeModel::~QmitkLesionTreeModel() +{ + // nothing here +} + +////////////////////////////////////////////////////////////////////////// +// overridden virtual functions from QAbstractItemModel +////////////////////////////////////////////////////////////////////////// +QModelIndex QmitkLesionTreeModel::index(int row, int column, const QModelIndex& itemIndex) const +{ + if (!hasIndex(row, column, itemIndex)) + { + return QModelIndex(); + } + + auto childItem = GetItemByIndex(itemIndex)->GetChildInRow(row); + if (nullptr == childItem) + { + return QModelIndex(); + } + + return createIndex(row, column, childItem.get()); +} + +QModelIndex QmitkLesionTreeModel::parent(const QModelIndex& itemIndex) const +{ + if (!itemIndex.isValid()) + { + return QModelIndex(); + } + + auto parentItem = GetItemByIndex(itemIndex)->GetParent(); + if (parentItem.expired()) + { + return QModelIndex(); + } + + auto sharedParent = parentItem.lock(); + if (sharedParent == m_RootItem) + { + return QModelIndex(); + } + + return createIndex(sharedParent->GetRow(), 0, sharedParent.get()); +} + +int QmitkLesionTreeModel::rowCount(const QModelIndex& itemIndex/* = QModelIndex()*/) const +{ + return GetItemByIndex(itemIndex)->ChildCount(); +} + +int QmitkLesionTreeModel::columnCount(const QModelIndex&/* itemIndex = QModelIndex() */) const +{ + return m_ControlPoints.size() + 1; +} + +QVariant QmitkLesionTreeModel::data(const QModelIndex& index, int role) const +{ + if (!index.isValid()) + { + return QVariant(); + } + + if (index.column() < 0 || index.column() > static_cast(m_ControlPoints.size())) + { + return QVariant(); + } + + QmitkLesionTreeItem* currentItem = GetItemByIndex(index); + if (Qt::DisplayRole == role) + { + if (currentItem->GetParent().expired()) + { + return QVariant(); + } + + auto parentItem = currentItem->GetParent().lock(); + // parent exists and is the root item -> 1. item of a lesion entry + if (m_RootItem == parentItem) + { + // display role fills the first columns with the lesion UID / name + if (0 == index.column()) + { + std::string itemString = currentItem->GetData().GetLesionName(); + if (itemString.empty()) + { + itemString = currentItem->GetData().GetLesionUID(); + } + return QString::fromStdString(itemString); + } + else + { + // display role fills other columns with the lesion presence info + const auto lesionPresence = currentItem->GetData().GetLesionPresence(); + if (index.column() - 1 > static_cast(lesionPresence.size())) + { + return ""; + } + + return QVariant(lesionPresence.at(index.column() - 1)); + } + } + // parent is not the root item -> 2. item of a lesion entry + else + { + // display role fills the first columns with the property name "Volume" + if (0 == index.column()) + { + return "Volume"; + } + else + { + // display role fills other columns with the lesion volume info + const auto lesionVolume= currentItem->GetData().GetLesionVolume(); + if (index.column() - 1 > static_cast(lesionVolume.size())) + { + return ""; + } + return QVariant(lesionVolume.at(index.column() - 1)); + } + } + } + + if (Qt::UserRole == role) + { + return QVariant::fromValue(currentItem); + } + + return QVariant(); +} + +QVariant QmitkLesionTreeModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (Qt::Horizontal == orientation && Qt::DisplayRole == role) + { + if (0 == section) + { + return QVariant("Lesion"); + } + + if (m_ControlPoints.size() >= section) + { + mitk::SemanticTypes::ControlPoint currentControlPoint = m_ControlPoints.at(section-1); + // generate a string from the control point + std::string currentControlPointAsString = mitk::GetControlPointAsString(currentControlPoint); + return QVariant(QString::fromStdString(currentControlPointAsString)); + } + } + + return QVariant(); +} + +void QmitkLesionTreeModel::NodePredicateChanged() +{ + // does not react to node predicate changes +} + +void QmitkLesionTreeModel::NodeAdded(const mitk::DataNode* node) +{ + // does not react to data storage changes +} + +void QmitkLesionTreeModel::NodeChanged(const mitk::DataNode* node) +{ + // does not react to data storage changes +} + +void QmitkLesionTreeModel::NodeRemoved(const mitk::DataNode* node) +{ + // does not react to data storage changes +} + +void QmitkLesionTreeModel::SetData() +{ + m_RootItem = std::make_shared(mitk::LesionData()); + + // get all control points of current case + m_ControlPoints = m_SemanticRelations->GetAllControlPointsOfCase(m_CaseID); + // sort the vector of control points for the timeline + std::sort(m_ControlPoints.begin(), m_ControlPoints.end()); + + SetLesionData(); +} + +void QmitkLesionTreeModel::SetLesionData() +{ + std::vector allLesions = m_SemanticRelations->GetAllLesionsOfCase(m_CaseID); + + for (auto& lesion : allLesions) + { + AddLesion(lesion); + } +} + +void QmitkLesionTreeModel::AddLesion(const mitk::SemanticTypes::Lesion& lesion) +{ + if (nullptr == m_SemanticRelations) + { + return; + } + + // create new lesion tree item data and modify it according to the control point data + mitk::LesionData lesionData(lesion); + mitk::GenerateAdditionalLesionData(lesionData, m_CaseID, m_SemanticRelations); + + // add the 1. level lesion item to the root item + std::shared_ptr newLesionTreeItem = std::make_shared(lesionData); + m_RootItem->AddChild(newLesionTreeItem); + + // add the 2. level lesion item to the 1. level lesion item + std::shared_ptr newChildItem = std::make_shared(lesionData); + newLesionTreeItem->AddChild(newChildItem); +} + +QmitkLesionTreeItem* QmitkLesionTreeModel::GetItemByIndex(const QModelIndex& index) const +{ + if (index.isValid()) + { + auto item = static_cast(index.internalPointer()); + if (nullptr != item) + { + return item; + } + } + + return m_RootItem.get(); +} diff --git a/Modules/SemanticRelationsUI/src/QmitkPatientTableInspector.cpp b/Modules/SemanticRelationsUI/src/QmitkPatientTableInspector.cpp index 546dec060b..5d01d78a9a 100644 --- a/Modules/SemanticRelationsUI/src/QmitkPatientTableInspector.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkPatientTableInspector.cpp @@ -1,160 +1,166 @@ /*=================================================================== 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. ===================================================================*/ // semantic relations UI module #include "QmitkPatientTableInspector.h" #include "QmitkPatientTableHeaderView.h" // mitk qt widgets module #include "QmitkCustomVariants.h" #include "QmitkEnums.h" // qt #include #include QmitkPatientTableInspector::QmitkPatientTableInspector(QWidget* parent/* =nullptr*/) { m_Controls.setupUi(this); QmitkPatientTableHeaderView* patientTableHeaderView = new QmitkPatientTableHeaderView(m_Controls.tableView); m_Controls.tableView->setHorizontalHeader(patientTableHeaderView); m_Controls.tableView->horizontalHeader()->setHighlightSections(false); m_Controls.tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed); m_Controls.tableView->verticalHeader()->setHighlightSections(false); m_Controls.tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); m_Controls.tableView->setSelectionMode(QAbstractItemView::SingleSelection); m_Controls.tableView->setSelectionBehavior(QAbstractItemView::SelectItems); m_Controls.tableView->setContextMenuPolicy(Qt::CustomContextMenu); m_StorageModel = new QmitkPatientTableModel(this); - m_Controls.tableView->setModel(m_StorageModel); SetUpConnections(); } QAbstractItemView* QmitkPatientTableInspector::GetView() { return m_Controls.tableView; } const QAbstractItemView* QmitkPatientTableInspector::GetView() const { return m_Controls.tableView; } void QmitkPatientTableInspector::SetSelectionMode(SelectionMode mode) { m_Controls.tableView->setSelectionMode(mode); } QmitkPatientTableInspector::SelectionMode QmitkPatientTableInspector::GetSelectionMode() const { return m_Controls.tableView->selectionMode(); } void QmitkPatientTableInspector::SetCaseID(const mitk::SemanticTypes::CaseID& caseID) { m_StorageModel->SetCaseID(caseID); } void QmitkPatientTableInspector::SetLesion(const mitk::SemanticTypes::Lesion& lesion) { m_StorageModel->SetLesion(lesion); } QItemSelectionModel* QmitkPatientTableInspector::GetSelectionModel() { return m_Controls.tableView->selectionModel(); } void QmitkPatientTableInspector::Initialize() { - m_StorageModel->SetDataStorage(m_DataStorage.Lock()); + if (m_DataStorage.IsExpired()) + { + return; + } + + auto dataStorage = m_DataStorage.Lock(); + + m_StorageModel->SetDataStorage(dataStorage); m_StorageModel->SetNodePredicate(m_NodePredicate); m_Connector->SetView(m_Controls.tableView); } void QmitkPatientTableInspector::OnModelUpdated() { m_Controls.tableView->resizeRowsToContents(); m_Controls.tableView->resizeColumnsToContents(); } void QmitkPatientTableInspector::OnNodeButtonClicked(const QString& nodeType) { m_StorageModel->SetNodeType(nodeType.toStdString()); } void QmitkPatientTableInspector::OnItemDoubleClicked(const QModelIndex& itemIndex) { if (itemIndex.isValid()) { QVariant qvariantDataNode = m_StorageModel->data(itemIndex, QmitkDataNodeRawPointerRole); if (qvariantDataNode.canConvert()) { mitk::DataNode* dataNode = qvariantDataNode.value(); emit DataNodeDoubleClicked(dataNode); } } } void QmitkPatientTableInspector::SetUpConnections() { connect(m_StorageModel, &QmitkPatientTableModel::ModelUpdated, this, &QmitkPatientTableInspector::OnModelUpdated); connect(m_Controls.tableView, &QTableView::customContextMenuRequested, this, &QmitkPatientTableInspector::OnContextMenuRequested); QSignalMapper* nodeButtonSignalMapper = new QSignalMapper(this); nodeButtonSignalMapper->setMapping(m_Controls.imageNodeButton, QString("Image")); nodeButtonSignalMapper->setMapping(m_Controls.segmentationNodeButton, QString("Segmentation")); connect(nodeButtonSignalMapper, static_cast(&QSignalMapper::mapped), this, &QmitkPatientTableInspector::OnNodeButtonClicked); connect(m_Controls.imageNodeButton, SIGNAL(clicked()), nodeButtonSignalMapper, SLOT(map())); connect(m_Controls.segmentationNodeButton, SIGNAL(clicked()), nodeButtonSignalMapper, SLOT(map())); m_Controls.imageNodeButton->setChecked(true); connect(m_Controls.tableView, &QTableView::doubleClicked, this, &QmitkPatientTableInspector::OnItemDoubleClicked); } void QmitkPatientTableInspector::keyPressEvent(QKeyEvent* e) { mitk::DataNode* dataNode = nullptr; QModelIndex selectedIndex = m_Controls.tableView->currentIndex(); if (selectedIndex.isValid()) { QVariant qvariantDataNode = m_StorageModel->data(selectedIndex, QmitkDataNodeRawPointerRole); if (qvariantDataNode.canConvert()) { dataNode = qvariantDataNode.value(); } } if (nullptr == dataNode) { return; } int key = e->key(); switch (key) { case Qt::Key_Delete: emit OnNodeRemoved(dataNode); break; default: break; } } diff --git a/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp b/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp index d6fa8c2172..265ef8caa9 100644 --- a/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp @@ -1,365 +1,368 @@ /*=================================================================== 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. ===================================================================*/ // semantic relations UI module #include "QmitkPatientTableModel.h" #include "QmitkPatientTableHeaderView.h" #include "QmitkSemanticRelationsUIHelper.h" // semantic relations module +#include #include #include #include #include // qt #include QmitkPatientTableModel::QmitkPatientTableModel(QObject* parent /*= nullptr*/) : QmitkAbstractSemanticRelationsStorageModel(parent) , m_SelectedNodeType("Image") { m_HeaderModel = new QStandardItemModel(this); } QmitkPatientTableModel::~QmitkPatientTableModel() { // nothing here } -QModelIndex QmitkPatientTableModel::index(int row, int column, const QModelIndex &parent/* = QModelIndex()*/) const +QModelIndex QmitkPatientTableModel::index(int row, int column, const QModelIndex& parent/* = QModelIndex()*/) const { - bool hasIndex = this->hasIndex(row, column, parent); - if (hasIndex) + if (hasIndex(row, column, parent)) { return createIndex(row, column); } return QModelIndex(); } -QModelIndex QmitkPatientTableModel::parent(const QModelIndex &child) const +QModelIndex QmitkPatientTableModel::parent(const QModelIndex& child) const { return QModelIndex(); } -int QmitkPatientTableModel::rowCount(const QModelIndex &parent/* = QModelIndex()*/) const +int QmitkPatientTableModel::rowCount(const QModelIndex& parent/* = QModelIndex()*/) const { if (parent.isValid()) { return 0; } return m_InformationTypes.size(); } -int QmitkPatientTableModel::columnCount(const QModelIndex &parent/* = QModelIndex()*/) const +int QmitkPatientTableModel::columnCount(const QModelIndex& parent/* = QModelIndex()*/) const { return m_ControlPoints.size(); } -QVariant QmitkPatientTableModel::data(const QModelIndex &index, int role/* = Qt::DisplayRole*/) const +QVariant QmitkPatientTableModel::data(const QModelIndex& index, int role/* = Qt::DisplayRole*/) const { if (QmitkPatientTableHeaderView::HorizontalHeaderDataRole == role) { return QVariant::fromValue(m_HeaderModel); } if (!index.isValid()) { return QVariant(); } if (index.row() < 0 || index.row() >= static_cast(m_InformationTypes.size()) || index.column() < 0 || index.column() >= static_cast(m_ControlPoints.size())) { return QVariant(); } mitk::DataNode* dataNode = GetCurrentDataNode(index); if (nullptr == dataNode) { return QVariant(); } if (Qt::DecorationRole == role) { auto it = m_PixmapMap.find(dataNode); if (it != m_PixmapMap.end()) { return QVariant(it->second); } } - else if (QmitkDataNodeRole == role) + + if (QmitkDataNodeRole == role) { return QVariant::fromValue(mitk::DataNode::Pointer(dataNode)); } - else if (QmitkDataNodeRawPointerRole == role) + + if (QmitkDataNodeRawPointerRole == role) { return QVariant::fromValue(dataNode); } - else if (Qt::BackgroundColorRole == role) + + if (Qt::BackgroundColorRole == role) { auto it = m_LesionPresence.find(dataNode); if (it != m_LesionPresence.end()) { return it->second ? QVariant(QColor(Qt::green)) : QVariant(QColor(Qt::transparent)); } return QVariant(QColor(Qt::transparent)); } return QVariant(); } QVariant QmitkPatientTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (Qt::Vertical == orientation && Qt::DisplayRole == role) { if (m_InformationTypes.size() > section) { mitk::SemanticTypes::InformationType currentInformationType = m_InformationTypes.at(section); return QVariant(QString::fromStdString(currentInformationType)); } } return QVariant(); } -Qt::ItemFlags QmitkPatientTableModel::flags(const QModelIndex &index) const +Qt::ItemFlags QmitkPatientTableModel::flags(const QModelIndex& index) const { Qt::ItemFlags flags; mitk::DataNode* dataNode = GetCurrentDataNode(index); if (nullptr != dataNode) { flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; } return flags; } void QmitkPatientTableModel::SetNodeType(const std::string& nodeType) { m_SelectedNodeType = nodeType; UpdateModelData(); } void QmitkPatientTableModel::NodePredicateChanged() { UpdateModelData(); } void QmitkPatientTableModel::NodeAdded(const mitk::DataNode* node) { // does not react to data storage changes } void QmitkPatientTableModel::NodeChanged(const mitk::DataNode* node) { // nothing here, since the "'NodeChanged'-event is currently sent far too often //UpdateModelData(); } void QmitkPatientTableModel::NodeRemoved(const mitk::DataNode* node) { // does not react to data storage changes } void QmitkPatientTableModel::SetPixmapOfNode(const mitk::DataNode* dataNode, QPixmap* pixmapFromImage) { if (nullptr == dataNode) { return; } std::map::iterator iter = m_PixmapMap.find(dataNode); if (iter != m_PixmapMap.end()) { // key already existing if (nullptr != pixmapFromImage) { // overwrite already stored pixmap iter->second = pixmapFromImage->scaled(120, 120, Qt::IgnoreAspectRatio); } else { // remove key if no pixmap is given m_PixmapMap.erase(iter); } } else { m_PixmapMap.insert(std::make_pair(dataNode, pixmapFromImage->scaled(120, 120, Qt::IgnoreAspectRatio))); } } void QmitkPatientTableModel::SetLesionPresence(const mitk::DataNode* dataNode, bool lesionPresence) { if (nullptr == dataNode) { return; } std::map::iterator iter = m_LesionPresence.find(dataNode); if (iter != m_LesionPresence.end()) { // key already existing, overwrite already stored bool value iter->second = lesionPresence; } else { m_LesionPresence.insert(std::make_pair(dataNode, lesionPresence)); } } void QmitkPatientTableModel::SetData() { // get all control points of current case m_ControlPoints = m_SemanticRelations->GetAllControlPointsOfCase(m_CaseID); // sort the vector of control points for the timeline std::sort(m_ControlPoints.begin(), m_ControlPoints.end()); // get all information types points of current case m_InformationTypes = m_SemanticRelations->GetAllInformationTypesOfCase(m_CaseID); m_PixmapMap.clear(); m_LesionPresence.clear(); SetDataNodes(); SetHeaderModel(); } void QmitkPatientTableModel::SetDataNodes() { std::vector allDataNodes; if("Image" == m_SelectedNodeType) { allDataNodes = m_SemanticRelations->GetAllImagesOfCase(m_CaseID); } else if("Segmentation" == m_SelectedNodeType) { allDataNodes = m_SemanticRelations->GetAllSegmentationsOfCase(m_CaseID); } for (const auto& dataNode : allDataNodes) { // set the pixmap for the current node QPixmap pixmapFromImage = QmitkSemanticRelationsUIHelper::GetPixmapFromImageNode(dataNode); SetPixmapOfNode(dataNode, &pixmapFromImage); // set the lesion presence for the current node if (m_SemanticRelations->InstanceExists(m_CaseID, m_Lesion)) { bool lesionPresence = lesionPresence = IsLesionPresentOnDataNode(dataNode); SetLesionPresence(dataNode, lesionPresence); } else { m_LesionPresence.clear(); } } } void QmitkPatientTableModel::SetHeaderModel() { m_HeaderModel->clear(); QStandardItem* rootItem = new QStandardItem("Timeline"); QList standardItems; for (const auto& controlPoint : m_ControlPoints) { std::string controlPointAsString = mitk::GetControlPointAsString(controlPoint); QStandardItem* standardItem = new QStandardItem(QString::fromStdString(controlPointAsString)); standardItems.push_back(standardItem); rootItem->appendColumn(standardItems); standardItems.clear(); } m_HeaderModel->setItem(0, 0, rootItem); } bool QmitkPatientTableModel::IsLesionPresentOnDataNode(const mitk::DataNode* dataNode) const { if (m_DataStorage.IsExpired()) { return false; } auto dataStorage = m_DataStorage.Lock(); try { mitk::SemanticRelations::DataNodeVector allSegmentationsOfLesion = m_SemanticRelations->GetAllSegmentationsOfLesion(m_CaseID, m_Lesion); for (const auto& segmentation : allSegmentationsOfLesion) { if ("Segmentation" == m_SelectedNodeType) { if (dataNode == segmentation) { // found a segmentation of the node that is represented by the selected lesion return true; } } else if ("Image" == m_SelectedNodeType) { // get parent node of the current segmentation node with the node predicate mitk::DataStorage::SetOfObjects::ConstPointer parentNodes = dataStorage->GetSources(segmentation, mitk::NodePredicates::GetImagePredicate(), false); for (auto it = parentNodes->Begin(); it != parentNodes->End(); ++it) { if (dataNode == it.Value()) { // found a segmentation of the node that is represented by the selected lesion return true; } } } } } catch (const mitk::SemanticRelationException&) { return false; } return false; } mitk::DataNode* QmitkPatientTableModel::GetCurrentDataNode(const QModelIndex& index) const { mitk::SemanticTypes::ControlPoint currentControlPoint = m_ControlPoints.at(index.column()); mitk::SemanticTypes::InformationType currentInformationType = m_InformationTypes.at(index.row()); try { std::vector filteredDataNodes; if ("Image" == m_SelectedNodeType) { filteredDataNodes = m_SemanticRelations->GetAllSpecificImages(m_CaseID, currentControlPoint, currentInformationType); } else if ("Segmentation" == m_SelectedNodeType) { filteredDataNodes = m_SemanticRelations->GetAllSpecificSegmentations(m_CaseID, currentControlPoint, currentInformationType); } if (filteredDataNodes.empty()) { return nullptr; } return filteredDataNodes.front(); } catch (const mitk::SemanticRelationException&) { return nullptr; } } diff --git a/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionDialog.cpp b/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionDialog.cpp index 1406db1518..9f89bb1c4e 100644 --- a/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionDialog.cpp +++ b/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionDialog.cpp @@ -1,186 +1,185 @@ /*=================================================================== 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 "QmitkNodeSelectionDialog.h" #include #include #include QmitkNodeSelectionDialog::QmitkNodeSelectionDialog(QWidget* parent, QString title, QString hint) : QDialog(parent), m_NodePredicate(nullptr), m_SelectOnlyVisibleNodes(false), m_SelectedNodes(NodeList()), m_SelectionMode(QAbstractItemView::SingleSelection) { m_Controls.setupUi(this); auto providers = mitk::DataStorageInspectorGenerator::GetProviders(); auto visibleProviders = mitk::GetVisibleDataStorageInspectors(); auto favoriteID = mitk::GetFavoriteDataStorageInspector(); if (visibleProviders.empty()) { MITK_DEBUG << "No presets for visible node selection inspectors available. Use fallback (show all available inspectors)"; unsigned int order = 0; for (auto proIter : providers) { visibleProviders.insert(std::make_pair(order, proIter.first)); } } int favIndex = 0; bool favoriteFound = false; for (auto proIter : visibleProviders) { auto finding = providers.find(proIter.second); if (finding != providers.end()) { auto inspector = finding->second->CreateInspector(); QString name = QString::fromStdString(finding->second->GetInspectorDisplayName()); QString desc = QString::fromStdString(finding->second->GetInspectorDescription()); AddPanel(inspector, name, desc); favoriteFound = favoriteFound || proIter.second == favoriteID; if (!favoriteFound) { ++favIndex; } } else { MITK_DEBUG << "No provider registered for inspector that is defined as visible in the preferences. Illegal inspector ID: " << proIter.second; } } m_Controls.tabWidget->setCurrentIndex(favIndex); this->setWindowTitle(title); this->setToolTip(hint); m_Controls.hint->setText(hint); m_Controls.hint->setVisible(!hint.isEmpty()); connect(m_Controls.buttonBox, SIGNAL(accepted()), this, SLOT(OnOK())); connect(m_Controls.buttonBox, SIGNAL(rejected()), this, SLOT(OnCancel())); } void QmitkNodeSelectionDialog::SetDataStorage(mitk::DataStorage* dataStorage) { if (m_DataStorage != dataStorage) { m_DataStorage = dataStorage; if (!m_DataStorage.IsExpired()) { for (auto panel : m_Panels) { panel->SetDataStorage(dataStorage); } } } } void QmitkNodeSelectionDialog::SetNodePredicate(mitk::NodePredicateBase* nodePredicate) { if (m_NodePredicate != nodePredicate) { m_NodePredicate = nodePredicate; for (auto panel : m_Panels) { panel->SetNodePredicate(m_NodePredicate); } } } mitk::NodePredicateBase* QmitkNodeSelectionDialog::GetNodePredicate() const { return m_NodePredicate; } QmitkNodeSelectionDialog::NodeList QmitkNodeSelectionDialog::GetSelectedNodes() const { return m_SelectedNodes; } void QmitkNodeSelectionDialog::SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes) { if (m_SelectOnlyVisibleNodes != selectOnlyVisibleNodes) { m_SelectOnlyVisibleNodes = selectOnlyVisibleNodes; for (auto panel : m_Panels) { panel->SetSelectOnlyVisibleNodes(m_SelectOnlyVisibleNodes); } } } void QmitkNodeSelectionDialog::SetCurrentSelection(NodeList selectedNodes) { m_SelectedNodes = selectedNodes; for (auto panel : m_Panels) { panel->SetCurrentSelection(selectedNodes); } } void QmitkNodeSelectionDialog::OnSelectionChanged(NodeList selectedNodes) { SetCurrentSelection(selectedNodes); emit CurrentSelectionChanged(selectedNodes); } void QmitkNodeSelectionDialog::AddPanel(QmitkAbstractDataStorageInspector* view, QString name, QString desc) { view->setParent(this); view->SetSelectionMode(m_SelectionMode); auto tabPanel = new QWidget(); tabPanel->setObjectName(QString("tab_")+name); tabPanel->setToolTip(desc); m_Controls.tabWidget->insertTab(m_Controls.tabWidget->count(), tabPanel, name); auto verticalLayout = new QVBoxLayout(tabPanel); verticalLayout->setSpacing(0); verticalLayout->setContentsMargins(0, 0, 0, 0); verticalLayout->addWidget(view); m_Panels.push_back(view); connect(view, &QmitkAbstractDataStorageInspector::CurrentSelectionChanged, this, &QmitkNodeSelectionDialog::OnSelectionChanged); } void QmitkNodeSelectionDialog::OnOK() { this->accept(); } void QmitkNodeSelectionDialog::OnCancel() { this->reject(); } void QmitkNodeSelectionDialog::SetSelectionMode(SelectionMode mode) { m_SelectionMode = mode; for (auto panel : m_Panels) { panel->SetSelectionMode(mode); } } QmitkNodeSelectionDialog::SelectionMode QmitkNodeSelectionDialog::GetSelectionMode() const { return m_SelectionMode; } diff --git a/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionDialog.h b/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionDialog.h index 02f828bcc5..480655c53c 100644 --- a/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionDialog.h +++ b/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionDialog.h @@ -1,127 +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. ===================================================================*/ - #ifndef QMITK_NODE_SELECTION_DIALOG_H #define QMITK_NODE_SELECTION_DIALOG_H - #include #include #include #include #include "org_mitk_gui_qt_common_Export.h" #include "ui_QmitkNodeSelectionDialog.h" #include /** * \class QmitkNodeSelectionDialog * \brief Widget that allows to show and edit the content of an mitk::IsoDoseLevel instance. */ class MITK_QT_COMMON QmitkNodeSelectionDialog : public QDialog { Q_OBJECT public: explicit QmitkNodeSelectionDialog(QWidget* parent = nullptr, QString caption = "", QString hint = ""); /* * @brief Sets the data storage that will be used /monitored by widget. * * @param dataStorage A pointer to the data storage to set. */ void SetDataStorage(mitk::DataStorage* dataStorage); /* * @brief Sets the node predicate and updates the widget, according to the node predicate. * * @param nodePredicate A pointer to node predicate. */ virtual void SetNodePredicate(mitk::NodePredicateBase* nodePredicate); mitk::NodePredicateBase* GetNodePredicate() const; using NodeList = QList; NodeList GetSelectedNodes() const; bool GetSelectOnlyVisibleNodes() const; using SelectionMode = QAbstractItemView::SelectionMode; void SetSelectionMode(SelectionMode mode); SelectionMode GetSelectionMode() const; Q_SIGNALS: /* * @brief A signal that will be emitted if the selected node has changed. * * @param nodes A list of data nodes that are newly selected. */ void CurrentSelectionChanged(NodeList nodes); public Q_SLOTS: /* * @brief Change the selection modus of the item view's selection model. * * If true, an incoming selection will be filtered (reduced) to only those nodes that are visible by the current view. * An outgoing selection can then at most contain the filtered nodes. * If false, the incoming non-visible selection will be stored and later added to the outgoing selection, * to include the original selection that could not be modified. * The part of the original selection, that is non-visible are the nodes that are not * * @param selectOnlyVisibleNodes The bool value to define the selection modus. */ void SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes); /* * @brief Transform a list of data nodes into a model selection and set this as a new selection of the * selection model of the private member item view. * * The function filters the given list of nodes according to the 'm_SelectOnlyVisibleNodes' member variable. If * necessary, the non-visible nodes are stored. This is done if 'm_SelectOnlyVisibleNodes' is false: In this case * the selection may be filtered and only a subset of the selected nodes may be visible and therefore (de-)selectable * in the data storage viewer. By storing the non-visible nodes it is possible to send the new, modified selection * but also include the selected nodes from the original selection that could not be modified (see 'SetSelectOnlyVisibleNodes'). * * @param nodes A list of data nodes that should be newly selected. */ void SetCurrentSelection(NodeList selectedNodes); protected Q_SLOTS: void OnSelectionChanged(NodeList selectedNodes); void OnOK(); void OnCancel(); protected: void AddPanel(QmitkAbstractDataStorageInspector* view, QString name, QString desc); mitk::WeakPointer m_DataStorage; mitk::NodePredicateBase::Pointer m_NodePredicate; bool m_SelectOnlyVisibleNodes; NodeList m_SelectedNodes; SelectionMode m_SelectionMode; using PanelVectorType = std::vector; PanelVectorType m_Panels; Ui_QmitkNodeSelectionDialog m_Controls; }; #endif // QmitkNodeSelectionDialog_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/files.cmake b/Plugins/org.mitk.gui.qt.semanticrelations/files.cmake index a79146ff6e..32a1308816 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/files.cmake +++ b/Plugins/org.mitk.gui.qt.semanticrelations/files.cmake @@ -1,42 +1,44 @@ set(INTERNAL_CPP_FILES mitkPluginActivator.cpp QmitkDataNodeAddToSemanticRelationsAction.cpp QmitkDataNodeInformationTypeAction.cpp QmitkDataNodeRemoveFromSemanticRelationsAction.cpp + QmitkDataNodeUnlinkFromLesionAction.cpp QmitkDataNodeOpenInAction.cpp QmitkDataNodeSetControlPointAction.cpp QmitkLesionInfoWidget.cpp QmitkSemanticRelationsContextMenu.cpp QmitkSemanticRelationsNodeSelectionDialog.cpp QmitkSemanticRelationsView.cpp ) set(UI_FILES src/internal/QmitkLesionInfoWidgetControls.ui src/internal/QmitkSemanticRelationsControls.ui ) set(MOC_H_FILES src/internal/mitkPluginActivator.h src/internal/QmitkDataNodeAddToSemanticRelationsAction.h src/internal/QmitkDataNodeInformationTypeAction.h src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.h + src/internal/QmitkDataNodeUnlinkFromLesionAction.h src/internal/QmitkDataNodeOpenInAction.h src/internal/QmitkDataNodeSetControlPointAction.h src/internal/QmitkLesionInfoWidget.h src/internal/QmitkSemanticRelationsContextMenu.h src/internal/QmitkSemanticRelationsNodeSelectionDialog.h src/internal/QmitkSemanticRelationsView.h ) set(CACHED_RESOURCE_FILES resources/SemanticRelations_48.png plugin.xml ) set(QRC_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.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.cpp index 3e86b3169e..5233610e9f 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.cpp @@ -1,193 +1,188 @@ /*=================================================================== 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. ===================================================================*/ // semantic relations plugin #include "QmitkDataNodeRemoveFromSemanticRelationsAction.h" // semantic relations module #include #include #include // mitk gui common plugin #include // berry #include #include // qt #include // namespace that contains the concrete action namespace RemoveFromSemanticRelationsAction { - void Run(mitk::SemanticRelations* semanticRelations, const mitk::DataStorage* dataStorage, const mitk::DataNode* dataNode) + void Run(mitk::SemanticRelations* semanticRelations, const mitk::DataNode* dataNode) { if (nullptr == dataNode) { return; } if (mitk::NodePredicates::GetImagePredicate()->CheckNode(dataNode)) { RemoveImage(semanticRelations, dataNode); } else if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { - RemoveSegmentation(semanticRelations, dataStorage, dataNode); + RemoveSegmentation(semanticRelations, dataNode); } } void RemoveImage(mitk::SemanticRelations* semanticRelations, const mitk::DataNode* image) { if (nullptr == image) { return; } try { // remove the image from the semantic relations storage semanticRelations->RemoveImage(image); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox; msgBox.setWindowTitle("Could not remove the selected image."); msgBox.setText("The program wasn't able to correctly remove the selected image.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } } - void RemoveSegmentation(mitk::SemanticRelations* semanticRelations, const mitk::DataStorage* dataStorage, const mitk::DataNode* segmentation) + void RemoveSegmentation(mitk::SemanticRelations* semanticRelations, const mitk::DataNode* segmentation) { if (nullptr == segmentation) { return; } try { semanticRelations->RemoveSegmentation(segmentation); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox; msgBox.setWindowTitle("Could not remove the selected segmentation."); msgBox.setText("The program wasn't able to correctly remove the selected segmentation.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } } } QmitkDataNodeRemoveFromSemanticRelationsAction::QmitkDataNodeRemoveFromSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) : QAction(parent) , m_WorkbenchPartSite(workbenchPartSite) { setText(tr("Remove from semantic relations")); InitializeAction(); } QmitkDataNodeRemoveFromSemanticRelationsAction::QmitkDataNodeRemoveFromSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) : QAction(parent) , m_WorkbenchPartSite(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) { setText(tr("Remove from semantic relations")); InitializeAction(); } QmitkDataNodeRemoveFromSemanticRelationsAction::~QmitkDataNodeRemoveFromSemanticRelationsAction() { // nothing here } void QmitkDataNodeRemoveFromSemanticRelationsAction::SetDataStorage(mitk::DataStorage* dataStorage) { if (m_DataStorage != dataStorage) { // set the new data storage m_DataStorage = dataStorage; m_SemanticRelations = std::make_unique(m_DataStorage.Lock()); } } void QmitkDataNodeRemoveFromSemanticRelationsAction::InitializeAction() { connect(this, &QAction::triggered, this, &QmitkDataNodeRemoveFromSemanticRelationsAction::OnActionTriggered); } void QmitkDataNodeRemoveFromSemanticRelationsAction::OnActionTriggered(bool checked) { if (nullptr == m_SemanticRelations) { return; } - if (m_DataStorage.IsExpired()) - { - return; - } - auto dataNode = GetSelectedNode(); - RemoveFromSemanticRelationsAction::Run(m_SemanticRelations.get(), m_DataStorage.Lock(), dataNode); + RemoveFromSemanticRelationsAction::Run(m_SemanticRelations.get(), dataNode); } QList QmitkDataNodeRemoveFromSemanticRelationsAction::GetSelectedNodes() { QList selectedNodes; if (m_WorkbenchPartSite.Expired()) { return selectedNodes; } berry::ISelection::ConstPointer selection = m_WorkbenchPartSite.Lock()->GetWorkbenchWindow()->GetSelectionService()->GetSelection(); mitk::DataNodeSelection::ConstPointer currentSelection = selection.Cast(); if (currentSelection.IsNull() || currentSelection->IsEmpty()) { return selectedNodes; } selectedNodes = QList::fromStdList(currentSelection->GetSelectedDataNodes()); return selectedNodes; } mitk::DataNode::Pointer QmitkDataNodeRemoveFromSemanticRelationsAction::GetSelectedNode() { QList selectedNodes = GetSelectedNodes(); if (selectedNodes.empty()) { return nullptr; } // no batch action; should only be called with a single node mitk::DataNode::Pointer dataNode = selectedNodes.front(); if (nullptr == dataNode) { return nullptr; } return dataNode; -} \ No newline at end of file +} diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.h index cf48d0ef33..eaa4bd2977 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.h @@ -1,73 +1,73 @@ /*=================================================================== 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 QMITKDATANODEREMOVEFROMSEMANTICRELATIONSACTION_H #define QMITKDATANODEREMOVEFROMSEMANTICRELATIONSACTION_H #include // mitk core #include #include #include // semantic relations module #include // berry #include // qt #include namespace RemoveFromSemanticRelationsAction { - MITK_GUI_SEMANTICRELATIONS_EXPORT void Run(mitk::SemanticRelations* semanticRelations, const mitk::DataStorage* dataStorage, const mitk::DataNode* image); - + MITK_GUI_SEMANTICRELATIONS_EXPORT void Run(mitk::SemanticRelations* semanticRelations, const mitk::DataNode* dataNode); + void RemoveImage(mitk::SemanticRelations* semanticRelations, const mitk::DataNode* image); - void RemoveSegmentation(mitk::SemanticRelations* semanticRelations, const mitk::DataStorage* dataStorage, const mitk::DataNode* segmentation); + void RemoveSegmentation(mitk::SemanticRelations* semanticRelations, const mitk::DataNode* segmentation); } class MITK_GUI_SEMANTICRELATIONS_EXPORT QmitkDataNodeRemoveFromSemanticRelationsAction : public QAction { Q_OBJECT public: QmitkDataNodeRemoveFromSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite); QmitkDataNodeRemoveFromSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite); virtual ~QmitkDataNodeRemoveFromSemanticRelationsAction() override; void SetDataStorage(mitk::DataStorage* dataStorage); private Q_SLOTS: void OnActionTriggered(bool); protected: void InitializeAction(); QList GetSelectedNodes(); mitk::DataNode::Pointer GetSelectedNode(); berry::IWorkbenchPartSite::WeakPtr m_WorkbenchPartSite; mitk::WeakPointer m_DataStorage; std::unique_ptr m_SemanticRelations; }; #endif // QMITKDATANODEREMOVEFROMSEMANTICRELATIONSACTION_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeUnlinkFromLesionAction.cpp similarity index 53% copy from Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.cpp copy to Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeUnlinkFromLesionAction.cpp index 3e86b3169e..0e4078d242 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeUnlinkFromLesionAction.cpp @@ -1,193 +1,168 @@ /*=================================================================== 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. ===================================================================*/ // semantic relations plugin -#include "QmitkDataNodeRemoveFromSemanticRelationsAction.h" +#include "QmitkDataNodeUnlinkFromLesionAction.h" // semantic relations module #include #include #include // mitk gui common plugin #include // berry #include #include // qt #include // namespace that contains the concrete action -namespace RemoveFromSemanticRelationsAction +namespace UnlinkFromLesionAction { - void Run(mitk::SemanticRelations* semanticRelations, const mitk::DataStorage* dataStorage, const mitk::DataNode* dataNode) + void Run(mitk::SemanticRelations* semanticRelations, const mitk::DataNode* dataNode) { if (nullptr == dataNode) { return; } - if (mitk::NodePredicates::GetImagePredicate()->CheckNode(dataNode)) + if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { - RemoveImage(semanticRelations, dataNode); + UnlinkSegmentation(semanticRelations, dataNode); } - else if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) + else { - RemoveSegmentation(semanticRelations, dataStorage, dataNode); - } - } - - void RemoveImage(mitk::SemanticRelations* semanticRelations, const mitk::DataNode* image) - { - if (nullptr == image) - { - return; - } - - try - { - // remove the image from the semantic relations storage - semanticRelations->RemoveImage(image); - } - catch (const mitk::SemanticRelationException& e) - { - std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox; - msgBox.setWindowTitle("Could not remove the selected image."); - msgBox.setText("The program wasn't able to correctly remove the selected image.\n" - "Reason:\n" + QString::fromStdString(exceptionMessage.str())); + msgBox.setWindowTitle("Could not unlink the selected data node."); + msgBox.setText("Please chose a valid segmentation to unlink from its represented lesion!"); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } } - void RemoveSegmentation(mitk::SemanticRelations* semanticRelations, const mitk::DataStorage* dataStorage, const mitk::DataNode* segmentation) + void UnlinkSegmentation(mitk::SemanticRelations* semanticRelations, const mitk::DataNode* segmentation) { if (nullptr == segmentation) { return; } try { - semanticRelations->RemoveSegmentation(segmentation); + semanticRelations->UnlinkSegmentationFromLesion(segmentation); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox; - msgBox.setWindowTitle("Could not remove the selected segmentation."); - msgBox.setText("The program wasn't able to correctly remove the selected segmentation.\n" + msgBox.setWindowTitle("Could not unlink the selected segmentation."); + msgBox.setText("The program wasn't able to correctly unlink the selected segmentation.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } } } -QmitkDataNodeRemoveFromSemanticRelationsAction::QmitkDataNodeRemoveFromSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) +QmitkDataNodeUnlinkFromLesionAction::QmitkDataNodeUnlinkFromLesionAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) : QAction(parent) , m_WorkbenchPartSite(workbenchPartSite) { - setText(tr("Remove from semantic relations")); + setText(tr("Unlink from lesion")); InitializeAction(); } -QmitkDataNodeRemoveFromSemanticRelationsAction::QmitkDataNodeRemoveFromSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) +QmitkDataNodeUnlinkFromLesionAction::QmitkDataNodeUnlinkFromLesionAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) : QAction(parent) , m_WorkbenchPartSite(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) { - setText(tr("Remove from semantic relations")); + setText(tr("Unlink from lesion")); InitializeAction(); } -QmitkDataNodeRemoveFromSemanticRelationsAction::~QmitkDataNodeRemoveFromSemanticRelationsAction() +QmitkDataNodeUnlinkFromLesionAction::~QmitkDataNodeUnlinkFromLesionAction() { // nothing here } -void QmitkDataNodeRemoveFromSemanticRelationsAction::SetDataStorage(mitk::DataStorage* dataStorage) +void QmitkDataNodeUnlinkFromLesionAction::SetDataStorage(mitk::DataStorage* dataStorage) { if (m_DataStorage != dataStorage) { // set the new data storage m_DataStorage = dataStorage; m_SemanticRelations = std::make_unique(m_DataStorage.Lock()); } } -void QmitkDataNodeRemoveFromSemanticRelationsAction::InitializeAction() +void QmitkDataNodeUnlinkFromLesionAction::InitializeAction() { - connect(this, &QAction::triggered, this, &QmitkDataNodeRemoveFromSemanticRelationsAction::OnActionTriggered); + connect(this, &QAction::triggered, this, &QmitkDataNodeUnlinkFromLesionAction::OnActionTriggered); } -void QmitkDataNodeRemoveFromSemanticRelationsAction::OnActionTriggered(bool checked) +void QmitkDataNodeUnlinkFromLesionAction::OnActionTriggered(bool checked) { if (nullptr == m_SemanticRelations) { return; } - if (m_DataStorage.IsExpired()) - { - return; - } - auto dataNode = GetSelectedNode(); - RemoveFromSemanticRelationsAction::Run(m_SemanticRelations.get(), m_DataStorage.Lock(), dataNode); + UnlinkFromLesionAction::Run(m_SemanticRelations.get(), dataNode); } -QList QmitkDataNodeRemoveFromSemanticRelationsAction::GetSelectedNodes() +QList QmitkDataNodeUnlinkFromLesionAction::GetSelectedNodes() { QList selectedNodes; if (m_WorkbenchPartSite.Expired()) { return selectedNodes; } berry::ISelection::ConstPointer selection = m_WorkbenchPartSite.Lock()->GetWorkbenchWindow()->GetSelectionService()->GetSelection(); mitk::DataNodeSelection::ConstPointer currentSelection = selection.Cast(); if (currentSelection.IsNull() || currentSelection->IsEmpty()) { return selectedNodes; } selectedNodes = QList::fromStdList(currentSelection->GetSelectedDataNodes()); return selectedNodes; } -mitk::DataNode::Pointer QmitkDataNodeRemoveFromSemanticRelationsAction::GetSelectedNode() +mitk::DataNode::Pointer QmitkDataNodeUnlinkFromLesionAction::GetSelectedNode() { QList selectedNodes = GetSelectedNodes(); if (selectedNodes.empty()) { return nullptr; } // no batch action; should only be called with a single node mitk::DataNode::Pointer dataNode = selectedNodes.front(); if (nullptr == dataNode) { return nullptr; } return dataNode; -} \ No newline at end of file +} diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeUnlinkFromLesionAction.h similarity index 60% copy from Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.h copy to Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeUnlinkFromLesionAction.h index cf48d0ef33..4702248a80 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeUnlinkFromLesionAction.h @@ -1,73 +1,72 @@ /*=================================================================== 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 QMITKDATANODEREMOVEFROMSEMANTICRELATIONSACTION_H -#define QMITKDATANODEREMOVEFROMSEMANTICRELATIONSACTION_H +#ifndef QMITKDATANODEUNLINKFROMLESIONACTION_H +#define QMITKDATANODEUNLINKFROMLESIONACTION_H #include // mitk core #include #include #include // semantic relations module #include // berry #include // qt #include -namespace RemoveFromSemanticRelationsAction +namespace UnlinkFromLesionAction { - MITK_GUI_SEMANTICRELATIONS_EXPORT void Run(mitk::SemanticRelations* semanticRelations, const mitk::DataStorage* dataStorage, const mitk::DataNode* image); - - void RemoveImage(mitk::SemanticRelations* semanticRelations, const mitk::DataNode* image); - void RemoveSegmentation(mitk::SemanticRelations* semanticRelations, const mitk::DataStorage* dataStorage, const mitk::DataNode* segmentation); + MITK_GUI_SEMANTICRELATIONS_EXPORT void Run(mitk::SemanticRelations* semanticRelations, const mitk::DataStorage* dataStorage, const mitk::DataNode* dataNode); + + void UnlinkSegmentation(mitk::SemanticRelations* semanticRelations, const mitk::DataNode* segmentation); } -class MITK_GUI_SEMANTICRELATIONS_EXPORT QmitkDataNodeRemoveFromSemanticRelationsAction : public QAction +class MITK_GUI_SEMANTICRELATIONS_EXPORT QmitkDataNodeUnlinkFromLesionAction : public QAction { Q_OBJECT public: - QmitkDataNodeRemoveFromSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite); - QmitkDataNodeRemoveFromSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite); + QmitkDataNodeUnlinkFromLesionAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite); + QmitkDataNodeUnlinkFromLesionAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite); - virtual ~QmitkDataNodeRemoveFromSemanticRelationsAction() override; + virtual ~QmitkDataNodeUnlinkFromLesionAction() override; void SetDataStorage(mitk::DataStorage* dataStorage); private Q_SLOTS: void OnActionTriggered(bool); protected: void InitializeAction(); QList GetSelectedNodes(); mitk::DataNode::Pointer GetSelectedNode(); berry::IWorkbenchPartSite::WeakPtr m_WorkbenchPartSite; mitk::WeakPointer m_DataStorage; std::unique_ptr m_SemanticRelations; }; -#endif // QMITKDATANODEREMOVEFROMSEMANTICRELATIONSACTION_H +#endif // QMITKDATANODEUNLINKFROMLESIONACTION_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp index a4e08f20f1..837aaaf405 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp @@ -1,478 +1,391 @@ /*=================================================================== 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. ===================================================================*/ // semantic relations plugin #include "QmitkLesionInfoWidget.h" #include "QmitkSemanticRelationsNodeSelectionDialog.h" // semantic relations UI module #include // semantic relations module #include #include #include -#include - -#include "QmitkCustomVariants.h" - -// mitk core -#include // qt #include #include #include #include #include -const QBrush QmitkLesionInfoWidget::DEFAULT_BACKGROUND_COLOR = QBrush(Qt::transparent); -const QBrush QmitkLesionInfoWidget::SELECTED_BACKGROUND_COLOR = QBrush(Qt::green); -const QBrush QmitkLesionInfoWidget::CONNECTED_BACKGROUND_COLOR = QBrush(Qt::darkGreen); - QmitkLesionInfoWidget::QmitkLesionInfoWidget(mitk::DataStorage* dataStorage, QWidget* parent /*= nullptr*/) : QWidget(parent) , m_DataStorage(dataStorage) , m_SemanticRelations(std::make_unique(dataStorage)) { - Init(); + Initialize(); } QmitkLesionInfoWidget::~QmitkLesionInfoWidget() { - if (nullptr != m_SemanticRelations) - { - m_SemanticRelations->RemoveObserver(this); - } + // nothing here } -void QmitkLesionInfoWidget::Init() +void QmitkLesionInfoWidget::Initialize() { - // create GUI from the Qt Designer's .ui file m_Controls.setupUi(this); - m_Controls.lesionListWidget->setContextMenuPolicy(Qt::CustomContextMenu); + m_Controls.lesionTreeView->setAlternatingRowColors(true); + m_Controls.lesionTreeView->setSelectionMode(QAbstractItemView::SingleSelection); + m_Controls.lesionTreeView->setSelectionBehavior(QAbstractItemView::SelectRows); + m_Controls.lesionTreeView->setContextMenuPolicy(Qt::CustomContextMenu); - SetUpConnections(); + m_StorageModel = new QmitkLesionTreeModel(this); + if (m_DataStorage.IsExpired()) + { + return; + } - m_SemanticRelations->AddObserver(this); + auto dataStorage = m_DataStorage.Lock(); + m_StorageModel->SetDataStorage(dataStorage); + m_Controls.lesionTreeView->setModel(m_StorageModel); + + SetUpConnections(); } void QmitkLesionInfoWidget::SetUpConnections() { + connect(m_StorageModel, &QmitkLesionTreeModel::ModelUpdated, this, &QmitkLesionInfoWidget::OnModelUpdated); + // connect buttons to modify semantic relations connect(m_Controls.addLesionPushButton, &QPushButton::clicked, this, &QmitkLesionInfoWidget::OnAddLesionButtonClicked); // connect each list widget with a custom slots - connect(m_Controls.lesionListWidget, &QListWidget::currentItemChanged, this, &QmitkLesionInfoWidget::OnCurrentLesionItemChanged); - connect(m_Controls.lesionListWidget, &QListWidget::itemDoubleClicked, this, &QmitkLesionInfoWidget::OnLesionItemDoubleClicked); + connect(m_Controls.lesionTreeView->selectionModel(), &QItemSelectionModel::currentChanged, this, &QmitkLesionInfoWidget::OnSelectionChanged); // connect context menu entries - connect(m_Controls.lesionListWidget, &QListWidget::customContextMenuRequested, this, &QmitkLesionInfoWidget::OnLesionListContextMenuRequested); + connect(m_Controls.lesionTreeView, &QTreeView::customContextMenuRequested, this, &QmitkLesionInfoWidget::OnLesionListContextMenuRequested); } -void QmitkLesionInfoWidget::SetCurrentCaseID(const mitk::SemanticTypes::CaseID& caseID) +void QmitkLesionInfoWidget::SetCaseID(const mitk::SemanticTypes::CaseID& caseID) { m_CaseID = caseID; - Update(m_CaseID); + m_StorageModel->SetCaseID(caseID); } -void QmitkLesionInfoWidget::Update(const mitk::SemanticTypes::CaseID& caseID) +////////////////////////////////////////////////////////////////////////// +// Implementation of the QT_SLOTS +////////////////////////////////////////////////////////////////////////// +void QmitkLesionInfoWidget::OnModelUpdated() { - if (nullptr == m_SemanticRelations) + m_Controls.lesionTreeView->expandAll(); + int columns = m_Controls.lesionTreeView->model()->columnCount(); + for (int i = 0; i < columns; ++i) { - return; - } - - // if the case ID of updated instance is equal to the currently active caseID - if (caseID == m_CaseID) - { - ResetLesionListWidget(); + m_Controls.lesionTreeView->resizeColumnToContents(i); } } -////////////////////////////////////////////////////////////////////////// -// Implementation of the QT_SLOTS -////////////////////////////////////////////////////////////////////////// void QmitkLesionInfoWidget::OnAddLesionButtonClicked() { if (m_CaseID.empty()) { QMessageBox msgBox; msgBox.setWindowTitle("No case ID set."); msgBox.setText("In order to add a lesion, please specify the current case / patient."); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } mitk::SemanticTypes::Lesion newLesion = mitk::GenerateNewLesion(); try { m_SemanticRelations->AddLesion(m_CaseID, newLesion); } catch (mitk::SemanticRelationException& e) { MITK_INFO << "Could not add a new lesion. " << e; } } -void QmitkLesionInfoWidget::OnCurrentLesionItemChanged(QListWidgetItem* currentItem, QListWidgetItem* /*previousItem*/) +void QmitkLesionInfoWidget::OnSelectionChanged(const QModelIndex& current, const QModelIndex& previous) { - if (nullptr == currentItem - || nullptr == m_SemanticRelations) + if (nullptr == m_SemanticRelations && current.isValid()) { return; } // only the UID is needed to identify a representing lesion - m_CurrentLesion.UID = currentItem->data(Qt::UserRole).toString().toStdString(); - if (false == m_SemanticRelations->InstanceExists(m_CaseID, m_CurrentLesion)) + QVariant data = m_StorageModel->data(current, Qt::UserRole); + if (data.canConvert()) { - // no UID found; cannot create a lesion - return; - } - - if (SELECTED_BACKGROUND_COLOR == currentItem->background() - || CONNECTED_BACKGROUND_COLOR == currentItem->background()) - { - DarkenBackgroundColors(); + m_CurrentLesion = data.value()->GetData().GetLesion(); } else { - ResetBackgroundColors(); + return; } - currentItem->setBackground(SELECTED_BACKGROUND_COLOR); - - emit LesionChanged(m_CurrentLesion); -} - -void QmitkLesionInfoWidget::OnLesionItemDoubleClicked(QListWidgetItem* clickedItem) -{ - if (nullptr == clickedItem - || nullptr == m_SemanticRelations) + if (false == m_SemanticRelations->InstanceExists(m_CaseID, m_CurrentLesion)) { + // no UID of a existing lesion found; cannot create a lesion return; } - // only the UID is needed to identify a representing lesion - m_CurrentLesion.UID = clickedItem->data(Qt::UserRole).toString().toStdString(); - m_CurrentLesion.name = clickedItem->text().toStdString(); + emit LesionChanged(m_CurrentLesion); } void QmitkLesionInfoWidget::OnLesionListContextMenuRequested(const QPoint& pos) { - QListWidgetItem* currentItem = m_Controls.lesionListWidget->itemAt(pos); - if (nullptr == currentItem) + QModelIndex index = m_Controls.lesionTreeView->indexAt(pos); + if (!index.isValid()) { // no item clicked; cannot retrieve the current lesion return; } - mitk::SemanticTypes::ID selectedLesionUID; - selectedLesionUID = currentItem->data(Qt::UserRole).toString().toStdString(); - if (selectedLesionUID.empty()) + QVariant data = m_StorageModel->data(index, Qt::UserRole); + mitk::SemanticTypes::Lesion selectedLesion; + if (data.canConvert()) + { + selectedLesion = data.value()->GetData().GetLesion(); + } + else { - // no UID found; cannot create a lesion return; } - QMenu* menu = new QMenu(m_Controls.lesionListWidget); + QMenu* menu = new QMenu(m_Controls.lesionTreeView); QAction* linkToSegmentation = new QAction("Link to segmentation", this); linkToSegmentation->setEnabled(true); - connect(linkToSegmentation, &QAction::triggered, [this, selectedLesionUID] { OnLinkToSegmentation(selectedLesionUID); }); + connect(linkToSegmentation, &QAction::triggered, [this, selectedLesion] { OnLinkToSegmentation(selectedLesion); }); menu->addAction(linkToSegmentation); QAction* setLesionName = new QAction("Set lesion name", this); setLesionName->setEnabled(true); - connect(setLesionName, &QAction::triggered, [this, selectedLesionUID] { OnSetLesionName(selectedLesionUID); }); + connect(setLesionName, &QAction::triggered, [this, selectedLesion] { OnSetLesionName(selectedLesion); }); menu->addAction(setLesionName); QAction* setLesionClass = new QAction("Set lesion class", this); setLesionClass->setEnabled(true); - connect(setLesionClass, &QAction::triggered, [this, selectedLesionUID] { OnSetLesionClass(selectedLesionUID); }); + connect(setLesionClass, &QAction::triggered, [this, selectedLesion] { OnSetLesionClass(selectedLesion); }); menu->addAction(setLesionClass); QAction* removeLesion = new QAction("Remove lesion", this); removeLesion->setEnabled(true); - connect(removeLesion, &QAction::triggered, [this, selectedLesionUID] { OnRemoveLesion(selectedLesionUID); }); + connect(removeLesion, &QAction::triggered, [this, selectedLesion] { OnRemoveLesion(selectedLesion); }); menu->addAction(removeLesion); menu->popup(QCursor::pos()); } -void QmitkLesionInfoWidget::OnLinkToSegmentation(const mitk::SemanticTypes::ID& selectedLesionUID) +void QmitkLesionInfoWidget::OnLinkToSegmentation(mitk::SemanticTypes::Lesion selectedLesion) { if (m_CaseID.empty()) { QMessageBox msgBox; msgBox.setWindowTitle("No case ID set."); msgBox.setText("In order to link a lesion to a segmentation, please specify the current case / patient."); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } - // retrieve the full lesion with its lesion class using the given lesion UID - mitk::SemanticTypes::Lesion selectedLesion = mitk::GetLesionByUID(selectedLesionUID, m_SemanticRelations->GetAllLesionsOfCase(m_CaseID)); - if (selectedLesion.UID.empty()) - { - // could not find lesion information for the selected lesion - return; - } - if (m_DataStorage.IsExpired()) { return; } auto dataStorage = m_DataStorage.Lock(); QmitkSemanticRelationsNodeSelectionDialog* dialog = new QmitkSemanticRelationsNodeSelectionDialog(this, "Select segmentation to link to the selected lesion.", ""); dialog->SetDataStorage(dataStorage); dialog->setWindowTitle("Select segmentation node"); dialog->SetNodePredicate(mitk::NodePredicates::GetSegmentationPredicate()); dialog->SetSelectOnlyVisibleNodes(true); dialog->SetSelectionMode(QAbstractItemView::SingleSelection); dialog->SetCaseID(m_CaseID); int dialogReturnValue = dialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } auto nodes = dialog->GetSelectedNodes(); mitk::DataNode::Pointer selectedDataNode = nullptr; if (!nodes.isEmpty()) { // only single selection allowed selectedDataNode = nodes.front(); } if (nullptr == selectedDataNode) { QMessageBox msgBox; msgBox.setWindowTitle("No valid segmentation node selected."); msgBox.setText("In order to link the selected lesion to a segmentation, please specify a valid segmentation node."); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } if (false == mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(selectedDataNode)) { QMessageBox msgBox; msgBox.setWindowTitle("No segmentation selected"); msgBox.setText("In order to link the selected lesion to a segmentation, please specify a valid segmentation node."); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } mitk::BaseData* baseData = selectedDataNode->GetData(); if (nullptr == baseData) { QMessageBox msgBox; msgBox.setWindowTitle("No valid base data."); msgBox.setText("In order to link the selected lesion to a segmentation, please specify a valid segmentation node."); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } try { m_SemanticRelations->LinkSegmentationToLesion(selectedDataNode, selectedLesion); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox; msgBox.setWindowTitle("Could not link the selected lesion."); msgBox.setText("The program wasn't able to correctly link the selected lesion with the selected segmentation.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); } } -void QmitkLesionInfoWidget::OnSetLesionName(const mitk::SemanticTypes::ID& selectedLesionUID) +void QmitkLesionInfoWidget::OnSetLesionName(mitk::SemanticTypes::Lesion selectedLesion) { - // retrieve the full lesion with its lesion class using the given lesion UID - mitk::SemanticTypes::Lesion selectedLesion = mitk::GetLesionByUID(selectedLesionUID, m_SemanticRelations->GetAllLesionsOfCase(m_CaseID)); - if (selectedLesion.UID.empty()) + if (m_CaseID.empty()) { - // could not find lesion information for the selected lesion + QMessageBox msgBox; + msgBox.setWindowTitle("No case ID set."); + msgBox.setText("In order to set a lesion name, please specify the current case / patient."); + msgBox.setIcon(QMessageBox::Warning); + msgBox.exec(); return; } // use the lesion information to set the input text for the dialog QmitkLesionTextDialog* inputDialog = new QmitkLesionTextDialog(this); inputDialog->setWindowTitle("Set lesion name"); inputDialog->SetLineEditText(selectedLesion.name); int dialogReturnValue = inputDialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } std::string newLesionName = inputDialog->GetLineEditText().toStdString(); selectedLesion.name = newLesionName; m_SemanticRelations->OverwriteLesion(m_CaseID, selectedLesion); } -void QmitkLesionInfoWidget::OnSetLesionClass(const mitk::SemanticTypes::ID& selectedLesionUID) +void QmitkLesionInfoWidget::OnSetLesionClass(mitk::SemanticTypes::Lesion selectedLesion) { - // retrieve the full lesion with its lesion class using the given lesion UID - mitk::SemanticTypes::Lesion selectedLesion = mitk::GetLesionByUID(selectedLesionUID, m_SemanticRelations->GetAllLesionsOfCase(m_CaseID)); - if (selectedLesion.UID.empty()) + if (m_CaseID.empty()) { - // could not find lesion information for the selected lesion + QMessageBox msgBox; + msgBox.setWindowTitle("No case ID set."); + msgBox.setText("In order to set a lesion class, please specify the current case / patient."); + msgBox.setIcon(QMessageBox::Warning); + msgBox.exec(); return; } // use the lesion information to set the input text for the dialog QmitkLesionTextDialog* inputDialog = new QmitkLesionTextDialog(this); inputDialog->setWindowTitle("Set lesion class"); inputDialog->SetLineEditText(selectedLesion.lesionClass.classType); // prepare the completer for the dialogs input text field mitk::LesionClassVector allLesionClasses = m_SemanticRelations->GetAllLesionClassesOfCase(m_CaseID); QStringList wordList; for (const auto& lesionClass : allLesionClasses) { wordList << QString::fromStdString(lesionClass.classType); } QCompleter* completer = new QCompleter(wordList, this); completer->setCaseSensitivity(Qt::CaseInsensitive); inputDialog->GetLineEdit()->setCompleter(completer); int dialogReturnValue = inputDialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } // retrieve the new input lesion class type and check for an already existing lesion class types std::string newLesionClassType = inputDialog->GetLineEditText().toStdString(); mitk::SemanticTypes::LesionClass existingLesionClass = mitk::FindExistingLesionClass(newLesionClassType, allLesionClasses); if (existingLesionClass.UID.empty()) { // could not find lesion class information for the new lesion class type // create a new lesion class for the selected lesion existingLesionClass = mitk::GenerateNewLesionClass(newLesionClassType); } selectedLesion.lesionClass = existingLesionClass; m_SemanticRelations->OverwriteLesion(m_CaseID, selectedLesion); } -void QmitkLesionInfoWidget::OnRemoveLesion(const mitk::SemanticTypes::ID& selectedLesionUID) +void QmitkLesionInfoWidget::OnRemoveLesion(mitk::SemanticTypes::Lesion selectedLesion) { if (m_CaseID.empty()) { QMessageBox msgBox; msgBox.setWindowTitle("No case ID set."); msgBox.setText("In order to remove a lesion, please specify the current case / patient."); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } - // retrieve the full lesion with its lesion class using the given lesion UID - mitk::SemanticTypes::Lesion selectedLesion = mitk::GetLesionByUID(selectedLesionUID, m_SemanticRelations->GetAllLesionsOfCase(m_CaseID)); - if (selectedLesion.UID.empty()) - { - // could not find lesion information for the selected lesion - return; - } - try { m_SemanticRelations->RemoveLesion(m_CaseID, selectedLesion); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox; msgBox.setWindowTitle("Could not remove the selected lesion."); msgBox.setText("The program wasn't able to correctly remove the selected lesion from the semantic relations model.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); } } - -void QmitkLesionInfoWidget::ResetLesionListWidget() -{ - m_Controls.lesionListWidget->clear(); - m_CurrentLesion.UID = ""; - m_CurrentLesion.name = ""; - - // create lesion list widget entries with the current lesions - mitk::SemanticRelations::LesionVector allLesionsOfCase = m_SemanticRelations->GetAllLesionsOfCase(m_CaseID); - if (allLesionsOfCase.empty()) - { - m_Controls.lesionListWidget->addItem("No lesions found"); - } - for (const auto& lesion : allLesionsOfCase) - { - // store the UID as 'UserRole' data in the widget item - QListWidgetItem* lesionItem = new QListWidgetItem; - lesionItem->setData(Qt::UserRole, QString::fromStdString(lesion.UID)); - - // use the lesion UID for the item text, if the lesion name is non-existent - if (lesion.name.empty()) - { - lesionItem->setText(QString::fromStdString(lesion.UID)); - } - else - { - lesionItem->setText(QString::fromStdString(lesion.name)); - } - - m_Controls.lesionListWidget->addItem(lesionItem); - } -} - -void QmitkLesionInfoWidget::ResetBackgroundColors() -{ - // reset all lesion list widget items to original background color - for (int i = 0; i < m_Controls.lesionListWidget->count(); ++i) - { - QListWidgetItem* item = m_Controls.lesionListWidget->item(i); - item->setBackground(DEFAULT_BACKGROUND_COLOR); - } -} - -void QmitkLesionInfoWidget::DarkenBackgroundColors() -{ - // reset all lesion list widget items to original background color - for (int i = 0; i < m_Controls.lesionListWidget->count(); ++i) - { - QListWidgetItem* item = m_Controls.lesionListWidget->item(i); - if (DEFAULT_BACKGROUND_COLOR != item->background()) - { - item->setBackground(CONNECTED_BACKGROUND_COLOR); - } - } -} diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.h index 7ede7961e7..5596a18cc2 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.h @@ -1,119 +1,99 @@ /*=================================================================== 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 QMITKLESIONINFOWIDGET_H #define QMITKLESIONINFOWIDGET_H -// semantic relations UI module +// semantic relations plugin #include +// semantic relations UI module +#include + // semantic relations module #include -#include // mitk #include // qt #include /* * @brief The QmitkLesionInfoWidget is a widget that shows and modifies the currently available lesion data of the semantic relations model. * * The widget provides a dialogs to add nodes from the data storage to the semantic relations model. * It provides functionality to create new lesions and link them with segmentation nodes. * * The QmitkLesionInfoWidget provides three QListWidgets, that show the lesion data and the referenced segmentation data, as * well as the connected image data, depending on the selected lesion. -* -* The QmitkLesionInfoWidget implements the 'ISemanticRelationsObserver', so that it is automatically updated, if the -* semantic relations model changes. Updating means freshly getting all lesion data and filling the lesion-ListWidget with the lesion data. */ -class QmitkLesionInfoWidget : public QWidget, public mitk::ISemanticRelationsObserver +class QmitkLesionInfoWidget : public QWidget { Q_OBJECT public: static const QBrush DEFAULT_BACKGROUND_COLOR; static const QBrush SELECTED_BACKGROUND_COLOR; static const QBrush CONNECTED_BACKGROUND_COLOR; QmitkLesionInfoWidget::QmitkLesionInfoWidget(mitk::DataStorage* dataStorage, QWidget* parent = nullptr); ~QmitkLesionInfoWidget(); - void SetCurrentCaseID(const mitk::SemanticTypes::CaseID& caseID); - /* - * @brief Updates the 'lesionListWidget' of the GUI with the current lesion-data from the semantic relations model. - * - * Overridden from 'ISemanticRelationsObserver'. - * In order for the Update-function to be called, this widget has to be added as a observer of SemanticRelation - * (e.g. m_SemanticRelations->AddObserver(m_LesionInfoWidget);) - * - * @par caseID The current case ID to identify the currently active patient / case. - */ - virtual void Update(const mitk::SemanticTypes::CaseID& caseID) override; + void SetCaseID(const mitk::SemanticTypes::CaseID& caseID); const mitk::SemanticTypes::Lesion& GetSelectedLesion() const { return m_CurrentLesion; } - /* - * @brief Resets all items from the lesion list widget. - */ - void ResetLesionListWidget(); - /* - * @brief Resets the background color of all items in each list widget. - */ - void ResetBackgroundColors(); - - void DarkenBackgroundColors(); - Q_SIGNALS: void LesionChanged(const mitk::SemanticTypes::Lesion&); private Q_SLOTS: + void OnModelUpdated(); /* * @brief Generates a new, empty lesion to add to the semantic relations model for the current case ID. */ void OnAddLesionButtonClicked(); - // slots for the mouse click events of the list widgets - void OnCurrentLesionItemChanged(QListWidgetItem*, QListWidgetItem*); - void OnLesionItemDoubleClicked(QListWidgetItem*); + // slots for the mouse click events of tree view's selection model + void OnSelectionChanged(const QModelIndex& current, const QModelIndex& previous); void OnLesionListContextMenuRequested(const QPoint&); // slots for the context menu actions of the lesion list widget - void OnLinkToSegmentation(const mitk::SemanticTypes::ID&); - void OnSetLesionName(const mitk::SemanticTypes::ID&); - void OnSetLesionClass(const mitk::SemanticTypes::ID&); - void OnRemoveLesion(const mitk::SemanticTypes::ID&); + void OnLinkToSegmentation(mitk::SemanticTypes::Lesion); + void OnSetLesionName(mitk::SemanticTypes::Lesion); + void OnSetLesionClass(mitk::SemanticTypes::Lesion); + void OnRemoveLesion(mitk::SemanticTypes::Lesion); private: - void Init(); + void Initialize(); void SetUpConnections(); Ui::QmitkLesionInfoWidgetControls m_Controls; + QmitkLesionTreeModel* m_StorageModel; + mitk::WeakPointer m_DataStorage; std::unique_ptr m_SemanticRelations; mitk::SemanticTypes::CaseID m_CaseID; mitk::SemanticTypes::Lesion m_CurrentLesion; }; #endif // QMITKLESIONINFOWIDGET_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidgetControls.ui b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidgetControls.ui index db08af5e22..35ace22701 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidgetControls.ui +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidgetControls.ui @@ -1,50 +1,50 @@ QmitkLesionInfoWidgetControls true 0 0 350 300 0 0 0 0 + + + + Available lesions: + + + - + Add new lesion - - - - Available lesions: - - - diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsContextMenu.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsContextMenu.cpp index f63bb4fc42..0994613b77 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsContextMenu.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsContextMenu.cpp @@ -1,74 +1,78 @@ /*=================================================================== 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. ===================================================================*/ // semantic relations plugin #include "QmitkSemanticRelationsContextMenu.h" QmitkSemanticRelationsContextMenu::QmitkSemanticRelationsContextMenu(berry::IWorkbenchPartSite::Pointer workbenchPartSite, QWidget* parent) : QMenu(parent) { m_WorkbenchPartSite = workbenchPartSite; m_Parent = parent; InitDefaultActions(); } QmitkSemanticRelationsContextMenu::~QmitkSemanticRelationsContextMenu() { // nothing here } void QmitkSemanticRelationsContextMenu::SetDataStorage(mitk::DataStorage* dataStorage) { if (m_DataStorage != dataStorage) { // set the new data storage - also for all actions m_DataStorage = dataStorage; m_ControlPointAction->SetDataStorage(m_DataStorage.Lock()); m_InformationTypeAction->SetDataStorage(m_DataStorage.Lock()); m_RemoveFromSemanticRelationsAction->SetDataStorage(m_DataStorage.Lock()); + m_UnlinkFromLesionAction->SetDataStorage(m_DataStorage.Lock()); } } void QmitkSemanticRelationsContextMenu::SetControlledRenderer(RenderWindowLayerUtilities::RendererVector controlledRenderer) { if (m_ControlledRenderer != controlledRenderer) { // set the new set of controlled renderer m_ControlledRenderer = controlledRenderer; m_OpenInAction->SetControlledRenderer(m_ControlledRenderer); } } void QmitkSemanticRelationsContextMenu::OnContextMenuRequested(const QPoint& /*pos*/) { popup(QCursor::pos()); } void QmitkSemanticRelationsContextMenu::InitDefaultActions() { m_ControlPointAction = new QmitkDataNodeSetControlPointAction(m_Parent, m_WorkbenchPartSite.Lock()); addAction(m_ControlPointAction); m_InformationTypeAction = new QmitkDataNodeInformationTypeAction(m_Parent, m_WorkbenchPartSite.Lock()); addAction(m_InformationTypeAction); m_RemoveFromSemanticRelationsAction = new QmitkDataNodeRemoveFromSemanticRelationsAction(m_Parent, m_WorkbenchPartSite.Lock()); addAction(m_RemoveFromSemanticRelationsAction); + m_UnlinkFromLesionAction = new QmitkDataNodeUnlinkFromLesionAction(m_Parent, m_WorkbenchPartSite.Lock()); + addAction(m_UnlinkFromLesionAction); + m_OpenInAction = new QmitkDataNodeOpenInAction(m_Parent, m_WorkbenchPartSite.Lock()); addAction(m_OpenInAction); } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsContextMenu.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsContextMenu.h index 3daa83805f..480dae7e05 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsContextMenu.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsContextMenu.h @@ -1,71 +1,73 @@ /*=================================================================== 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 QMITKSEMANTICRELATIONSCONTEXTMENU_H #define QMITKSEMANTICRELATIONSCONTEXTMENU_H // semantic relations plugin #include "QmitkDataNodeSetControlPointAction.h" #include "QmitkDataNodeInformationTypeAction.h" #include "QmitkDataNodeRemoveFromSemanticRelationsAction.h" +#include "QmitkDataNodeUnlinkFromLesionAction.h" #include "QmitkDataNodeOpenInAction.h" // mitk core #include #include // mitk render window manager module #include // blueberry ui qt plugin #include //qt #include class QmitkSemanticRelationsContextMenu : public QMenu { Q_OBJECT public: QmitkSemanticRelationsContextMenu(berry::IWorkbenchPartSite::Pointer workbenchPartSite, QWidget* parent = nullptr); virtual ~QmitkSemanticRelationsContextMenu() override; void SetDataStorage(mitk::DataStorage* dataStorage); void SetControlledRenderer(RenderWindowLayerUtilities::RendererVector controlledRenderer); public Q_SLOTS: void OnContextMenuRequested(const QPoint&); private: void InitDefaultActions(); QWidget* m_Parent; berry::IWorkbenchPartSite::WeakPtr m_WorkbenchPartSite; mitk::WeakPointer m_DataStorage; RenderWindowLayerUtilities::RendererVector m_ControlledRenderer; QmitkDataNodeSetControlPointAction* m_ControlPointAction; QmitkDataNodeInformationTypeAction* m_InformationTypeAction; QmitkDataNodeRemoveFromSemanticRelationsAction* m_RemoveFromSemanticRelationsAction; + QmitkDataNodeUnlinkFromLesionAction* m_UnlinkFromLesionAction; QmitkDataNodeOpenInAction* m_OpenInAction; }; #endif // QMITKSEMANTICRELATIONSCONTEXTMENU_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.cpp index f1d314d19f..506d823dcb 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.cpp @@ -1,264 +1,265 @@ /*=================================================================== 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. ===================================================================*/ // semantic relations plugin #include "QmitkSemanticRelationsView.h" #include "QmitkSemanticRelationsNodeSelectionDialog.h" #include "QmitkDataNodeAddToSemanticRelationsAction.h" #include "QmitkDataNodeRemoveFromSemanticRelationsAction.h" // semantic relations module #include #include // mitk qt widgets module #include #include // mitk multi label module #include // berry #include #include // qt #include +#include const std::string QmitkSemanticRelationsView::VIEW_ID = "org.mitk.views.semanticrelations"; void QmitkSemanticRelationsView::SetFocus() { // nothing here } void QmitkSemanticRelationsView::CreateQtPartControl(QWidget* parent) { // create GUI widgets m_Controls.setupUi(parent); // initialize the semantic relations m_SemanticRelations = std::make_unique(GetDataStorage()); m_LesionInfoWidget = new QmitkLesionInfoWidget(GetDataStorage(), parent); m_Controls.gridLayout->addWidget(m_LesionInfoWidget); m_PatientTableInspector = new QmitkPatientTableInspector(parent); m_PatientTableInspector->SetDataStorage(GetDataStorage()); m_Controls.gridLayout->addWidget(m_PatientTableInspector); QGridLayout* dndDataNodeWidgetLayout = new QGridLayout; dndDataNodeWidgetLayout->addWidget(m_PatientTableInspector, 0, 0); dndDataNodeWidgetLayout->setContentsMargins(0, 0, 0, 0); m_DnDDataNodeWidget = new QmitkDnDDataNodeWidget(parent); m_DnDDataNodeWidget->setLayout(dndDataNodeWidgetLayout); m_Controls.gridLayout->addWidget(m_DnDDataNodeWidget); m_ContextMenu = new QmitkSemanticRelationsContextMenu(GetSite(), m_PatientTableInspector); m_ContextMenu->SetDataStorage(GetDataStorage()); mitk::IRenderWindowPart* renderWindowPart = GetRenderWindowPart(); if (nullptr != renderWindowPart) { RenderWindowPartActivated(renderWindowPart); } SetUpConnections(); } void QmitkSemanticRelationsView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { // connect QmitkRenderWindows - underlying vtkRenderWindow is the same as "mitk::RenderingManager::GetInstance()->GetAllRegisteredRenderWindows()" QHash windowMap = renderWindowPart->GetQmitkRenderWindows(); QHash::Iterator it; mitk::BaseRenderer* baseRenderer = nullptr; RenderWindowLayerUtilities::RendererVector controlledRenderer; for (it = windowMap.begin(); it != windowMap.end(); ++it) { baseRenderer = mitk::BaseRenderer::GetInstance(it.value()->GetVtkRenderWindow()); if (nullptr != baseRenderer) { controlledRenderer.push_back(baseRenderer); } } m_ContextMenu->SetControlledRenderer(controlledRenderer); } void QmitkSemanticRelationsView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart) { // nothing here } void QmitkSemanticRelationsView::SetUpConnections() { connect(m_Controls.caseIDComboBox, static_cast(&QComboBox::currentIndexChanged), this, &QmitkSemanticRelationsView::OnCaseIDSelectionChanged); connect(m_LesionInfoWidget, &QmitkLesionInfoWidget::LesionChanged, this, &QmitkSemanticRelationsView::OnLesionChanged); connect(m_PatientTableInspector, &QmitkPatientTableInspector::DataNodeDoubleClicked, this, &QmitkSemanticRelationsView::OnDataNodeDoubleClicked); connect(m_DnDDataNodeWidget, &QmitkDnDDataNodeWidget::NodesDropped, this, &QmitkSemanticRelationsView::OnNodesAdded); connect(m_PatientTableInspector, &QmitkPatientTableInspector::OnContextMenuRequested, m_ContextMenu, &QmitkSemanticRelationsContextMenu::OnContextMenuRequested); connect(m_PatientTableInspector, &QmitkPatientTableInspector::OnNodeRemoved, this, &QmitkSemanticRelationsView::NodeRemoved); } QItemSelectionModel* QmitkSemanticRelationsView::GetDataNodeSelectionModel() const { return m_PatientTableInspector->GetSelectionModel(); } void QmitkSemanticRelationsView::NodeRemoved(const mitk::DataNode* dataNode) { if (nullptr == dataNode) { return; } if (m_SemanticRelations->InstanceExists(dataNode)) { - RemoveFromSemanticRelationsAction::Run(m_SemanticRelations.get(), GetDataStorage(), dataNode); + RemoveFromSemanticRelationsAction::Run(m_SemanticRelations.get(), dataNode); mitk::SemanticTypes::CaseID caseID = mitk::GetCaseIDFromDataNode(dataNode); RemoveFromComboBox(caseID); } } void QmitkSemanticRelationsView::OnLesionChanged(const mitk::SemanticTypes::Lesion& lesion) { m_PatientTableInspector->SetLesion(lesion); } void QmitkSemanticRelationsView::OnDataNodeDoubleClicked(const mitk::DataNode* dataNode) { if (nullptr == dataNode) { return; } if (mitk::NodePredicates::GetImagePredicate()->CheckNode(dataNode)) { OpenInEditor(dataNode); } else if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { JumpToPosition(dataNode); } } void QmitkSemanticRelationsView::OnCaseIDSelectionChanged(const QString& caseID) { - m_LesionInfoWidget->SetCurrentCaseID(caseID.toStdString()); + m_LesionInfoWidget->SetCaseID(caseID.toStdString()); m_PatientTableInspector->SetCaseID(caseID.toStdString()); } void QmitkSemanticRelationsView::OnNodesAdded(QmitkDnDDataNodeWidget* dnDDataNodeWidget, std::vector nodes) { mitk::SemanticTypes::CaseID caseID = ""; for (mitk::DataNode* dataNode : nodes) { AdddToSemanticRelationsAction::Run(m_SemanticRelations.get(), GetDataStorage(), dataNode); caseID = mitk::GetCaseIDFromDataNode(dataNode); AddToComboBox(caseID); } } void QmitkSemanticRelationsView::OnNodeRemoved(const mitk::DataNode* dataNode) { NodeRemoved(dataNode); } void QmitkSemanticRelationsView::AddToComboBox(const mitk::SemanticTypes::CaseID& caseID) { int foundIndex = m_Controls.caseIDComboBox->findText(QString::fromStdString(caseID)); if (-1 == foundIndex) { // add the caseID to the combo box, as it is not already contained m_Controls.caseIDComboBox->addItem(QString::fromStdString(caseID)); } } void QmitkSemanticRelationsView::RemoveFromComboBox(const mitk::SemanticTypes::CaseID& caseID) { std::vector allControlPoints = m_SemanticRelations->GetAllControlPointsOfCase(caseID); int foundIndex = m_Controls.caseIDComboBox->findText(QString::fromStdString(caseID)); if (allControlPoints.empty() && -1 != foundIndex) { // TODO: find new way to check for empty case id // caseID does not contain any control points and therefore no data // remove the caseID, if it is still contained m_Controls.caseIDComboBox->removeItem(foundIndex); } } void QmitkSemanticRelationsView::OpenInEditor(const mitk::DataNode* dataNode) { auto renderWindowPart = GetRenderWindowPart(); if (nullptr == renderWindowPart) { renderWindowPart = GetRenderWindowPart(QmitkAbstractView::BRING_TO_FRONT | QmitkAbstractView::OPEN); if (nullptr == renderWindowPart) { // no render window available return; } } auto image = dynamic_cast(dataNode->GetData()); if (nullptr != image) { mitk::RenderingManager::GetInstance()->InitializeViews(image->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); } } void QmitkSemanticRelationsView::JumpToPosition(const mitk::DataNode* dataNode) { if (nullptr == dataNode) { return; } mitk::LabelSetImage* labelSetImage = dynamic_cast(dataNode->GetData()); if (nullptr == labelSetImage) { return; } int activeLayer = labelSetImage->GetActiveLayer(); mitk::Label* activeLabel = labelSetImage->GetActiveLabel(activeLayer); labelSetImage->UpdateCenterOfMass(activeLabel->GetValue(), activeLayer); const mitk::Point3D& centerPosition = activeLabel->GetCenterOfMassCoordinates(); if (centerPosition.GetVnlVector().max_value() > 0.0) { auto renderWindowPart = GetRenderWindowPart(); if (nullptr == renderWindowPart) { renderWindowPart = GetRenderWindowPart(QmitkAbstractView::BRING_TO_FRONT | QmitkAbstractView::OPEN); if (nullptr == renderWindowPart) { // no render window available return; } } auto segmentation = dynamic_cast(dataNode->GetData()); if (nullptr != segmentation) { renderWindowPart->SetSelectedPosition(centerPosition); mitk::RenderingManager::GetInstance()->InitializeViews(segmentation->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); } } } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.h index 15bc8dd129..4bfd5f4ab3 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.h @@ -1,107 +1,109 @@ /*=================================================================== 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 QMITKSEMANTICRELATIONSVIEW_H #define QMITKSEMANTICRELATIONSVIEW_H // semantic relations plugin #include "ui_QmitkSemanticRelationsControls.h" #include "QmitkLesionInfoWidget.h" #include "QmitkSemanticRelationsContextMenu.h" // semantic relations module #include #include // semantic relations UI module +#include #include // mitk gui common plugin #include // berry #include // mitk qt #include class QmitkDnDDataNodeWidget; class QMenu; /* * @brief The QmitkSemanticRelationsView is an MITK view to combine and show the widgets of the 'SemanticRelationsUI'-module and this semantic relations plugin. * * It allows the MITK user to see and modify the content of the SemanticRelations-session. * A combo box is used to select and show the current patient. */ class QmitkSemanticRelationsView : public QmitkAbstractView, public mitk::IRenderWindowPartListener { Q_OBJECT public: static const std::string VIEW_ID; protected: virtual void SetFocus() override; virtual void CreateQtPartControl(QWidget* parent) override; virtual void RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) override; virtual void RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart) override; private Q_SLOTS: void OnLesionChanged(const mitk::SemanticTypes::Lesion&); void OnDataNodeDoubleClicked(const mitk::DataNode*); void OnCaseIDSelectionChanged(const QString&); void OnNodesAdded(QmitkDnDDataNodeWidget*, std::vector); void OnNodeRemoved(const mitk::DataNode*); private: void SetUpConnections(); /** * @brief Provide a QItemSelectionModel, which supports the data role 'QmitkDataNodeRole' (\see QmitkRenderWindowDataModel). * * The provided QItemSelectionModel is used in the QmitkAbstractView-base class as the selection model of * the selection provider (\see QmitkAbstractView::SetSelectionProvider()). * The default selection provider is a QmitkDataNodeSelectionProvider. Each time a selection in the provided * QItemSeletionModel is changed, a selection changed event is fired. All plugins (views), that subclass the * QmitkAbstractView will be informed about the selection changed via the OnSelectionChanged-function. */ virtual QItemSelectionModel* GetDataNodeSelectionModel() const override; virtual void NodeRemoved(const mitk::DataNode* dataNode) override; void AddToComboBox(const mitk::SemanticTypes::CaseID& caseID); void RemoveFromComboBox(const mitk::SemanticTypes::CaseID& caseID); void OpenInEditor(const mitk::DataNode* dataNode); void JumpToPosition(const mitk::DataNode* dataNode); Ui::QmitkSemanticRelationsControls m_Controls; QmitkLesionInfoWidget* m_LesionInfoWidget; QmitkPatientTableInspector* m_PatientTableInspector; QmitkDnDDataNodeWidget* m_DnDDataNodeWidget; + QmitkLesionTreeModel* m_LesionTreeModel; QmitkSemanticRelationsContextMenu* m_ContextMenu; std::unique_ptr m_SemanticRelations; }; #endif // QMITKSEMANTICRELATIONSVIEW_H