diff --git a/Modules/SemanticRelationsUI/files.cmake b/Modules/SemanticRelationsUI/files.cmake index 9d2e0591b6..dc27298082 100644 --- a/Modules/SemanticRelationsUI/files.cmake +++ b/Modules/SemanticRelationsUI/files.cmake @@ -1,24 +1,26 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkModuleActivator.cpp QmitkAbstractSemanticRelationsStorageModel.cpp QmitkControlPointDialog.cpp QmitkLesionTextDialog.cpp + QmitkPatientTableHeaderView.cpp QmitkPatientTableInspector.cpp QmitkPatientTableModel.cpp QmitkSemanticRelationsUIHelper.cpp ) set(MOC_H_FILES include/QmitkAbstractSemanticRelationsStorageInspector.h include/QmitkAbstractSemanticRelationsStorageModel.h include/QmitkControlPointDialog.h include/QmitkLesionTextDialog.h + include/QmitkPatientTableHeaderView.h include/QmitkPatientTableInspector.h include/QmitkPatientTableModel.h ) set(UI_FILES src/QmitkPatientTableInspector.ui ) diff --git a/Modules/SemanticRelationsUI/include/QmitkPatientTableHeaderView.h b/Modules/SemanticRelationsUI/include/QmitkPatientTableHeaderView.h new file mode 100644 index 0000000000..263baafa41 --- /dev/null +++ b/Modules/SemanticRelationsUI/include/QmitkPatientTableHeaderView.h @@ -0,0 +1,83 @@ +/*=================================================================== + +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 QMITKPATIENTTABLEHEADERVIEW_H +#define QMITKPATIENTTABLEHEADERVIEW_H + +// semantic relations ui module +#include "MitkSemanticRelationsUIExports.h" + +// qt +#include +#include +#include + +/* +* @brief +*/ +class MITKSEMANTICRELATIONSUI_EXPORT QmitkPatientTableHeaderView : public QHeaderView +{ + Q_OBJECT + +public: + + QmitkPatientTableHeaderView(QWidget* parent = nullptr); + ~QmitkPatientTableHeaderView(); + + enum HeaderDataModelRoles + { + HorizontalHeaderDataRole = Qt::UserRole + }; + + /** + * @brief Set the model of the table view of this header view. + * This model returns a standard item model for the 'HorizontalHeaderDataRole'-role. + * The header model has been previously filled with header data. + * This function is overwritten from QHeaderView. + */ + virtual void setModel(QAbstractItemModel* model) override; + +protected: + + /** + * @brief Paint each header using 'PaintHeader'. + * This function is overwritten from QHeaderView. + */ + virtual void paintSection(QPainter* painter, const QRect& rect, int logicalIndex) const override; + /** + * @brief Get the section size by retrieving the text content. + * The section size is dependent on the child and parent headers. + * This function is overwritten from QHeaderView. + */ + QSize sectionSizeFromContents(int logicalIndex) const override; + +private: + + int PaintHeader(QPainter* painter, const QModelIndex& currentIndex, int logicalIndex, const QRect& sectionRect, int top) const; + QSize HeaderSize(const QModelIndex& index) const; + int CurrentHeaderLeft(const QModelIndex& currentIndex, const QModelIndex& headerIndex, int sectionIndex, int left) const; + int CurrentHeaderWidth(const QModelIndex& currentIndex, const QModelIndex& headerIndex, int sectionIndex) const; + + QModelIndexList ParentIndexList(QModelIndex index) const; + QModelIndex HeaderIndex(int sectionIndex) const; + QModelIndex FindHeader(const QModelIndex& currentIndex, int sectionIndex, int& currentHeaderIndex) const; + QModelIndexList ListHeader(const QModelIndex& currentIndex) const; + + QStandardItemModel* m_HeaderModel; + +}; + +#endif // QMITKPATIENTTABLEHEADERVIEW_H diff --git a/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h b/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h index 57fa34fcd3..c787fc44ce 100644 --- a/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h +++ b/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h @@ -1,121 +1,125 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QMITKPATIENTTABLEMODEL_H #define QMITKPATIENTTABLEMODEL_H // semantic relations module #include // semantic relations UI module #include // mitk core #include // qt #include +#include /* * @brief The QmitkPatientTableModel is a subclass of the QmitkAbstractSemanticRelationsStorageModel and holds the semantic relations data of the currently selected case. * * The QmitkPatientTableModel uses the 'data' function to return either the data node of a table cell or the thumbnail of the underlying image. * The horizontal header of the table shows the control points of the current case and the vertical header of the table shows the information types of the current case. * Using the 'GetFilteredData'-function of the SemanticRelations-class the model is able to retrieve the correct data node for each table entry. * * Additionally the model creates and holds the QPixmaps of the known data nodes in order to return a thumbnail, if needed. */ class QmitkPatientTableModel : public QmitkAbstractSemanticRelationsStorageModel { Q_OBJECT public: QmitkPatientTableModel(QObject* parent = nullptr); ~QmitkPatientTableModel(); ////////////////////////////////////////////////////////////////////////// // overridden functions from QAbstractItemModel ////////////////////////////////////////////////////////////////////////// virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; virtual QModelIndex parent(const QModelIndex &child) const override; virtual 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; void SetDataNodes(); private: + void SetHeaderModel(); + /** * @brief The function uses the ID of the node to see if a pixmap was already set. If not, the given pixmap * is used and stored inside a member variable. If the pixmap was already set, it will be overwritten. * Using 'nullptr' as a pixmap will erase the entry for the given data node. * * @param dataNode The data node whose pixmap should be set * @param pixmapFromImage The pixmap that shows an image of the content of the data node */ void SetPixmapOfNode(const mitk::DataNode* dataNode, QPixmap* pixmapFromImage); void SetLesionPresence(const mitk::DataNode* dataNode, bool lesionPresence); bool IsLesionPresentOnDataNode(const mitk::DataNode* dataNode) const; /* * @brief Returns the data node that is associated with the given table entry (index). * * The function uses the SemanticRelations-class and the current control point data and information type data to * filter the nodes of the current case. * The index is used to access the correct row in the table (information type) and the correct column in the table (control point). * * @par index The QModelIndex of the table entry */ mitk::DataNode* GetCurrentDataNode(const QModelIndex &index) const; std::map m_PixmapMap; std::map m_LesionPresence; std::vector m_InformationTypes; std::vector m_ControlPoints; std::string m_SelectedNodeType; + QStandardItemModel* m_HeaderModel; }; #endif // QMITKPATIENTTABLEMODEL_H diff --git a/Modules/SemanticRelationsUI/src/QmitkPatientTableHeaderView.cpp b/Modules/SemanticRelationsUI/src/QmitkPatientTableHeaderView.cpp new file mode 100644 index 0000000000..4f7bcb766e --- /dev/null +++ b/Modules/SemanticRelationsUI/src/QmitkPatientTableHeaderView.cpp @@ -0,0 +1,237 @@ +/*=================================================================== + +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 "QmitkPatientTableHeaderView.h" + +// qt +#include +#include + +QmitkPatientTableHeaderView::QmitkPatientTableHeaderView(QWidget* parent/* = nullptr*/) + : QHeaderView(Qt::Horizontal, parent) + , m_HeaderModel(nullptr) +{ + // nothing here +} + +QmitkPatientTableHeaderView::~QmitkPatientTableHeaderView() +{ + // nothing here +} + +void QmitkPatientTableHeaderView::setModel(QAbstractItemModel* model) +{ + // retrieve the header model from the given table model + QVariant variant = model->data(QModelIndex(), HorizontalHeaderDataRole); + if (variant.isValid() && variant.canConvert()) + { + m_HeaderModel = variant.value(); + } + + QHeaderView::setModel(model); +} + +void QmitkPatientTableHeaderView::paintSection(QPainter* painter, const QRect& rect, int logicalIndex) const +{ + if (rect.isValid()) + { + int top = rect.y(); + + QModelIndex leafIndex = HeaderIndex(logicalIndex); + QModelIndexList indexes = ParentIndexList(leafIndex); + for (const auto& index : indexes) + { + top = PaintHeader(painter, index, logicalIndex, rect, top); + } + + return; + } + + QHeaderView::paintSection(painter, rect, logicalIndex); +} + +QSize QmitkPatientTableHeaderView::sectionSizeFromContents(int logicalIndex) const +{ + if (nullptr != m_HeaderModel) + { + QModelIndex leafIndex = HeaderIndex(logicalIndex); + if (leafIndex.isValid()) + { + QSize headerSize = HeaderSize(leafIndex); + leafIndex = leafIndex.parent(); + while (leafIndex.isValid()) + { + headerSize.rheight() += HeaderSize(leafIndex).height(); + leafIndex = leafIndex.parent(); + } + return headerSize; + } + } + + return QHeaderView::sectionSizeFromContents(logicalIndex); +} + +int QmitkPatientTableHeaderView::PaintHeader(QPainter* painter, const QModelIndex& currentIndex, int logicalIndex, const QRect& sectionRect, int top) const +{ + QModelIndex headerIndex = HeaderIndex(logicalIndex); + int height = HeaderSize(currentIndex).height(); + if (currentIndex == headerIndex) + { + height = sectionRect.height() - top; + } + int left = CurrentHeaderLeft(currentIndex, headerIndex, logicalIndex, sectionRect.left()); + int width = CurrentHeaderWidth(currentIndex, headerIndex, logicalIndex); + + QStyleOptionHeader headerStyleOptions; + initStyleOption(&headerStyleOptions); + headerStyleOptions.text = currentIndex.data(Qt::DisplayRole).toString(); + headerStyleOptions.textAlignment = Qt::AlignCenter; + painter->save(); + + QRect rect(left, top, width, height); + headerStyleOptions.rect = rect; + style()->drawControl(QStyle::CE_Header, &headerStyleOptions, painter, this); + painter->restore(); + + return top + height; +} + +QSize QmitkPatientTableHeaderView::HeaderSize(const QModelIndex& index) const +{ + QFont font = this->font(); + QFontMetrics fontMetrics(font); + QSize fontSize = fontMetrics.size(0, index.data(Qt::DisplayRole).toString()); + QSize emptyFontSize = fontMetrics.size(0, ""); + + return fontSize + emptyFontSize; +} + +int QmitkPatientTableHeaderView::CurrentHeaderLeft(const QModelIndex& currentIndex, const QModelIndex& headerIndex, int sectionIndex, int left) const +{ + QModelIndexList headerList = ListHeader(currentIndex); + if (!headerList.empty()) + { + int index = headerList.indexOf(headerIndex); + int firstHeaderSectionIndex = sectionIndex - index; + --index; + for (; index >= 0; --index) + { + left -= sectionSize(firstHeaderSectionIndex + index); + } + } + + return left; +} + +int QmitkPatientTableHeaderView::CurrentHeaderWidth(const QModelIndex& currentIndex, const QModelIndex& headerIndex, int sectionIndex) const +{ + QModelIndexList headerList = ListHeader(currentIndex); + if (headerList.empty()) + { + return sectionSize(sectionIndex); + } + + int width = 0; + int index = headerList.indexOf(headerIndex); + int firstHeaderSectionIndex = sectionIndex - index; + for (int i = 0; i < headerList.size(); ++i) + { + width += sectionSize(firstHeaderSectionIndex + i); + } + + return width; +} + +QModelIndexList QmitkPatientTableHeaderView::ParentIndexList(QModelIndex index) const +{ + QModelIndexList indexList; + while (index.isValid()) + { + indexList.push_front(index); + index = index.parent(); + } + + return indexList; +} + +QModelIndex QmitkPatientTableHeaderView::HeaderIndex(int sectionIndex) const +{ + if (nullptr != m_HeaderModel) + { + int currentHeaderIndex = -1; + for (int i = 0; i < m_HeaderModel->columnCount(); ++i) + { + QModelIndex modelIndex = FindHeader(m_HeaderModel->index(0, i), sectionIndex, currentHeaderIndex); + if (modelIndex.isValid()) + { + return modelIndex; + } + } + } + + return QModelIndex(); +} + +QModelIndex QmitkPatientTableHeaderView::FindHeader(const QModelIndex& currentIndex, int sectionIndex, int& currentHeaderIndex) const +{ + if (currentIndex.isValid()) + { + int childCount = currentIndex.model()->columnCount(currentIndex); + if (childCount > 0) + { + for (int i = 0; i < childCount; ++i) + { + QModelIndex modelIndex = FindHeader(currentIndex.child(0, i), sectionIndex, currentHeaderIndex); + if (modelIndex.isValid()) + { + return modelIndex; + } + } + } + else + { + ++currentHeaderIndex; + if (currentHeaderIndex == sectionIndex) + { + return currentIndex; + } + } + } + + return QModelIndex(); +} + +QModelIndexList QmitkPatientTableHeaderView::ListHeader(const QModelIndex& currentIndex) const +{ + QModelIndexList headerList; + if (currentIndex.isValid()) + { + int childCount = currentIndex.model()->columnCount(currentIndex); + if (childCount > 0) + { + for (int i = 0; i < childCount; ++i) + { + headerList += ListHeader(currentIndex.child(0, i)); + } + } + else + { + headerList.push_back(currentIndex); + } + } + return headerList; +} diff --git a/Modules/SemanticRelationsUI/src/QmitkPatientTableInspector.cpp b/Modules/SemanticRelationsUI/src/QmitkPatientTableInspector.cpp index 7aefd51bba..546dec060b 100644 --- a/Modules/SemanticRelationsUI/src/QmitkPatientTableInspector.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkPatientTableInspector.cpp @@ -1,155 +1,160 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // semantic relations UI module #include "QmitkPatientTableInspector.h" +#include "QmitkPatientTableHeaderView.h" // mitk qt widgets module #include "QmitkCustomVariants.h" #include "QmitkEnums.h" // qt #include #include QmitkPatientTableInspector::QmitkPatientTableInspector(QWidget* parent/* =nullptr*/) { m_Controls.setupUi(this); + QmitkPatientTableHeaderView* patientTableHeaderView = new QmitkPatientTableHeaderView(m_Controls.tableView); + m_Controls.tableView->setHorizontalHeader(patientTableHeaderView); m_Controls.tableView->horizontalHeader()->setHighlightSections(false); + m_Controls.tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed); m_Controls.tableView->verticalHeader()->setHighlightSections(false); + m_Controls.tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); m_Controls.tableView->setSelectionMode(QAbstractItemView::SingleSelection); m_Controls.tableView->setSelectionBehavior(QAbstractItemView::SelectItems); m_Controls.tableView->setContextMenuPolicy(Qt::CustomContextMenu); m_StorageModel = new QmitkPatientTableModel(this); m_Controls.tableView->setModel(m_StorageModel); SetUpConnections(); } QAbstractItemView* QmitkPatientTableInspector::GetView() { return m_Controls.tableView; } const QAbstractItemView* QmitkPatientTableInspector::GetView() const { return m_Controls.tableView; } void QmitkPatientTableInspector::SetSelectionMode(SelectionMode mode) { m_Controls.tableView->setSelectionMode(mode); } QmitkPatientTableInspector::SelectionMode QmitkPatientTableInspector::GetSelectionMode() const { return m_Controls.tableView->selectionMode(); } void QmitkPatientTableInspector::SetCaseID(const mitk::SemanticTypes::CaseID& caseID) { m_StorageModel->SetCaseID(caseID); } void QmitkPatientTableInspector::SetLesion(const mitk::SemanticTypes::Lesion& lesion) { m_StorageModel->SetLesion(lesion); } QItemSelectionModel* QmitkPatientTableInspector::GetSelectionModel() { return m_Controls.tableView->selectionModel(); } void QmitkPatientTableInspector::Initialize() { m_StorageModel->SetDataStorage(m_DataStorage.Lock()); m_StorageModel->SetNodePredicate(m_NodePredicate); m_Connector->SetView(m_Controls.tableView); } void QmitkPatientTableInspector::OnModelUpdated() { m_Controls.tableView->resizeRowsToContents(); m_Controls.tableView->resizeColumnsToContents(); } void QmitkPatientTableInspector::OnNodeButtonClicked(const QString& nodeType) { m_StorageModel->SetNodeType(nodeType.toStdString()); } void QmitkPatientTableInspector::OnItemDoubleClicked(const QModelIndex& itemIndex) { if (itemIndex.isValid()) { QVariant qvariantDataNode = m_StorageModel->data(itemIndex, QmitkDataNodeRawPointerRole); if (qvariantDataNode.canConvert()) { mitk::DataNode* dataNode = qvariantDataNode.value(); emit DataNodeDoubleClicked(dataNode); } } } void QmitkPatientTableInspector::SetUpConnections() { connect(m_StorageModel, &QmitkPatientTableModel::ModelUpdated, this, &QmitkPatientTableInspector::OnModelUpdated); connect(m_Controls.tableView, &QTableView::customContextMenuRequested, this, &QmitkPatientTableInspector::OnContextMenuRequested); QSignalMapper* nodeButtonSignalMapper = new QSignalMapper(this); nodeButtonSignalMapper->setMapping(m_Controls.imageNodeButton, QString("Image")); nodeButtonSignalMapper->setMapping(m_Controls.segmentationNodeButton, QString("Segmentation")); connect(nodeButtonSignalMapper, static_cast(&QSignalMapper::mapped), this, &QmitkPatientTableInspector::OnNodeButtonClicked); connect(m_Controls.imageNodeButton, SIGNAL(clicked()), nodeButtonSignalMapper, SLOT(map())); connect(m_Controls.segmentationNodeButton, SIGNAL(clicked()), nodeButtonSignalMapper, SLOT(map())); m_Controls.imageNodeButton->setChecked(true); connect(m_Controls.tableView, &QTableView::doubleClicked, this, &QmitkPatientTableInspector::OnItemDoubleClicked); } void QmitkPatientTableInspector::keyPressEvent(QKeyEvent* e) { mitk::DataNode* dataNode = nullptr; QModelIndex selectedIndex = m_Controls.tableView->currentIndex(); if (selectedIndex.isValid()) { QVariant qvariantDataNode = m_StorageModel->data(selectedIndex, QmitkDataNodeRawPointerRole); if (qvariantDataNode.canConvert()) { dataNode = qvariantDataNode.value(); } } if (nullptr == dataNode) { return; } int key = e->key(); switch (key) { case Qt::Key_Delete: emit OnNodeRemoved(dataNode); break; default: break; } } diff --git a/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp b/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp index 46d3f73c84..49d5e9c668 100644 --- a/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp @@ -1,354 +1,381 @@ /*=================================================================== 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 // qt #include QmitkPatientTableModel::QmitkPatientTableModel(QObject* parent /*= nullptr*/) : QmitkAbstractSemanticRelationsStorageModel(parent) , m_SelectedNodeType("Image") { - // nothing here + m_HeaderModel = new QStandardItemModel(this); } QmitkPatientTableModel::~QmitkPatientTableModel() { // nothing here } QModelIndex QmitkPatientTableModel::index(int row, int column, const QModelIndex &parent/* = QModelIndex()*/) const { bool hasIndex = this->hasIndex(row, column, parent); if (hasIndex) { 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 { - if (parent.isValid()) - { - return 0; - } - 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); } } else if (QmitkDataNodeRole == role) { return QVariant::fromValue(mitk::DataNode::Pointer(dataNode)); } else if (QmitkDataNodeRawPointerRole == role) { - return QVariant::fromValue(dataNode); + return QVariant::fromValue(dataNode); } else 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::Horizontal == orientation && Qt::DisplayRole == role) - { - if (m_ControlPoints.size() > section) - { - mitk::SemanticTypes::ControlPoint currentControlPoint = m_ControlPoints.at(section); - // generate a string from the control point - std::string currentControlPointAsString = mitk::GetControlPointAsString(currentControlPoint); - return QVariant(QString::fromStdString(currentControlPointAsString)); - } - } 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 information types points of current case m_InformationTypes = m_SemanticRelations->GetAllInformationTypesOfCase(m_CaseID); m_PixmapMap.clear(); m_LesionPresence.clear(); SetDataNodes(); + SetHeaderModel(); } void QmitkPatientTableModel::SetDataNodes() { std::vector allDataNodes; if("Image" == m_SelectedNodeType) { allDataNodes = m_SemanticRelations->GetAllImagesOfCase(m_CaseID); } else if("Segmentation" == m_SelectedNodeType) { allDataNodes = m_SemanticRelations->GetAllSegmentationsOfCase(m_CaseID); } for (const auto& dataNode : allDataNodes) { // set the pixmap for the current node QPixmap pixmapFromImage = QmitkSemanticRelationsUIHelper::GetPixmapFromImageNode(dataNode); SetPixmapOfNode(dataNode, &pixmapFromImage); // set the lesion presence for the current node if (m_SemanticRelations->InstanceExists(m_CaseID, m_Lesion)) { bool lesionPresence = lesionPresence = IsLesionPresentOnDataNode(dataNode); SetLesionPresence(dataNode, lesionPresence); } else { m_LesionPresence.clear(); } } } +void QmitkPatientTableModel::SetHeaderModel() +{ + m_HeaderModel->clear(); + QStandardItem* rootItem = new QStandardItem("Timeline"); + QStandardItem* baseline = new QStandardItem("Baseline"); + QStandardItem* followup = new QStandardItem("Follow-up"); + QList standardItems; + + standardItems.push_back(baseline); + rootItem->appendColumn(standardItems); + standardItems.clear(); + + standardItems.push_back(followup); + rootItem->appendColumn(standardItems); + standardItems.clear(); + + for (const auto& controlPoint : m_ControlPoints) + { + std::string controlPointAsString = mitk::GetControlPointAsString(controlPoint); + QStandardItem* standardItem = new QStandardItem(QString::fromStdString(controlPointAsString)); + standardItems.push_back(standardItem); + if (controlPointAsString.find("to") != std::string::npos) + { + followup->appendColumn(standardItems); + } + else + { + baseline->appendColumn(standardItems); + } + standardItems.clear(); + } + + m_HeaderModel->setItem(0, 0, rootItem); +} + bool QmitkPatientTableModel::IsLesionPresentOnDataNode(const mitk::DataNode* dataNode) const { if (m_DataStorage.IsExpired()) { return false; } auto dataStorage = m_DataStorage.Lock(); try { mitk::SemanticRelations::DataNodeVector allSegmentationsOfLesion = m_SemanticRelations->GetAllSegmentationsOfLesion(m_CaseID, m_Lesion); for (const auto& segmentation : allSegmentationsOfLesion) { if ("Segmentation" == m_SelectedNodeType) { if (dataNode == segmentation) { // found a segmentation of the node that is represented by the selected lesion return true; } } else if ("Image" == m_SelectedNodeType) { // get parent node of the current segmentation node with the node predicate mitk::DataStorage::SetOfObjects::ConstPointer parentNodes = dataStorage->GetSources(segmentation, mitk::NodePredicates::GetImagePredicate(), false); for (auto it = parentNodes->Begin(); it != parentNodes->End(); ++it) { if (dataNode == it.Value()) { // found a segmentation of the node that is represented by the selected lesion return true; } } } } } catch (const mitk::SemanticRelationException&) { return false; } return false; } mitk::DataNode* QmitkPatientTableModel::GetCurrentDataNode(const QModelIndex& index) const { mitk::SemanticTypes::ControlPoint currentControlPoint = m_ControlPoints.at(index.column()); mitk::SemanticTypes::InformationType currentInformationType = m_InformationTypes.at(index.row()); try { std::vector filteredDataNodes; if ("Image" == m_SelectedNodeType) { filteredDataNodes = m_SemanticRelations->GetAllSpecificImages(m_CaseID, currentControlPoint, currentInformationType); } else if ("Segmentation" == m_SelectedNodeType) { filteredDataNodes = m_SemanticRelations->GetAllSpecificSegmentations(m_CaseID, currentControlPoint, currentInformationType); } if (filteredDataNodes.empty()) { return nullptr; } return filteredDataNodes.front(); } catch (const mitk::SemanticRelationException&) { return nullptr; } }