diff --git a/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h b/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h index 22729e9e03..6fee950a35 100644 --- a/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h +++ b/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h @@ -1,125 +1,142 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QMITKPATIENTTABLEMODEL_H #define QMITKPATIENTTABLEMODEL_H // semantic relations UI module #include "QmitkAbstractSemanticRelationsStorageModel.h" // semantic relations module #include // mitk core #include // qt #include #include -/* +/** * @brief The QmitkPatientTableModel is a subclass of the QmitkAbstractSemanticRelationsStorageModel and holds the semantic relations data of the currently selected case. * * The QmitkPatientTableModel uses the 'data' function to return either the data node of a table cell or the thumbnail of the underlying image. * The horizontal header of the table shows the control points of the current case and the vertical header of the table shows the information types of the current case. * Using the 'GetFilteredData'-function of the SemanticRelations-class the model is able to retrieve the correct data node for each table entry. * * Additionally the model creates and holds the QPixmaps of the known data nodes in order to return a thumbnail, if needed. */ class QmitkPatientTableModel : public QmitkAbstractSemanticRelationsStorageModel { Q_OBJECT public: QmitkPatientTableModel(QObject* parent = nullptr); ~QmitkPatientTableModel(); ////////////////////////////////////////////////////////////////////////// // overridden functions from QAbstractItemModel ////////////////////////////////////////////////////////////////////////// virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; virtual QModelIndex parent(const QModelIndex& child) const override; virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override; virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override; virtual Qt::ItemFlags flags(const QModelIndex& index) const override; ////////////////////////////////////////////////////////////////////////// /// end override ///////////////////////////////////////////////////////////////////////// void SetNodeType(const std::string& nodeType); protected: // the following functions have to be overridden but are not implemented in this model virtual void NodePredicateChanged() override; virtual void NodeAdded(const mitk::DataNode* node) override; virtual void NodeChanged(const mitk::DataNode* node) override; virtual void NodeRemoved(const mitk::DataNode* node) override; /** * @brief Overridden from 'QmitkAbstractSemanticRelationsStorageModel': This function retrieves all control points * and information types of the current case and stores them to define the header of the table. * Furthermore all images are retrieved and the pixmap of the images are generated and stored. */ virtual void SetData() override; private: - void SetDataNodes(); void SetHeaderModel(); + void SetPixmaps(); + void SetLesionPresences(); /** * @brief The function uses the ID of the node to see if a pixmap was already set. If not, the given pixmap * is used and stored inside a member variable. If the pixmap was already set, it will be overwritten. * Using 'nullptr' as a pixmap will erase the entry for the given data node. * * @param dataNode The data node whose pixmap should be set - * @param pixmapFromImage The pixmap that shows an image of the content of the data node + * @param pixmapFromImage The pixmap that shows an image of the content of the data node */ void SetPixmapOfNode(const mitk::DataNode* dataNode, QPixmap* pixmapFromImage); - - void SetLesionPresence(const mitk::DataNode* dataNode, bool lesionPresence); - + /** + * @brief Check if the member lesion is present on the given data node. + * For this the given data node is checked against the image / segmentation predicate. If the node is a + * segmentation, the represented lesion of the segmentation is simply compared against the member lesion. + * If the node is an image its derived segmentations are retrieved. For each derived segmentation + * the represented lesion is compared against the member lesion. + * The function is used by the 'SetLesionPresences' function. + * + */ bool IsLesionPresentOnDataNode(const mitk::DataNode* dataNode) const; - - /* + /** + * @brief The function uses the ID of the node to see if a lesion presence was already set. If not, the given + * bool value is used and stored inside a member variable. If the lesion presence was already set, it + * will be overwritten. + * The function is used by the 'SetLesionPresences' function. + * + * @param dataNode The data node whose lesion presence should be set + * @param lesionPresence The bool value that defines the lesion presence of the given data node + */ + void SetLesionPresenceOfNode(const mitk::DataNode* dataNode, bool lesionPresence); + /** * @brief Returns the data node that is associated with the given table entry (index). * * The function uses the SemanticRelations-class and the current control point data and information type data to * filter the nodes of the current case. * The index is used to access the correct row in the table (information type) and the correct column in the table (control point). * * @par index The QModelIndex of the table entry */ mitk::DataNode* GetCurrentDataNode(const QModelIndex &index) const; std::map m_PixmapMap; std::map m_LesionPresence; mitk::SemanticTypes::InformationTypeVector m_InformationTypes; mitk::SemanticTypes::ControlPointVector m_ControlPoints; mitk::SemanticTypes::ExaminationPeriodVector m_ExaminationPeriods; + mitk::SemanticRelations::DataNodeVector m_CurrentDataNodes; std::string m_SelectedNodeType; QStandardItemModel* m_HeaderModel; }; #endif // QMITKPATIENTTABLEMODEL_H diff --git a/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp b/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp index 9bbeab9eb5..8ec94c329e 100644 --- a/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp @@ -1,387 +1,379 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // semantic relations UI module #include "QmitkPatientTableModel.h" #include "QmitkPatientTableHeaderView.h" #include "QmitkSemanticRelationsUIHelper.h" // semantic relations module #include #include #include #include #include // qt #include // c++ #include #include QmitkPatientTableModel::QmitkPatientTableModel(QObject* parent /*= nullptr*/) : QmitkAbstractSemanticRelationsStorageModel(parent) , m_SelectedNodeType("Image") { m_HeaderModel = new QStandardItemModel(this); } QmitkPatientTableModel::~QmitkPatientTableModel() { // nothing here } QModelIndex QmitkPatientTableModel::index(int row, int column, const QModelIndex& parent/* = QModelIndex()*/) const { if (hasIndex(row, column, parent)) { return createIndex(row, column); } return QModelIndex(); } QModelIndex QmitkPatientTableModel::parent(const QModelIndex& child) const { return QModelIndex(); } int QmitkPatientTableModel::rowCount(const QModelIndex& parent/* = QModelIndex()*/) const { if (parent.isValid()) { return 0; } return m_InformationTypes.size(); } int QmitkPatientTableModel::columnCount(const QModelIndex& parent/* = QModelIndex()*/) const { return m_ControlPoints.size(); } QVariant QmitkPatientTableModel::data(const QModelIndex& index, int role/* = Qt::DisplayRole*/) const { if (QmitkPatientTableHeaderView::HorizontalHeaderDataRole == role) { return QVariant::fromValue(m_HeaderModel); } if (!index.isValid()) { return QVariant(); } if (index.row() < 0 || index.row() >= static_cast(m_InformationTypes.size()) || index.column() < 0 || index.column() >= static_cast(m_ControlPoints.size())) { return QVariant(); } mitk::DataNode* dataNode = GetCurrentDataNode(index); if (nullptr == dataNode) { return QVariant(); } if (Qt::DecorationRole == role) { auto it = m_PixmapMap.find(dataNode); if (it != m_PixmapMap.end()) { return QVariant(it->second); } } if (QmitkDataNodeRole == role) { return QVariant::fromValue(mitk::DataNode::Pointer(dataNode)); } if (QmitkDataNodeRawPointerRole == role) { return QVariant::fromValue(dataNode); } if (Qt::BackgroundColorRole == role) { auto it = m_LesionPresence.find(dataNode); if (it != m_LesionPresence.end()) { return it->second ? QVariant(QColor(Qt::green)) : QVariant(QColor(Qt::transparent)); } return QVariant(QColor(Qt::transparent)); } return QVariant(); } QVariant QmitkPatientTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (Qt::Vertical == orientation && Qt::DisplayRole == role) { if (m_InformationTypes.size() > section) { mitk::SemanticTypes::InformationType currentInformationType = m_InformationTypes.at(section); return QVariant(QString::fromStdString(currentInformationType)); } } return QVariant(); } Qt::ItemFlags QmitkPatientTableModel::flags(const QModelIndex& index) const { Qt::ItemFlags flags; mitk::DataNode* dataNode = GetCurrentDataNode(index); if (nullptr != dataNode) { flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; } return flags; } void QmitkPatientTableModel::SetNodeType(const std::string& nodeType) { m_SelectedNodeType = nodeType; UpdateModelData(); } void QmitkPatientTableModel::NodePredicateChanged() { UpdateModelData(); } void QmitkPatientTableModel::NodeAdded(const mitk::DataNode* node) { // does not react to data storage changes } void QmitkPatientTableModel::NodeChanged(const mitk::DataNode* node) { // nothing here, since the "'NodeChanged'-event is currently sent far too often //UpdateModelData(); } void QmitkPatientTableModel::NodeRemoved(const mitk::DataNode* node) { // does not react to data storage changes } -void QmitkPatientTableModel::SetPixmapOfNode(const mitk::DataNode* dataNode, QPixmap* pixmapFromImage) -{ - if (nullptr == dataNode) - { - return; - } - - std::map::iterator iter = m_PixmapMap.find(dataNode); - if (iter != m_PixmapMap.end()) - { - // key already existing - if (nullptr != pixmapFromImage) - { - // overwrite already stored pixmap - iter->second = pixmapFromImage->scaled(120, 120, Qt::IgnoreAspectRatio); - } - else - { - // remove key if no pixmap is given - m_PixmapMap.erase(iter); - } - } - else - { - m_PixmapMap.insert(std::make_pair(dataNode, pixmapFromImage->scaled(120, 120, Qt::IgnoreAspectRatio))); - } -} - -void QmitkPatientTableModel::SetLesionPresence(const mitk::DataNode* dataNode, bool lesionPresence) -{ - if (nullptr == dataNode) - { - return; - } - - std::map::iterator iter = m_LesionPresence.find(dataNode); - if (iter != m_LesionPresence.end()) - { - // key already existing, overwrite already stored bool value - iter->second = lesionPresence; - } - else - { - m_LesionPresence.insert(std::make_pair(dataNode, lesionPresence)); - } -} - void QmitkPatientTableModel::SetData() { // get all control points of current case m_ControlPoints = m_SemanticRelations->GetAllControlPointsOfCase(m_CaseID); // sort the vector of control points for the timeline std::sort(m_ControlPoints.begin(), m_ControlPoints.end()); // get all examination periods of current case m_ExaminationPeriods = m_SemanticRelations->GetAllExaminationPeriodsOfCase(m_CaseID); // sort the vector of examination periods for the timeline mitk::SortExaminationPeriods(m_ExaminationPeriods, m_ControlPoints); // get all information types points of current case m_InformationTypes = m_SemanticRelations->GetAllInformationTypesOfCase(m_CaseID); - m_PixmapMap.clear(); - m_LesionPresence.clear(); - - SetDataNodes(); - SetHeaderModel(); -} - -void QmitkPatientTableModel::SetDataNodes() -{ - std::vector allDataNodes; - if("Image" == m_SelectedNodeType) + if ("Image" == m_SelectedNodeType) { - allDataNodes = m_SemanticRelations->GetAllImagesOfCase(m_CaseID); + m_CurrentDataNodes = m_SemanticRelations->GetAllImagesOfCase(m_CaseID); } - else if("Segmentation" == m_SelectedNodeType) + else if ("Segmentation" == m_SelectedNodeType) { - allDataNodes = m_SemanticRelations->GetAllSegmentationsOfCase(m_CaseID); + m_CurrentDataNodes = m_SemanticRelations->GetAllSegmentationsOfCase(m_CaseID); } - for (const auto& dataNode : allDataNodes) - { - // set the pixmap for the current node - QPixmap pixmapFromImage = QmitkSemanticRelationsUIHelper::GetPixmapFromImageNode(dataNode); - SetPixmapOfNode(dataNode, &pixmapFromImage); - - // set the lesion presence for the current node - if (m_SemanticRelations->InstanceExists(m_CaseID, m_Lesion)) - { - bool lesionPresence = lesionPresence = IsLesionPresentOnDataNode(dataNode); - SetLesionPresence(dataNode, lesionPresence); - } - else - { - m_LesionPresence.clear(); - } - } + SetHeaderModel(); + SetPixmaps(); + SetLesionPresences(); } void QmitkPatientTableModel::SetHeaderModel() { m_HeaderModel->clear(); QStandardItem* rootItem = new QStandardItem("Timeline"); QList standardItems; for (const auto& examinationPeriod : m_ExaminationPeriods) { QStandardItem* examinationPeriodItem = new QStandardItem(QString::fromStdString(examinationPeriod.name)); standardItems.push_back(examinationPeriodItem); rootItem->appendColumn(standardItems); standardItems.clear(); const auto& currentControlPoints = examinationPeriod.controlPointUIDs; for (const auto& controlPointUID : currentControlPoints) { const auto& controlPoint = mitk::GetControlPointByUID(controlPointUID, m_ControlPoints); std::string controlPointAsString = mitk::GetControlPointAsString(controlPoint); QStandardItem* controlPointItem = new QStandardItem(QString::fromStdString(controlPointAsString)); standardItems.push_back(controlPointItem); examinationPeriodItem->appendColumn(standardItems); standardItems.clear(); } } m_HeaderModel->setItem(0, 0, rootItem); } +void QmitkPatientTableModel::SetPixmaps() +{ + m_PixmapMap.clear(); + for (const auto& dataNode : m_CurrentDataNodes) + { + // set the pixmap for the current node + QPixmap pixmapFromImage = QmitkSemanticRelationsUIHelper::GetPixmapFromImageNode(dataNode); + SetPixmapOfNode(dataNode, &pixmapFromImage); + } +} + +void QmitkPatientTableModel::SetPixmapOfNode(const mitk::DataNode* dataNode, QPixmap* pixmapFromImage) +{ + if (nullptr == dataNode) + { + return; + } + + std::map::iterator iter = m_PixmapMap.find(dataNode); + if (iter != m_PixmapMap.end()) + { + // key already existing + if (nullptr != pixmapFromImage) + { + // overwrite already stored pixmap + iter->second = pixmapFromImage->scaled(120, 120, Qt::IgnoreAspectRatio); + } + else + { + // remove key if no pixmap is given + m_PixmapMap.erase(iter); + } + } + else + { + m_PixmapMap.insert(std::make_pair(dataNode, pixmapFromImage->scaled(120, 120, Qt::IgnoreAspectRatio))); + } +} + +void QmitkPatientTableModel::SetLesionPresences() +{ + m_LesionPresence.clear(); + if (!m_SemanticRelations->InstanceExists(m_CaseID, m_Lesion)) + { + return; + } + + for (const auto& dataNode : m_CurrentDataNodes) + { + // set the lesion presence for the current node + bool lesionPresence = lesionPresence = IsLesionPresentOnDataNode(dataNode); + SetLesionPresenceOfNode(dataNode, lesionPresence); + } +} + bool QmitkPatientTableModel::IsLesionPresentOnDataNode(const mitk::DataNode* dataNode) const { if (m_DataStorage.IsExpired()) { return false; } auto dataStorage = m_DataStorage.Lock(); try { - mitk::SemanticRelations::DataNodeVector allSegmentationsOfLesion = m_SemanticRelations->GetAllSegmentationsOfLesion(m_CaseID, m_Lesion); - for (const auto& segmentation : allSegmentationsOfLesion) + if (mitk::NodePredicates::GetImagePredicate()->CheckNode(dataNode)) { - if ("Segmentation" == m_SelectedNodeType) - { - if (dataNode == segmentation) - { - // found a segmentation of the node that is represented by the selected lesion - return true; - } - } - else if ("Image" == m_SelectedNodeType) + // get segmentations of the image node with the segmentation predicate + mitk::DataStorage::SetOfObjects::ConstPointer segmentations = dataStorage->GetDerivations(dataNode, mitk::NodePredicates::GetSegmentationPredicate(), false); + for (auto it = segmentations->Begin(); it != segmentations->End(); ++it) { - // get parent node of the current segmentation node with the node predicate - mitk::DataStorage::SetOfObjects::ConstPointer parentNodes = dataStorage->GetSources(segmentation, mitk::NodePredicates::GetImagePredicate(), false); - for (auto it = parentNodes->Begin(); it != parentNodes->End(); ++it) - { - if (dataNode == it.Value()) - { - // found a segmentation of the node that is represented by the selected lesion - return true; - } - } + const auto representedLesion = m_SemanticRelations->GetRepresentedLesion(it.Value()); + return m_Lesion.UID == representedLesion.UID; } } + else if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) + { + const auto representedLesion = m_SemanticRelations->GetRepresentedLesion(dataNode); + return m_Lesion.UID == representedLesion.UID; + } } catch (const mitk::SemanticRelationException&) { return false; } return false; } +void QmitkPatientTableModel::SetLesionPresenceOfNode(const mitk::DataNode* dataNode, bool lesionPresence) +{ + if (nullptr == dataNode) + { + return; + } + + std::map::iterator iter = m_LesionPresence.find(dataNode); + if (iter != m_LesionPresence.end()) + { + // key already existing, overwrite already stored bool value + iter->second = lesionPresence; + } + else + { + m_LesionPresence.insert(std::make_pair(dataNode, lesionPresence)); + } +} + mitk::DataNode* QmitkPatientTableModel::GetCurrentDataNode(const QModelIndex& index) const { mitk::SemanticTypes::ControlPoint currentControlPoint = m_ControlPoints.at(index.column()); mitk::SemanticTypes::InformationType currentInformationType = m_InformationTypes.at(index.row()); try { std::vector filteredDataNodes; if ("Image" == m_SelectedNodeType) { - filteredDataNodes = m_SemanticRelations->GetAllSpecificImages(m_CaseID, currentControlPoint, currentInformationType); } else if ("Segmentation" == m_SelectedNodeType) { filteredDataNodes = m_SemanticRelations->GetAllSpecificSegmentations(m_CaseID, currentControlPoint, currentInformationType); } if (filteredDataNodes.empty()) { return nullptr; } return filteredDataNodes.front(); } catch (const mitk::SemanticRelationException&) { return nullptr; } }