diff --git a/Modules/SemanticRelations/include/mitkSemanticRelations.h b/Modules/SemanticRelations/include/mitkSemanticRelations.h index 70166d8605..3155aa8ca9 100644 --- a/Modules/SemanticRelations/include/mitkSemanticRelations.h +++ b/Modules/SemanticRelations/include/mitkSemanticRelations.h @@ -1,616 +1,626 @@ /*=================================================================== 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(); typedef std::vector LesionVector; typedef std::vector LesionClassVector; typedef std::vector ControlpointVector; typedef std::vector InformationTypeVector; typedef std::vector DataNodeVector; /************************************************************************/ /* 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. */ 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. */ 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 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. */ 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. */ 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. */ 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. */ 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. */ 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). * * @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 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. */ 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. */ 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 Use the given date to set the control point for the given data node. + * The function tries to find a fitting control point for the given data and link to this control point or extend it. + * If no fitting (or any) control point is found a new control point is generated from the date and added to the semantic relations storage. + * + * @pre The given data node has to be valid (!nullptr). + * @throw SemanticRelationException, if the given data node is invalid (==nullptr). + + */ + void SetControlPointFromDate(const DataNode* dataNode, const SemanticTypes::Date& date); /* * @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 and overwrite the start or end point of the 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'). * @pre The given control point must differ from the overwritten control point, but only either in the start point or in the end point. * It is not possible to overwrite a single control point from a single date and simultaneously changing both ends of the time period. * @throw SemanticRelationException, if the given control point does not differ from the overwritten control point or if the given control point differs in the start date and the end date from the overwritten control point. * @pre The given control point must not overlap with an already existing control point. * @throw SemanticRelationException, if the given control point overlaps with an already existing control point interval (this can be checked via 'CheckOverlappingControlPoint'). * * @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 that overwrites an existing control point. * @param checkConsistence If true, the function checks, whether the date of the data node actually lies inside the control point to link. */ void OverwriteControlPointAndLinkData(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 add 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 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 Check if the given control point overlaps with an already existing control point. * If the UID already exists, then the given control point may overlap with the previous or next control point, * because the start point or the end point of the given control point might be modified (different to the same control point * that is stored at the moment). * If the UID does not already exists, the previous and next control point are found by comparing the dates of the already * existing control points and the given control point. * * @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. */ bool CheckOverlappingControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); /* * @brief Check if the given control point is contained within an already existing control point. * If the UID already exists, then the given control point is contained in this same control point. * However, the function does not check if the given control point is really contained (just compares UID) in this case. * If the UID does not already exist, the already existing control points are tested to see if they contain the * given control point. * * @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. */ bool CheckContainingControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint); }; } // namespace mitk #endif // MITKSEMANTICRELATIONS_H diff --git a/Modules/SemanticRelations/src/mitkSemanticRelations.cpp b/Modules/SemanticRelations/src/mitkSemanticRelations.cpp index 4ac8f05901..7149700e5a 100644 --- a/Modules/SemanticRelations/src/mitkSemanticRelations.cpp +++ b/Modules/SemanticRelations/src/mitkSemanticRelations.cpp @@ -1,1028 +1,1117 @@ /*=================================================================== 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 "mitkSemanticRelations.h" #include "mitkSemanticRelationException.h" #include "mitkNodePredicates.h" #include "mitkUIDGeneratorBoost.h" // multi label module #include // c++ #include std::vector mitk::SemanticRelations::m_ObserverVector; -mitk::SemanticRelations::SemanticRelations(mitk::DataStorage::Pointer dataStorage) +mitk::SemanticRelations::SemanticRelations(DataStorage::Pointer dataStorage) : m_DataStorage(dataStorage) { - m_RelationStorage = std::make_shared(); + 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); + 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; } } 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 { if (nullptr == dataNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); SemanticTypes::ID dataNodeID = GetIDFromDataNode(dataNode); return m_RelationStorage->GetControlPointOfData(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::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 mitk::DataNode* imageNode) +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); // find the correct control point for this image std::vector allControlPoints = GetAllControlPointsOfCase(caseID); if (allControlPoints.empty()) { // no stored control point // create a new control point for the image data SemanticTypes::ControlPoint newControlPoint = GenerateControlPoint(imageNode); AddControlPointAndLinkData(imageNode, newControlPoint); NotifyObserver(caseID); return; } // some control points already exist - find a control point where the date of the data node fits in SemanticTypes::ControlPoint fittingControlPoint = FindFittingControlPoint(imageNode, allControlPoints); if (fittingControlPoint.UID.empty()) { // did not find a fitting control point, although some control points already exist // need to see if a close control point can be extended SemanticTypes::ControlPoint extendedControlPoint = ExtendClosestControlPoint(imageNode, allControlPoints); if (extendedControlPoint.UID.empty()) { // closest control point can not be extended // create a new control point for the image data SemanticTypes::ControlPoint newControlPoint = GenerateControlPoint(imageNode); AddControlPointAndLinkData(imageNode, newControlPoint); } else { // found a control point that was extended OverwriteControlPointAndLinkData(imageNode, extendedControlPoint); } } else { // found a fitting control point LinkDataToControlPoint(imageNode, fittingControlPoint); } NotifyObserver(caseID); } -void mitk::SemanticRelations::RemoveImage(const mitk::DataNode* imageNode) +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 mitk::DataNode* segmentationNode, const mitk::DataNode* parentNode) +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 mitk::DataNode* segmentationNode) +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::SetControlPointFromDate(const DataNode* dataNode, const SemanticTypes::Date& date) +{ + if (nullptr == dataNode) + { + mitkThrowException(SemanticRelationException) << "Not a valid data node."; + } + + SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); + + // store the current control point to relink it, if anything goes wrong + mitk::SemanticTypes::ControlPoint originalControlPoint = GetControlPointOfData(dataNode); + // unlink the data, that is about to receive a new date + // this is needed in order to not extend a single control point, to which the selected node is currently linked + UnlinkDataFromControlPoint(dataNode); + + ControlpointVector allControlPoints = GetAllControlPointsOfCase(caseID); + if (!allControlPoints.empty()) + { + // need to check if an already existing control point fits/contains the user control point + SemanticTypes::ControlPoint fittingControlPoint = FindFittingControlPoint(date, allControlPoints); + if (!fittingControlPoint.UID.empty()) + { + try + { + // found a fitting control point + LinkDataToControlPoint(dataNode, fittingControlPoint, false); + } + catch (const SemanticRelationException&) + { + try + { + // link to the original control point + LinkDataToControlPoint(dataNode, originalControlPoint, false); + } + catch (const SemanticRelationException&) + { + mitkThrowException(SemanticRelationException) << "The data can not be linked. Inconsistency in the semantic relations storage assumed."; + } + } + return; + } + + // did not find a fitting control point, although some control points already exist + // need to check if a close control point can be found and extended + SemanticTypes::ControlPoint extendedControlPoint = ExtendClosestControlPoint(date, allControlPoints); + if (!extendedControlPoint.UID.empty()) + { + try + { + // found and extended a close control point + OverwriteControlPointAndLinkData(dataNode, extendedControlPoint, false); + } + catch (const SemanticRelationException&) + { + try + { + // link to the original control point + LinkDataToControlPoint(dataNode, originalControlPoint, false); + } + catch (const SemanticRelationException&) + { + mitkThrowException(SemanticRelationException) << "The data can not be linked Inconsistency in the semantic relations storage assumed."; + } + } + return; + } + } + + // generate a control point from the user-given date + SemanticTypes::ControlPoint controlPointFromUserDate = GenerateControlPoint(date); + try + { + AddControlPointAndLinkData(dataNode, controlPointFromUserDate, false); + } + catch (const SemanticRelationException&) + { + try + { + // link to the original control point + LinkDataToControlPoint(dataNode, originalControlPoint, false); + } + catch (const SemanticRelationException&) + { + mitkThrowException(SemanticRelationException) << "The data can not be linked. Inconsistency in the semantic relations storage assumed."; + } + } + + 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."; } // control point does not already exist bool contained = CheckContainingControlPoint(caseID, controlPoint); if (contained) { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to add is already contained in an existing control point."; } // control point is not already contained in an existing control point if (checkConsistence) { // to check the consistency, the control point manager checks, if the date extracted from the data node is inside the given control point bool insideControlPoint = InsideControlPoint(dataNode, controlPoint); if (!insideControlPoint) { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to add does not contain the date of the given data node."; } } m_RelationStorage->AddControlPoint(caseID, controlPoint); LinkDataToControlPoint(dataNode, controlPoint, checkConsistence); } void mitk::SemanticRelations::OverwriteControlPointAndLinkData(const DataNode* dataNode, const SemanticTypes::ControlPoint& controlPoint, bool checkConsistence) { if (nullptr == dataNode) { mitkThrowException(SemanticRelationException) << "Not a valid data node."; } SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(dataNode); ControlpointVector allControlPoints = GetAllControlPointsOfCase(caseID); const auto existingControlPoint = std::find_if(allControlPoints.begin(), allControlPoints.end(), [&controlPoint](const SemanticTypes::ControlPoint& currentControlPoint) { return currentControlPoint.UID == controlPoint.UID; }); if (existingControlPoint != allControlPoints.end()) { // control point does already exist if (checkConsistence) { // to check the consistency, the control point manager checks, if the date extracted from the data node is inside the given control point bool insideControlPoint = InsideControlPoint(dataNode, controlPoint); if (!insideControlPoint) { mitkThrowException(SemanticRelationException) << "The overwriting control point " << controlPoint.UID << " does not contain the date of the given data node."; } } bool sameStartPoint = controlPoint.startPoint == (*existingControlPoint).startPoint; bool sameEndPoint = controlPoint.endPoint == (*existingControlPoint).endPoint; if (!sameStartPoint && !sameEndPoint) { mitkThrowException(SemanticRelationException) << "The overwriting control point " << controlPoint.UID << " differs in the start date and in the end date from the original control point."; } if (sameStartPoint && sameEndPoint) { mitkThrowException(SemanticRelationException) << "The overwriting control point " << controlPoint.UID << " does not differ from the original control point."; } bool overlapping = CheckOverlappingControlPoint(caseID, controlPoint); if (overlapping) { mitkThrowException(SemanticRelationException) << "The overwriting control point " << controlPoint.UID << " overlaps with an already existing control point"; } else { m_RelationStorage->OverwriteControlPoint(caseID, controlPoint); LinkDataToControlPoint(dataNode, controlPoint, checkConsistence); } } else { mitkThrowException(SemanticRelationException) << "The control point " << controlPoint.UID << " to overwrite does not exist for the given case. \n Use 'AddControlPointAndLinkData' instead."; } } 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); // control point does already exist if (checkConsistence) { // to check the consistency, the control point manager checks, if the date extracted from the data node is inside the given control point bool insideControlPoint = InsideControlPoint(dataNode, controlPoint); if (!insideControlPoint) { mitkThrowException(SemanticRelationException) << "The data to link does not lie inside the given control point " << controlPoint.UID << " ."; } } 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); - mitk::SemanticTypes::ControlPoint controlPoint = m_RelationStorage->GetControlPointOfData(caseID, dataID); + SemanticTypes::ControlPoint controlPoint = m_RelationStorage->GetControlPointOfData(caseID, dataID); m_RelationStorage->UnlinkDataFromControlPoint(caseID, dataID); DataNodeVector allImagesOfControlPoint = GetAllImagesOfControlPoint(caseID, controlPoint); if (allImagesOfControlPoint.empty()) { // no more data is linked to the specific control point // the control point can be removed from the storage m_RelationStorage->RemoveControlPointFromCase(caseID, controlPoint); } else { // some data is still linked to this control point // the control point can not be removed, but has to be adjusted to fit the remaining data SemanticTypes::ControlPoint adjustedControlPoint = GenerateControlPoint(allImagesOfControlPoint); // set the UIDs to be the same, so that all references still work adjustedControlPoint.UID = controlPoint.UID; adjustedControlPoint.startPoint.UID = controlPoint.startPoint.UID; adjustedControlPoint.endPoint.UID = controlPoint.endPoint.UID; m_RelationStorage->OverwriteControlPoint(caseID, adjustedControlPoint); } } 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 mitk::SemanticTypes::CaseID& caseID) const +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(); } bool mitk::SemanticRelations::CheckOverlappingControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { ControlpointVector allControlPoints = GetAllControlPointsOfCase(caseID); if (allControlPoints.empty()) { return false; } std::sort(allControlPoints.begin(), allControlPoints.end()); const auto existingControlPoint = std::find_if(allControlPoints.begin(), allControlPoints.end(), [&controlPoint](const SemanticTypes::ControlPoint& currentControlPoint) { return currentControlPoint.UID == controlPoint.UID; }); auto nextControlPoint = allControlPoints.end(); auto previousControlPoint = allControlPoints.begin(); if (existingControlPoint != allControlPoints.end()) { // case overwriting: control point already contained in the list of all existing control points nextControlPoint = std::next(existingControlPoint, 1); if (existingControlPoint != allControlPoints.begin()) { previousControlPoint = std::prev(existingControlPoint, 1); } } else { // case adding: control point not contained in the list of all existing control points nextControlPoint = std::find_if(allControlPoints.begin(), allControlPoints.end(), [&controlPoint](const SemanticTypes::ControlPoint& currentControlPoint) { return currentControlPoint.startPoint >= controlPoint.endPoint; }); if (nextControlPoint != allControlPoints.begin()) { previousControlPoint = std::prev(existingControlPoint, 1); } } // check the neighboring control points for overlap bool overlapWithNext = false; bool overlapWithPrevious = CheckForOverlap(controlPoint, *previousControlPoint); if (nextControlPoint != allControlPoints.end()) { overlapWithNext = CheckForOverlap(controlPoint, *nextControlPoint); } return overlapWithNext || overlapWithPrevious; // return true if at least one overlap is detected } bool mitk::SemanticRelations::CheckContainingControlPoint(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint) { ControlpointVector allControlPoints = GetAllControlPointsOfCase(caseID); if (allControlPoints.empty()) { return false; } const auto existingControlPoint = std::find_if(allControlPoints.begin(), allControlPoints.end(), [&controlPoint](const SemanticTypes::ControlPoint& currentControlPoint) { return currentControlPoint.UID == controlPoint.UID; }); if (existingControlPoint != allControlPoints.end()) { // case overwriting: control point already contained in the list of all existing control points // -> duplicated control point found (regardless of the actual start point and end point) return true; } else { // case adding: control point not contained in the list of all existing control points for (const auto& existingControlPoint : allControlPoints) { bool contained = InsideControlPoint(controlPoint, existingControlPoint); if (contained) { return true; } } } return false; } diff --git a/Modules/SemanticRelationsUI/include/QmitkPatientTableInspector.h b/Modules/SemanticRelationsUI/include/QmitkPatientTableInspector.h index eae348e7aa..54b62b526e 100644 --- a/Modules/SemanticRelationsUI/include/QmitkPatientTableInspector.h +++ b/Modules/SemanticRelationsUI/include/QmitkPatientTableInspector.h @@ -1,89 +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 "ui_QmitkPatientTableInspector.h" // qt widgets module #include "QmitkEnums.h" // 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 OnContextMenuSetControlPoint(); 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/src/QmitkPatientTableInspector.cpp b/Modules/SemanticRelationsUI/src/QmitkPatientTableInspector.cpp index b61b5b617b..7aefd51bba 100644 --- a/Modules/SemanticRelationsUI/src/QmitkPatientTableInspector.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkPatientTableInspector.cpp @@ -1,265 +1,155 @@ /*=================================================================== 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 "QmitkControlPointDialog.h" // mitk qt widgets module #include "QmitkCustomVariants.h" #include "QmitkEnums.h" -// semantic relations module -#include -#include -#include - // qt #include #include QmitkPatientTableInspector::QmitkPatientTableInspector(QWidget* parent/* =nullptr*/) { m_Controls.setupUi(this); m_Controls.tableView->horizontalHeader()->setHighlightSections(false); m_Controls.tableView->verticalHeader()->setHighlightSections(false); 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()); 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::OnContextMenuSetControlPoint() -{ - QmitkControlPointDialog* inputDialog = new QmitkControlPointDialog(m_Controls.tableView); - inputDialog->setWindowTitle("Set control point"); - inputDialog->SetCurrentDate(mitk::GetDICOMDateFromDataNode(m_SelectedDataNode)); - - int dialogReturnValue = inputDialog->exec(); - if (QDialog::Rejected == dialogReturnValue) - { - return; - } - - // store the current control point to relink it, if anything goes wrong - mitk::SemanticTypes::ControlPoint originalControlPoint = m_StorageModel->GetSemanticRelations()->GetControlPointOfData(m_SelectedDataNode); - // unlink the data, that is about to receive a new date - // this is needed in order to not extend a single control point, to which the selected node is currently linked - m_StorageModel->GetSemanticRelations()->UnlinkDataFromControlPoint(m_SelectedDataNode); - - const QDate& userSelectedDate = inputDialog->GetCurrentDate(); - mitk::SemanticTypes::Date date; - date.UID = mitk::UIDGeneratorBoost::GenerateUID(); - date.year = userSelectedDate.year(); - date.month = userSelectedDate.month(); - date.day = userSelectedDate.day(); - - std::vector allControlPoints = m_StorageModel->GetSemanticRelations()->GetAllControlPointsOfCase(m_StorageModel->GetCaseID()); - if (!allControlPoints.empty()) - { - // need to check if an already existing control point fits/contains the user control point - mitk::SemanticTypes::ControlPoint fittingControlPoint = mitk::FindFittingControlPoint(date, allControlPoints); - if (!fittingControlPoint.UID.empty()) - { - try - { - // found a fitting control point - m_StorageModel->GetSemanticRelations()->LinkDataToControlPoint(m_SelectedDataNode, fittingControlPoint, false); - m_StorageModel->UpdateModelData(); - } - catch (const mitk::SemanticRelationException&) - { - MITK_INFO << "The data can not be linked to the fitting control point."; - try - { - // link to the original control point - m_StorageModel->GetSemanticRelations()->LinkDataToControlPoint(m_SelectedDataNode, originalControlPoint, false); - } - catch (const mitk::SemanticRelationException&) - { - MITK_INFO << "The data can not be linked to its original control point. Inconsistency in the semantic relations storage assumed."; - } - } - return; - } - - // did not find a fitting control point, although some control points already exist - // need to check if a close control point can be found and extended - mitk::SemanticTypes::ControlPoint extendedControlPoint = mitk::ExtendClosestControlPoint(date, allControlPoints); - if (!extendedControlPoint.UID.empty()) - { - try - { - // found and extended a close control point - m_StorageModel->GetSemanticRelations()->OverwriteControlPointAndLinkData(m_SelectedDataNode, extendedControlPoint, false); - m_StorageModel->UpdateModelData(); - } - catch (const mitk::SemanticRelationException&) - { - MITK_INFO << "The extended control point can not be overwritten and the data can not be linked to this control point."; - try - { - // link to the original control point - m_StorageModel->GetSemanticRelations()->LinkDataToControlPoint(m_SelectedDataNode, originalControlPoint, false); - } - catch (const mitk::SemanticRelationException&) - { - MITK_INFO << "The data can not be linked to its original control point. Inconsistency in the semantic relations storage assumed."; - } - } - return; - } - } - - // generate a control point from the user-given date - mitk::SemanticTypes::ControlPoint controlPointFromUserDate = mitk::GenerateControlPoint(date); - try - { - m_StorageModel->GetSemanticRelations()->AddControlPointAndLinkData(m_SelectedDataNode, controlPointFromUserDate, false); - m_StorageModel->UpdateModelData(); - } - catch (const mitk::SemanticRelationException&) - { - MITK_INFO << "The control point can not be added and the data can not be linked to this control point."; - try - { - // link to the original control point - m_StorageModel->GetSemanticRelations()->LinkDataToControlPoint(m_SelectedDataNode, originalControlPoint, false); - } - catch (const mitk::SemanticRelationException&) - { - MITK_INFO << "The data can not be linked to its original control point. Inconsistency in the semantic relations storage assumed."; - } - } -} - 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/Plugins/org.mitk.gui.qt.semanticrelations/files.cmake b/Plugins/org.mitk.gui.qt.semanticrelations/files.cmake index 41ff6b2801..193d7eaef8 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/files.cmake +++ b/Plugins/org.mitk.gui.qt.semanticrelations/files.cmake @@ -1,34 +1,38 @@ set(INTERNAL_CPP_FILES mitkPluginActivator.cpp + QmitkDataNodeControlPointAction.cpp QmitkDataNodeInformationTypeAction.cpp QmitkDataNodeOpenInAction.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/QmitkDataNodeControlPointAction.h src/internal/QmitkDataNodeInformationTypeAction.h src/internal/QmitkDataNodeOpenInAction.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/QmitkDataNodeInformationTypeAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeControlPointAction.cpp similarity index 55% copy from Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeInformationTypeAction.cpp copy to Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeControlPointAction.cpp index d9c09a3b24..2ad0ce853a 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeInformationTypeAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeControlPointAction.cpp @@ -1,131 +1,152 @@ /*=================================================================== 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 "QmitkDataNodeInformationTypeAction.h" +#include "QmitkDataNodeControlPointAction.h" // semantic relations module +#include #include +#include + +// semantic relations UI module +#include "QmitkControlPointDialog.h" // mitk gui common plugin #include // berry #include #include // qt #include -#include -QmitkDataNodeInformationTypeAction::QmitkDataNodeInformationTypeAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) +QmitkDataNodeControlPointAction::QmitkDataNodeControlPointAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) : QAction(parent) , m_WorkbenchPartSite(workbenchPartSite) { - setText(tr("Set information type")); + setText(tr("Set control point")); m_Parent = parent; InitializeAction(); } -QmitkDataNodeInformationTypeAction::QmitkDataNodeInformationTypeAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) +QmitkDataNodeControlPointAction::QmitkDataNodeControlPointAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) : QAction(parent) , m_WorkbenchPartSite(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) { - setText(tr("Open in")); + setText(tr("Set control point")); m_Parent = parent; InitializeAction(); } -QmitkDataNodeInformationTypeAction::~QmitkDataNodeInformationTypeAction() +QmitkDataNodeControlPointAction::~QmitkDataNodeControlPointAction() { // nothing here } -void QmitkDataNodeInformationTypeAction::SetDataStorage(mitk::DataStorage* dataStorage) +void QmitkDataNodeControlPointAction::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 QmitkDataNodeInformationTypeAction::InitializeAction() +void QmitkDataNodeControlPointAction::InitializeAction() { - connect(this, &QAction::triggered, this, &QmitkDataNodeInformationTypeAction::OnActionTriggered); + connect(this, &QAction::triggered, this, &QmitkDataNodeControlPointAction::OnActionTriggered); } -void QmitkDataNodeInformationTypeAction::OnActionTriggered(bool checked) +void QmitkDataNodeControlPointAction::OnActionTriggered(bool checked) { + if (nullptr == m_SemanticRelations) + { + return; + } + auto dataNode = GetSelectedNode(); if (dataNode.IsNull()) { return; } - bool ok = false; - QString text = QInputDialog::getText(m_Parent, tr("Set information type of selected node"), tr("Information type:"), QLineEdit::Normal, "", &ok); - if (ok && !text.isEmpty()) + QmitkControlPointDialog* inputDialog = new QmitkControlPointDialog(m_Parent); + inputDialog->setWindowTitle("Set control point"); + inputDialog->SetCurrentDate(mitk::GetDICOMDateFromDataNode(dataNode)); + + int dialogReturnValue = inputDialog->exec(); + if (QDialog::Rejected == dialogReturnValue) + { + return; + } + + const QDate& userSelectedDate = inputDialog->GetCurrentDate(); + mitk::SemanticTypes::Date date; + date.UID = mitk::UIDGeneratorBoost::GenerateUID(); + date.year = userSelectedDate.year(); + date.month = userSelectedDate.month(); + date.day = userSelectedDate.day(); + + try { - try - { - m_SemanticRelations->SetInformationType(dataNode, text.toStdString()); - } - catch (const mitk::SemanticRelationException&) - { - return; - } + m_SemanticRelations->SetControlPointFromDate(dataNode, date); + } + catch (const mitk::SemanticRelationException&) + { + return; } } -QList QmitkDataNodeInformationTypeAction::GetSelectedNodes() +QList QmitkDataNodeControlPointAction::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 QmitkDataNodeInformationTypeAction::GetSelectedNode() +mitk::DataNode::Pointer QmitkDataNodeControlPointAction::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; } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeInformationTypeAction.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeControlPointAction.h similarity index 72% copy from Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeInformationTypeAction.h copy to Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeControlPointAction.h index 9ac84af69f..08be01e956 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeInformationTypeAction.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeControlPointAction.h @@ -1,65 +1,64 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ -#ifndef QMITKDATANODEINFORMATIONTYPEACTION_H -#define QMITKDATANODEINFORMATIONTYPEACTION_H +#ifndef QMITKDATANODECONTROLPOINTEACTION_H +#define QMITKDATANODECONTROLPOINTEACTION_H // mitk core #include #include #include // semantic relations module #include // berry #include // qt #include -class QmitkDataNodeInformationTypeAction : public QAction +class QmitkDataNodeControlPointAction : public QAction { Q_OBJECT public: - QmitkDataNodeInformationTypeAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite); - QmitkDataNodeInformationTypeAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite); + QmitkDataNodeControlPointAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite); + QmitkDataNodeControlPointAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite); - virtual ~QmitkDataNodeInformationTypeAction() override; + virtual ~QmitkDataNodeControlPointAction() override; void SetDataStorage(mitk::DataStorage* dataStorage); private Q_SLOTS: void OnActionTriggered(bool); protected: void InitializeAction(); QList GetSelectedNodes(); mitk::DataNode::Pointer GetSelectedNode(); QWidget* m_Parent; berry::IWorkbenchPartSite::WeakPtr m_WorkbenchPartSite; mitk::WeakPointer m_DataStorage; std::unique_ptr m_SemanticRelations; - }; -#endif // QMITKDATANODEINFORMATIONTYPEACTION_H +#endif // QMITKDATANODECONTROLPOINTEACTION_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeInformationTypeAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeInformationTypeAction.cpp index d9c09a3b24..bc1c82e249 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeInformationTypeAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeInformationTypeAction.cpp @@ -1,131 +1,135 @@ /*=================================================================== 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 "QmitkDataNodeInformationTypeAction.h" // semantic relations module #include // mitk gui common plugin #include // berry #include #include // qt #include -#include QmitkDataNodeInformationTypeAction::QmitkDataNodeInformationTypeAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) : QAction(parent) , m_WorkbenchPartSite(workbenchPartSite) { setText(tr("Set information type")); m_Parent = parent; InitializeAction(); } QmitkDataNodeInformationTypeAction::QmitkDataNodeInformationTypeAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) : QAction(parent) , m_WorkbenchPartSite(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) { - setText(tr("Open in")); + setText(tr("Set information type")); m_Parent = parent; InitializeAction(); } QmitkDataNodeInformationTypeAction::~QmitkDataNodeInformationTypeAction() { // nothing here } void QmitkDataNodeInformationTypeAction::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 QmitkDataNodeInformationTypeAction::InitializeAction() { connect(this, &QAction::triggered, this, &QmitkDataNodeInformationTypeAction::OnActionTriggered); } void QmitkDataNodeInformationTypeAction::OnActionTriggered(bool checked) { + if (nullptr == m_SemanticRelations) + { + return; + } + auto dataNode = GetSelectedNode(); if (dataNode.IsNull()) { return; } bool ok = false; QString text = QInputDialog::getText(m_Parent, tr("Set information type of selected node"), tr("Information type:"), QLineEdit::Normal, "", &ok); if (ok && !text.isEmpty()) { try { m_SemanticRelations->SetInformationType(dataNode, text.toStdString()); } catch (const mitk::SemanticRelationException&) { return; } } } QList QmitkDataNodeInformationTypeAction::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 QmitkDataNodeInformationTypeAction::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; } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeInformationTypeAction.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeInformationTypeAction.h index 9ac84af69f..5985bf5ee3 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeInformationTypeAction.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeInformationTypeAction.h @@ -1,65 +1,64 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QMITKDATANODEINFORMATIONTYPEACTION_H #define QMITKDATANODEINFORMATIONTYPEACTION_H // mitk core #include #include #include // semantic relations module #include // berry #include // qt #include class QmitkDataNodeInformationTypeAction : public QAction { Q_OBJECT public: QmitkDataNodeInformationTypeAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite); QmitkDataNodeInformationTypeAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite); virtual ~QmitkDataNodeInformationTypeAction() override; void SetDataStorage(mitk::DataStorage* dataStorage); private Q_SLOTS: void OnActionTriggered(bool); protected: void InitializeAction(); QList GetSelectedNodes(); mitk::DataNode::Pointer GetSelectedNode(); QWidget* m_Parent; berry::IWorkbenchPartSite::WeakPtr m_WorkbenchPartSite; mitk::WeakPointer m_DataStorage; std::unique_ptr m_SemanticRelations; - }; #endif // QMITKDATANODEINFORMATIONTYPEACTION_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsContextMenu.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsContextMenu.cpp new file mode 100644 index 0000000000..c769f88f65 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsContextMenu.cpp @@ -0,0 +1,70 @@ +/*=================================================================== + +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()); + } +} + +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 QmitkDataNodeControlPointAction(m_Parent, m_WorkbenchPartSite.Lock()); + addAction(m_ControlPointAction); + + m_InformationTypeAction = new QmitkDataNodeInformationTypeAction(m_Parent, m_WorkbenchPartSite.Lock()); + addAction(m_InformationTypeAction); + + 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 new file mode 100644 index 0000000000..d41945ad30 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsContextMenu.h @@ -0,0 +1,69 @@ +/*=================================================================== + +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 "QmitkDataNodeControlPointAction.h" +#include "QmitkDataNodeInformationTypeAction.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; + + QmitkDataNodeControlPointAction* m_ControlPointAction; + QmitkDataNodeInformationTypeAction* m_InformationTypeAction; + 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 b2316b2f92..e5a903cda0 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.cpp @@ -1,395 +1,384 @@ /*=================================================================== 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" // semantic relations module #include #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 QMenu(m_PatientTableInspector); - - m_InformationTypeAction = new QmitkDataNodeInformationTypeAction(m_ContextMenu, GetSite()); - m_InformationTypeAction->SetDataStorage(GetDataStorage()); - m_ContextMenu->addAction(m_InformationTypeAction); - - m_OpenInAction = new QmitkDataNodeOpenInAction(m_ContextMenu, GetSite()); - m_ContextMenu->addAction(m_OpenInAction); + 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_OpenInAction->SetControlledRenderer(controlledRenderer); + 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, this, &QmitkSemanticRelationsView::OnContextMenuRequested); + connect(m_PatientTableInspector, &QmitkPatientTableInspector::OnContextMenuRequested, m_ContextMenu, &QmitkSemanticRelationsContextMenu::OnContextMenuRequested); connect(m_PatientTableInspector, &QmitkPatientTableInspector::OnNodeRemoved, this, &QmitkSemanticRelationsView::OnNodeRemoved); } QItemSelectionModel* QmitkSemanticRelationsView::GetDataNodeSelectionModel() const { return m_PatientTableInspector->GetSelectionModel(); } void QmitkSemanticRelationsView::NodeRemoved(const mitk::DataNode* dataNode) { if (nullptr == dataNode) { return; } if (mitk::NodePredicates::GetImagePredicate()->CheckNode(dataNode)) { RemoveImage(dataNode); } else if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { RemoveSegmentation(dataNode); } } void QmitkSemanticRelationsView::OnCaseIDSelectionChanged(const QString& caseID) { m_LesionInfoWidget->SetCurrentCaseID(caseID.toStdString()); m_PatientTableInspector->SetCaseID(caseID.toStdString()); } 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::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::OnNodesAdded(QmitkDnDDataNodeWidget* dnDDataNodeWidget, std::vector nodes) { for (mitk::DataNode* dataNode : nodes) { if (nullptr == dataNode) { continue; } if (mitk::NodePredicates::GetImagePredicate()->CheckNode(dataNode)) { AddImage(dataNode); } else if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { AddSegmentation(dataNode); } } } -void QmitkSemanticRelationsView::OnContextMenuRequested(const QPoint& /*pos*/) -{ - m_ContextMenu->popup(QCursor::pos()); -} - void QmitkSemanticRelationsView::OnNodeRemoved(const mitk::DataNode* dataNode) { NodeRemoved(dataNode); } 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); } } } void QmitkSemanticRelationsView::AddImage(const mitk::DataNode* image) { if (nullptr == image) { return; } try { // add the image to the semantic relations storage m_SemanticRelations->AddImage(image); mitk::SemanticTypes::CaseID caseID = mitk::GetCaseIDFromDataNode(image); AddToComboBox(caseID); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox; msgBox.setWindowTitle("Could not add the selected image."); msgBox.setText("The program wasn't able to correctly add the selected images.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } } void QmitkSemanticRelationsView::AddSegmentation(const mitk::DataNode* segmentation) { if (nullptr == segmentation) { return; } mitk::BaseData* baseData = segmentation->GetData(); if (nullptr == baseData) { return; } // continue with valid segmentation data // get parent node of the current segmentation node with the node predicate mitk::DataStorage::SetOfObjects::ConstPointer parentNodes = GetDataStorage()->GetSources(segmentation, mitk::NodePredicates::GetImagePredicate(), false); // check for already existing, identifying base properties mitk::BaseProperty* caseIDProperty = baseData->GetProperty("DICOM.0010.0010"); mitk::BaseProperty* nodeIDProperty = baseData->GetProperty("DICOM.0020.000E"); if (nullptr == caseIDProperty || nullptr == nodeIDProperty) { MITK_INFO << "No DICOM tags for case and node identification found. Transferring DICOM tags from the parent node to the selected segmentation node."; mitk::SemanticTypes::CaseID caseID = mitk::GetCaseIDFromDataNode(parentNodes->front()); mitk::SemanticTypes::ID nodeID = mitk::GetIDFromDataNode(parentNodes->front()); // transfer DICOM tags to the segmentation node mitk::StringProperty::Pointer caseIDTag = mitk::StringProperty::New(caseID); baseData->SetProperty("DICOM.0010.0010", caseIDTag); // DICOM tag is "PatientName" // add UID to distinguish between different segmentations of the same parent node mitk::StringProperty::Pointer nodeIDTag = mitk::StringProperty::New(nodeID + mitk::UIDGeneratorBoost::GenerateUID()); baseData->SetProperty("DICOM.0020.000E", nodeIDTag); // DICOM tag is "SeriesInstanceUID" } try { m_SemanticRelations->AddSegmentation(segmentation, parentNodes->front()); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox; msgBox.setWindowTitle("Could not add the selected segmentation."); msgBox.setText("The program wasn't able to correctly add the selected segmentation.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } } void QmitkSemanticRelationsView::RemoveImage(const mitk::DataNode* image) { try { m_SemanticRelations->RemoveImage(image); mitk::SemanticTypes::CaseID caseID = mitk::GetCaseIDFromDataNode(image); RemoveFromComboBox(caseID); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; MITK_WARN << "Could not correctly remove the selected image from the semantic relations model.\n" << "Reason: " + exceptionMessage.str(); } } void QmitkSemanticRelationsView::RemoveSegmentation(const mitk::DataNode* segmentation) { try { m_SemanticRelations->RemoveSegmentation(segmentation); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; MITK_WARN << "Could not correctly remove the selected segmentation from the semantic relations model.\n" << "Reason: " + exceptionMessage.str(); } } 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 b2d51657b9..418725afe1 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.h @@ -1,123 +1,114 @@ /*=================================================================== 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 "QmitkDataNodeInformationTypeAction.h" -#include "QmitkDataNodeOpenInAction.h" #include "QmitkLesionInfoWidget.h" +#include "QmitkSemanticRelationsContextMenu.h" // semantic relations module #include #include // semantic relations UI module #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. -* -* SIDE NOTE: Modifying the control points and information types of data in the semantic relations model is currently done -* by using the right-click context-menu of the "PatientTableWidget" in the "Select Patient Node"-Dialog. -* This is a leftover from when the widget was not used as a selection widget. Those functionality will be moved -* to this main GUI soon. */ 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 OnCaseIDSelectionChanged(const QString&); void AddToComboBox(const mitk::SemanticTypes::CaseID&); void OnLesionChanged(const mitk::SemanticTypes::Lesion&); void OnDataNodeDoubleClicked(const mitk::DataNode*); void OnNodesAdded(QmitkDnDDataNodeWidget*, std::vector); - void OnContextMenuRequested(const QPoint&); void OnNodeRemoved(const mitk::DataNode* 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 RemoveFromComboBox(const mitk::SemanticTypes::CaseID& caseID); void OpenInEditor(const mitk::DataNode* dataNode); void JumpToPosition(const mitk::DataNode* dataNode); void AddImage(const mitk::DataNode* image); void AddSegmentation(const mitk::DataNode* segmentation); void RemoveImage(const mitk::DataNode* image); void RemoveSegmentation(const mitk::DataNode* segmentation); Ui::QmitkSemanticRelationsControls m_Controls; QmitkLesionInfoWidget* m_LesionInfoWidget; QmitkPatientTableInspector* m_PatientTableInspector; QmitkDnDDataNodeWidget* m_DnDDataNodeWidget; - QMenu* m_ContextMenu; - QmitkDataNodeInformationTypeAction* m_InformationTypeAction; - QmitkDataNodeOpenInAction* m_OpenInAction; + QmitkSemanticRelationsContextMenu* m_ContextMenu; std::unique_ptr m_SemanticRelations; }; #endif // QMITKSEMANTICRELATIONSVIEW_H