diff --git a/Modules/SemanticRelationsUI/files.cmake b/Modules/SemanticRelationsUI/files.cmake index 9d2e0591b6..3cc737670f 100644 --- a/Modules/SemanticRelationsUI/files.cmake +++ b/Modules/SemanticRelationsUI/files.cmake @@ -1,24 +1,28 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES + mitkLesionItemData.cpp mitkModuleActivator.cpp QmitkAbstractSemanticRelationsStorageModel.cpp QmitkControlPointDialog.cpp QmitkLesionTextDialog.cpp + QmitkLesionTreeItem.cpp + QmitkLesionTreeModel.cpp QmitkPatientTableInspector.cpp QmitkPatientTableModel.cpp QmitkSemanticRelationsUIHelper.cpp ) set(MOC_H_FILES include/QmitkAbstractSemanticRelationsStorageInspector.h include/QmitkAbstractSemanticRelationsStorageModel.h include/QmitkControlPointDialog.h include/QmitkLesionTextDialog.h + include/QmitkLesionTreeModel.h include/QmitkPatientTableInspector.h include/QmitkPatientTableModel.h ) set(UI_FILES src/QmitkPatientTableInspector.ui ) diff --git a/Modules/SemanticRelationsUI/include/QmitkAbstractSemanticRelationsStorageModel.h b/Modules/SemanticRelationsUI/include/QmitkAbstractSemanticRelationsStorageModel.h index abf0253036..a69a42bd56 100644 --- a/Modules/SemanticRelationsUI/include/QmitkAbstractSemanticRelationsStorageModel.h +++ b/Modules/SemanticRelationsUI/include/QmitkAbstractSemanticRelationsStorageModel.h @@ -1,104 +1,107 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QMITKABSTRACTSEMANTICRELATIONSSTORAGEMODEL_H #define QMITKABSTRACTSEMANTICRELATIONSSTORAGEMODEL_H +// mitk semantic relations UI +#include "MitkSemanticRelationsUIExports.h" + // semantic relations module #include #include #include // qt widgets module #include "QmitkAbstractDataStorageModel.h" /* * @brief The QmitkAbstractSemanticRelationsStorageModel is a subclass of 'QmitkAbstractDataStorageModel' and provides additional * functionality to set and store a semantic relations instance, the current case ID and the current lesion. */ -class QmitkAbstractSemanticRelationsStorageModel : public QmitkAbstractDataStorageModel, public mitk::ISemanticRelationsObserver +class MITKSEMANTICRELATIONSUI_EXPORT QmitkAbstractSemanticRelationsStorageModel : public QmitkAbstractDataStorageModel, public mitk::ISemanticRelationsObserver { Q_OBJECT public: QmitkAbstractSemanticRelationsStorageModel(QObject* parent = nullptr); virtual ~QmitkAbstractSemanticRelationsStorageModel(); /* * @brief Updates this model with the data from the semantic relations. * * Overridden from 'ISemanticRelationsObserver'. * In order for the Update-function to be called, this model has to be added as a observer of SemanticRelation * (e.g. m_SemanticRelations->AddObserver(m_SemanticRelationsStorageModel);) * * @par caseID The current case ID to identify the currently active patient / case. */ virtual void Update(const mitk::SemanticTypes::CaseID& caseID) override; std::shared_ptr GetSemanticRelations() const { return m_SemanticRelations; } /** * @brief Sets the current case ID which is needed to access the semantic relations storage. * * @param caseID A case ID as string */ void SetCaseID(const mitk::SemanticTypes::CaseID& caseID); const mitk::SemanticTypes::CaseID& GetCaseID() const { return m_CaseID; } /** * @brief Sets the current lesion which can be used to show on which images the lesion is visible. * * @param lesion The selected lesion */ void SetLesion(const mitk::SemanticTypes::Lesion& lesion); const mitk::SemanticTypes::Lesion& GetLesion() const { return m_Lesion; } /* * @brief Updates the semantic relations storage model with the current data from the semantic relations model, * if the case ID is equal to the currently selected case ID of the table model. */ void UpdateModelData(const mitk::SemanticTypes::CaseID& caseID); /* * @brief Updates the semantic relations storage model with the current data from the semantic relations model * and the current case ID. */ void UpdateModelData(); Q_SIGNALS: void ModelUpdated(); protected: /** * @brief Creates a new 'SemanticRelations' instance with the new data storage and updates the model data. * This functions is called inside the 'SetDataStorage'-function from the parent class. */ virtual void DataStorageChanged() override; /** * @brief This function is called if the model data is updated. It can be used by subclasses to define * the way the data of a specific model is generated. It typically consists of access to the * semantic relations storage to retrieve certain information. */ virtual void SetData() = 0; std::shared_ptr m_SemanticRelations; mitk::SemanticTypes::CaseID m_CaseID; mitk::SemanticTypes::Lesion m_Lesion; }; #endif // QMITKABSTRACTSEMANTICRELATIONSSTORAGEMODEL_H diff --git a/Modules/SemanticRelationsUI/include/QmitkLesionTreeItem.h b/Modules/SemanticRelationsUI/include/QmitkLesionTreeItem.h new file mode 100644 index 0000000000..88668f8d1c --- /dev/null +++ b/Modules/SemanticRelationsUI/include/QmitkLesionTreeItem.h @@ -0,0 +1,102 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef QMITKLESIONTREEITEM_H +#define QMITKLESIONTREEITEM_H + +// mitk semantic relations UI +#include "mitkLesionItemData.h" + +// mitk semantic relations +#include + +// c++ +#include +#include + +/* +* @brief +*/ +class QmitkLesionTreeItem : public std::enable_shared_from_this +{ +public: + + using ChildPointer = std::shared_ptr; + using ParentPointer = std::weak_ptr; + + QmitkLesionTreeItem(mitk::SemanticTypes::ID lesionUID); + ~QmitkLesionTreeItem(); + + /** + * @brief Return the child of this item at a specific position. + * + * @param row Determines the position of a child item to return. + */ + ChildPointer GetChildInRow(int row) const; + /** + * @brief Return the parent item. + */ + ParentPointer GetParent() const { return m_ParentItem; }; + + /** + * @brief Return the item data, which contains ... + * + * see mitk::LesionItemData + */ + mitk::LesionItemData& GetData() { return m_ItemData; }; + + mitk::SemanticTypes::ID GetLesionID() { return m_LesionID; }; + /** + * @brief + * + * + */ + int GetRow() const; + /** + * @brief + * + * + */ + size_t ChildCount() const; + /** + * @brief + * + * + */ + void AddChild(ChildPointer child); + /** + * @brief + * + * + */ + void RemoveChild(ChildPointer child); + /** + * @brief Set the item data of this item. + * + * @param value LesionItemData that provides information about this item. + */ + void SetData(const mitk::SemanticTypes::Lesion& value); + +private: + + ParentPointer m_ParentItem; + std::vector m_Children; + mitk::LesionItemData m_ItemData; + + mitk::SemanticTypes::ID m_LesionID; +}; + +#endif // QMITKLESIONTREEITEM_H diff --git a/Modules/SemanticRelationsUI/include/QmitkLesionTreeModel.h b/Modules/SemanticRelationsUI/include/QmitkLesionTreeModel.h new file mode 100644 index 0000000000..1597ba7d48 --- /dev/null +++ b/Modules/SemanticRelationsUI/include/QmitkLesionTreeModel.h @@ -0,0 +1,87 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef QMITKLESIONTREEMODEL_H +#define QMITKLESIONTREEMODEL_H + +// mitk semantic relations UI +#include "MitkSemanticRelationsUIExports.h" +#include "QmitkAbstractSemanticRelationsStorageModel.h" +#include "QmitkLesionTreeItem.h" + +// c++ +#include + +/* +* @brief +*/ +class MITKSEMANTICRELATIONSUI_EXPORT QmitkLesionTreeModel: public QmitkAbstractSemanticRelationsStorageModel +{ + Q_OBJECT + +public: + + /** + * @brief Initialize the root item of the model. The root item does not have a parent item. + */ + QmitkLesionTreeModel(QObject* parent = nullptr); + ~QmitkLesionTreeModel(); + + ////////////////////////////////////////////////////////////////////////// + // overridden virtual functions from QAbstractItemModel + ////////////////////////////////////////////////////////////////////////// + virtual QModelIndex index(int row, int column, const QModelIndex& itemIndex = QModelIndex()) const override; + virtual QModelIndex parent(const QModelIndex& itemIndex) const override; + + virtual int rowCount(const QModelIndex& itemIndex = QModelIndex()) const override; + virtual int columnCount(const QModelIndex& itemIndex = QModelIndex()) const override; + + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + //Qt::ItemFlags flags(const QModelIndex &index) const override; + + ////////////////////////////////////////////////////////////////////////// + // end override + ////////////////////////////////////////////////////////////////////////// + +protected: + + // the following functions have to be overridden but are not implemented in this model + virtual void NodePredicateChanged() override; + virtual void NodeAdded(const mitk::DataNode* node) override; + virtual void NodeChanged(const mitk::DataNode* node) override; + virtual void NodeRemoved(const mitk::DataNode* node) override; + /** + * @brief Overridden from 'QmitkAbstractSemanticRelationsStorageModel': This function retrieves all control points + * of the current case and stores them to define the header of the tree. + * Furthermore all lesions are retrieved and the lesion data is stored and show in the tree view. + */ + virtual void SetData() override; + +private: + + void SetLesionData(); + void AddLesion(mitk::SemanticTypes::Lesion lesion); + + QmitkLesionTreeItem* GetItemByIndex(const QModelIndex& index) const; + + std::shared_ptr m_RootItem; + std::vector m_ControlPoints; + +}; + +#endif // QMITKLESIONTREEMODEL_H diff --git a/Modules/SemanticRelationsUI/include/QmitkPatientTableInspector.h b/Modules/SemanticRelationsUI/include/QmitkPatientTableInspector.h index 54b62b526e..9d9cc16e0c 100644 --- a/Modules/SemanticRelationsUI/include/QmitkPatientTableInspector.h +++ b/Modules/SemanticRelationsUI/include/QmitkPatientTableInspector.h @@ -1,88 +1,88 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QMITKPATIENTTABLEINSPECTOR_H #define QMITKPATIENTTABLEINSPECTOR_H // semantic relations UI module #include "MitkSemanticRelationsUIExports.h" -#include -#include +#include "QmitkAbstractSemanticRelationsStorageInspector.h" +#include "QmitkPatientTableModel.h" #include "ui_QmitkPatientTableInspector.h" // qt widgets module -#include "QmitkEnums.h" +#include // qt #include /* * @brief The QmitkPatientTableInspector is a QmitkAbstractSemanticRelationsStorageInspector that shows the currently * available data of the semantic relations storage model in a control-point - information type matrix. * * The QmitkPatientTableInspector uses the QmitkSemanticRelationsStorageModel, a QmitkAbstractDataStorageModel that * presents the semantic relations data as a table, showing a QPixmap as thumbnail for the data nodes. */ class MITKSEMANTICRELATIONSUI_EXPORT QmitkPatientTableInspector : public QmitkAbstractSemanticRelationsStorageInspector { Q_OBJECT public: QmitkPatientTableInspector(QWidget* parent = nullptr); virtual QAbstractItemView* GetView() override; virtual const QAbstractItemView* GetView() const override; virtual void SetSelectionMode(SelectionMode mode) override; virtual SelectionMode GetSelectionMode() const override; virtual void SetCaseID(const mitk::SemanticTypes::CaseID& caseID) override; virtual void SetLesion(const mitk::SemanticTypes::Lesion& lesion) override; QItemSelectionModel* GetSelectionModel(); Q_SIGNALS: void DataNodeDoubleClicked(const mitk::DataNode*); void OnContextMenuRequested(const QPoint&); void OnNodeRemoved(const mitk::DataNode*); private Q_SLOTS: void OnModelUpdated(); void OnNodeButtonClicked(const QString&); void OnItemDoubleClicked(const QModelIndex&); protected: virtual void Initialize() override; private: void SetUpConnections(); virtual void keyPressEvent(QKeyEvent* e) override; Ui::QmitkPatientTableInspector m_Controls; QmitkPatientTableModel* m_StorageModel; mitk::DataNode* m_SelectedDataNode; }; #endif // QMITKPATIENTTABLEINSPECTOR_H diff --git a/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h b/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h index 57fa34fcd3..549c5ac870 100644 --- a/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h +++ b/Modules/SemanticRelationsUI/include/QmitkPatientTableModel.h @@ -1,121 +1,121 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QMITKPATIENTTABLEMODEL_H #define QMITKPATIENTTABLEMODEL_H +// semantic relations UI module +#include "QmitkAbstractSemanticRelationsStorageModel.h" + // semantic relations module #include -// semantic relations UI module -#include - // mitk core #include // qt #include /* * @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: /** * @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; }; #endif // QMITKPATIENTTABLEMODEL_H diff --git a/Modules/SemanticRelationsUI/include/mitkLesionItemData.h b/Modules/SemanticRelationsUI/include/mitkLesionItemData.h new file mode 100644 index 0000000000..c9eaf85e72 --- /dev/null +++ b/Modules/SemanticRelationsUI/include/mitkLesionItemData.h @@ -0,0 +1,54 @@ +/*=================================================================== + +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 MITKLESIONITEMDATA_H +#define MITKLESIONITEMDATA_H + +// mitk semantic relations module +#include + +// qt +#include + +namespace mitk +{ + /** + * @brief This class holds the data of each lesion in the lesion tree view. + * + */ + class LesionItemData + { + + public: + /** + * @brief sets the data members to their initial values + */ + LesionItemData(); + ~LesionItemData(); + + std::string GetLesionName() const { return m_Lesion.name; } + void SetLesion(const mitk::SemanticTypes::Lesion& lesion); + + private: + + mitk::SemanticTypes::Lesion m_Lesion; + + }; +} // end namespace + +Q_DECLARE_METATYPE(mitk::LesionItemData) + +#endif // MITKLESIONITEMDATA_H \ No newline at end of file diff --git a/Modules/SemanticRelationsUI/src/QmitkLesionTreeItem.cpp b/Modules/SemanticRelationsUI/src/QmitkLesionTreeItem.cpp new file mode 100644 index 0000000000..bf020f3730 --- /dev/null +++ b/Modules/SemanticRelationsUI/src/QmitkLesionTreeItem.cpp @@ -0,0 +1,84 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +// semantic relations UI module +#include "QmitkLesionTreeItem.h" + +// qt +#include + +QmitkLesionTreeItem::QmitkLesionTreeItem(mitk::SemanticTypes::ID lesionUID) +{ + m_LesionID = lesionUID; +} + +QmitkLesionTreeItem::~QmitkLesionTreeItem() +{ + // nothing here +} + +QmitkLesionTreeItem::ChildPointer QmitkLesionTreeItem::GetChildInRow(int row) const +{ + return m_Children.at(row); +} + +int QmitkLesionTreeItem::GetRow() const +{ + if (m_ParentItem.expired()) + { + return 0; + } + + auto it = std::find(m_Children.begin(), m_Children.end(), this->shared_from_this()); + if (it == m_Children.end()) + { + return -1; + } + else + { + return std::distance(m_Children.begin(), it); + } +} + +size_t QmitkLesionTreeItem::ChildCount() const +{ + return m_Children.size(); +} + +void QmitkLesionTreeItem::AddChild(ChildPointer child) +{ + auto it = std::find(m_Children.begin(), m_Children.end(), child); + if (it == m_Children.end()) + { + // child does not already exist; add to vector of children + m_Children.push_back(child); + m_ParentItem = this->shared_from_this(); + } +} + +void QmitkLesionTreeItem::RemoveChild(ChildPointer child) +{ + auto it = std::find(m_Children.begin(), m_Children.end(), child); + if (it != m_Children.end()) + { + m_Children.erase(it); + } +} + +void QmitkLesionTreeItem::SetData(const mitk::SemanticTypes::Lesion& value) +{ + m_ItemData.SetLesion(value); +} diff --git a/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp b/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp new file mode 100644 index 0000000000..2ff9ba6b5d --- /dev/null +++ b/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp @@ -0,0 +1,209 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +// semantic relations UI module +#include "QmitkLesionTreeModel.h" + +// semantic relations module +#include + +QmitkLesionTreeModel::QmitkLesionTreeModel(QObject* parent/* = nullptr*/) + : QmitkAbstractSemanticRelationsStorageModel(parent) + , m_RootItem(std::make_shared("RootItem")) +{ + // nothing here +} + +QmitkLesionTreeModel::~QmitkLesionTreeModel() +{ + // nothing here +} + +////////////////////////////////////////////////////////////////////////// +// overridden virtual functions from QAbstractItemModel +////////////////////////////////////////////////////////////////////////// +QModelIndex QmitkLesionTreeModel::index(int row, int column, const QModelIndex& itemIndex) const +{ + if (!hasIndex(row, column, itemIndex)) + { + return QModelIndex(); + } + + auto childItem = GetItemByIndex(itemIndex)->GetChildInRow(row); + if (nullptr == childItem) + { + return QModelIndex(); + } + + return createIndex(row, column, childItem.get()); +} + +QModelIndex QmitkLesionTreeModel::parent(const QModelIndex& itemIndex) const +{ + if (!itemIndex.isValid()) + { + return QModelIndex(); + } + + auto parentItem = GetItemByIndex(itemIndex)->GetParent(); + + if (parentItem.expired()) + { + return QModelIndex(); + } + + /* + if (sharedParentPtr == m_RootItem) + { + return QModelIndex(); + } + */ + auto sharedParentItem = parentItem.lock(); + return createIndex(sharedParentItem->GetRow(), 0, sharedParentItem.get()); +} + +int QmitkLesionTreeModel::rowCount(const QModelIndex& itemIndex/* = QModelIndex()*/) const +{ + return GetItemByIndex(itemIndex)->ChildCount(); +} + +int QmitkLesionTreeModel::columnCount(const QModelIndex&/* itemIndex = QModelIndex() */) const +{ + return m_ControlPoints.size() + 1; +} + +QVariant QmitkLesionTreeModel::data(const QModelIndex& index, int role) const +{ + if (!index.isValid()) + { + return QVariant(); + } + + if (index.column() < 0 || index.column() > static_cast(m_ControlPoints.size())) + { + return QVariant(); + } + + QmitkLesionTreeItem* currentItem = GetItemByIndex(index); + if (Qt::DisplayRole == role) + { + // display role fills the columns with certain strings or numbers + if (0 == index.column()) + { + return QVariant::fromValue(QString::fromStdString(currentItem->GetLesionID())); + //return QVariant::fromValue(QString::fromStdString(currentItem->GetData().GetLesionName())); + } + else if (1 == index.column()) + { + //return currentItem->GetData().GetStoredInDatabase(); + } + else if (2 == index.column()) + { + //return currentItem->GetData().GetNumberOfLoadedDataNodes(); + } + + return ""; + } + + return QVariant(); +} + +QVariant QmitkLesionTreeModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (Qt::Horizontal == orientation && Qt::DisplayRole == role) + { + if (0 == section) + { + return QVariant("Lesion"); + } + + if (m_ControlPoints.size() >= section) + { + mitk::SemanticTypes::ControlPoint currentControlPoint = m_ControlPoints.at(section-1); + // generate a string from the control point + std::string currentControlPointAsString = mitk::GetControlPointAsString(currentControlPoint); + return QVariant(QString::fromStdString(currentControlPointAsString)); + } + } + + return QVariant(); +} + +void QmitkLesionTreeModel::NodePredicateChanged() +{ + // does not react to node predicate changes +} + +void QmitkLesionTreeModel::NodeAdded(const mitk::DataNode* node) +{ + // does not react to data storage changes +} + +void QmitkLesionTreeModel::NodeChanged(const mitk::DataNode* node) +{ + // does not react to data storage changes +} + +void QmitkLesionTreeModel::NodeRemoved(const mitk::DataNode* node) +{ + // does not react to data storage changes +} + +void QmitkLesionTreeModel::SetData() +{ + m_RootItem = std::make_shared("RootItem"); + + // get all control points of current case + m_ControlPoints = m_SemanticRelations->GetAllControlPointsOfCase(m_CaseID); + // sort the vector of control points for the timeline + std::sort(m_ControlPoints.begin(), m_ControlPoints.end()); + + SetLesionData(); +} + +void QmitkLesionTreeModel::SetLesionData() +{ + std::vector allLesions = m_SemanticRelations->GetAllLesionsOfCase(m_CaseID); + + for (const auto& lesion : allLesions) + { + AddLesion(lesion); + } +} + +void QmitkLesionTreeModel::AddLesion(mitk::SemanticTypes::Lesion lesion) +{ + if (nullptr == m_SemanticRelations) + { + return; + } + + m_RootItem->AddChild(std::make_shared(lesion.UID)); +} + +QmitkLesionTreeItem* QmitkLesionTreeModel::GetItemByIndex(const QModelIndex& index) const +{ + if (index.isValid()) + { + auto item = static_cast(index.internalPointer()); + if (nullptr != item) + { + return item; + } + } + + return m_RootItem.get(); +} diff --git a/Modules/SemanticRelationsUI/src/mitkLesionItemData.cpp b/Modules/SemanticRelationsUI/src/mitkLesionItemData.cpp new file mode 100644 index 0000000000..6c30621cba --- /dev/null +++ b/Modules/SemanticRelationsUI/src/mitkLesionItemData.cpp @@ -0,0 +1,34 @@ +/*=================================================================== + +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 +#include "mitkLesionItemData.h" + +mitk::LesionItemData::LesionItemData() +{ + // nothing here +} + + +mitk::LesionItemData::~LesionItemData() +{ + // nothing here +} + +void mitk::LesionItemData::SetLesion(const mitk::SemanticTypes::Lesion& lesion) +{ + m_Lesion = lesion; +} diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp index a4e08f20f1..0fb8e25b6d 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp @@ -1,478 +1,436 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // semantic relations plugin #include "QmitkLesionInfoWidget.h" #include "QmitkSemanticRelationsNodeSelectionDialog.h" // semantic relations UI module #include // semantic relations module #include #include #include #include #include "QmitkCustomVariants.h" // mitk core #include // qt #include #include #include #include #include const QBrush QmitkLesionInfoWidget::DEFAULT_BACKGROUND_COLOR = QBrush(Qt::transparent); const QBrush QmitkLesionInfoWidget::SELECTED_BACKGROUND_COLOR = QBrush(Qt::green); const QBrush QmitkLesionInfoWidget::CONNECTED_BACKGROUND_COLOR = QBrush(Qt::darkGreen); QmitkLesionInfoWidget::QmitkLesionInfoWidget(mitk::DataStorage* dataStorage, QWidget* parent /*= nullptr*/) : QWidget(parent) , m_DataStorage(dataStorage) , m_SemanticRelations(std::make_unique(dataStorage)) { - Init(); + Initialize(); } QmitkLesionInfoWidget::~QmitkLesionInfoWidget() { if (nullptr != m_SemanticRelations) { m_SemanticRelations->RemoveObserver(this); } } -void QmitkLesionInfoWidget::Init() +void QmitkLesionInfoWidget::Initialize() { - // create GUI from the Qt Designer's .ui file m_Controls.setupUi(this); - m_Controls.lesionListWidget->setContextMenuPolicy(Qt::CustomContextMenu); + m_Controls.lesionTreeView->setAlternatingRowColors(true); + m_Controls.lesionTreeView->setContextMenuPolicy(Qt::CustomContextMenu); + + m_StorageModel = new QmitkLesionTreeModel(this); + if (m_DataStorage.IsExpired()) + { + return; + } + + auto dataStorage = m_DataStorage.Lock(); + m_StorageModel->SetDataStorage(dataStorage); + m_Controls.lesionTreeView->setModel(m_StorageModel); SetUpConnections(); m_SemanticRelations->AddObserver(this); } void QmitkLesionInfoWidget::SetUpConnections() { // connect buttons to modify semantic relations connect(m_Controls.addLesionPushButton, &QPushButton::clicked, this, &QmitkLesionInfoWidget::OnAddLesionButtonClicked); // connect each list widget with a custom slots - connect(m_Controls.lesionListWidget, &QListWidget::currentItemChanged, this, &QmitkLesionInfoWidget::OnCurrentLesionItemChanged); - connect(m_Controls.lesionListWidget, &QListWidget::itemDoubleClicked, this, &QmitkLesionInfoWidget::OnLesionItemDoubleClicked); + //connect(m_Controls.lesionTreeView, &QTreeView::selectionChanged, this, &QmitkLesionInfoWidget::OnSelectionChanged); + //connect(m_Controls.lesionTreeView, &QListWidget::itemDoubleClicked, this, &QmitkLesionInfoWidget::OnLesionItemDoubleClicked); // connect context menu entries - connect(m_Controls.lesionListWidget, &QListWidget::customContextMenuRequested, this, &QmitkLesionInfoWidget::OnLesionListContextMenuRequested); + connect(m_Controls.lesionTreeView, &QTreeView::customContextMenuRequested, this, &QmitkLesionInfoWidget::OnLesionListContextMenuRequested); } -void QmitkLesionInfoWidget::SetCurrentCaseID(const mitk::SemanticTypes::CaseID& caseID) +void QmitkLesionInfoWidget::SetCaseID(const mitk::SemanticTypes::CaseID& caseID) { m_CaseID = caseID; Update(m_CaseID); } void QmitkLesionInfoWidget::Update(const mitk::SemanticTypes::CaseID& caseID) { if (nullptr == m_SemanticRelations) { return; } // if the case ID of updated instance is equal to the currently active caseID if (caseID == m_CaseID) { - ResetLesionListWidget(); + m_StorageModel->SetCaseID(caseID); } } ////////////////////////////////////////////////////////////////////////// // Implementation of the QT_SLOTS ////////////////////////////////////////////////////////////////////////// void QmitkLesionInfoWidget::OnAddLesionButtonClicked() { if (m_CaseID.empty()) { QMessageBox msgBox; msgBox.setWindowTitle("No case ID set."); msgBox.setText("In order to add a lesion, please specify the current case / patient."); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } mitk::SemanticTypes::Lesion newLesion = mitk::GenerateNewLesion(); try { m_SemanticRelations->AddLesion(m_CaseID, newLesion); } catch (mitk::SemanticRelationException& e) { MITK_INFO << "Could not add a new lesion. " << e; } } -void QmitkLesionInfoWidget::OnCurrentLesionItemChanged(QListWidgetItem* currentItem, QListWidgetItem* /*previousItem*/) +void QmitkLesionInfoWidget::OnSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { + /* if (nullptr == currentItem || nullptr == m_SemanticRelations) { return; } // only the UID is needed to identify a representing lesion m_CurrentLesion.UID = currentItem->data(Qt::UserRole).toString().toStdString(); if (false == m_SemanticRelations->InstanceExists(m_CaseID, m_CurrentLesion)) { // no UID found; cannot create a lesion return; } if (SELECTED_BACKGROUND_COLOR == currentItem->background() || CONNECTED_BACKGROUND_COLOR == currentItem->background()) { DarkenBackgroundColors(); } else { ResetBackgroundColors(); } currentItem->setBackground(SELECTED_BACKGROUND_COLOR); emit LesionChanged(m_CurrentLesion); + */ } +/* void QmitkLesionInfoWidget::OnLesionItemDoubleClicked(QListWidgetItem* clickedItem) { if (nullptr == clickedItem || nullptr == m_SemanticRelations) { return; } // only the UID is needed to identify a representing lesion m_CurrentLesion.UID = clickedItem->data(Qt::UserRole).toString().toStdString(); m_CurrentLesion.name = clickedItem->text().toStdString(); } +*/ void QmitkLesionInfoWidget::OnLesionListContextMenuRequested(const QPoint& pos) { - QListWidgetItem* currentItem = m_Controls.lesionListWidget->itemAt(pos); - if (nullptr == currentItem) + QModelIndex index = m_Controls.lesionTreeView->indexAt(pos); + if (!index.isValid()) { // no item clicked; cannot retrieve the current lesion return; } - mitk::SemanticTypes::ID selectedLesionUID; - selectedLesionUID = currentItem->data(Qt::UserRole).toString().toStdString(); + mitk::SemanticTypes::ID selectedLesionUID = m_StorageModel->data(m_StorageModel->index(index.row(), 0), Qt::DisplayRole).toString().toStdString(); if (selectedLesionUID.empty()) { // no UID found; cannot create a lesion return; } - QMenu* menu = new QMenu(m_Controls.lesionListWidget); + QMenu* menu = new QMenu(m_Controls.lesionTreeView); QAction* linkToSegmentation = new QAction("Link to segmentation", this); linkToSegmentation->setEnabled(true); connect(linkToSegmentation, &QAction::triggered, [this, selectedLesionUID] { OnLinkToSegmentation(selectedLesionUID); }); menu->addAction(linkToSegmentation); QAction* setLesionName = new QAction("Set lesion name", this); setLesionName->setEnabled(true); connect(setLesionName, &QAction::triggered, [this, selectedLesionUID] { OnSetLesionName(selectedLesionUID); }); menu->addAction(setLesionName); QAction* setLesionClass = new QAction("Set lesion class", this); setLesionClass->setEnabled(true); connect(setLesionClass, &QAction::triggered, [this, selectedLesionUID] { OnSetLesionClass(selectedLesionUID); }); menu->addAction(setLesionClass); QAction* removeLesion = new QAction("Remove lesion", this); removeLesion->setEnabled(true); connect(removeLesion, &QAction::triggered, [this, selectedLesionUID] { OnRemoveLesion(selectedLesionUID); }); menu->addAction(removeLesion); menu->popup(QCursor::pos()); } void QmitkLesionInfoWidget::OnLinkToSegmentation(const mitk::SemanticTypes::ID& selectedLesionUID) { if (m_CaseID.empty()) { QMessageBox msgBox; msgBox.setWindowTitle("No case ID set."); msgBox.setText("In order to link a lesion to a segmentation, please specify the current case / patient."); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } // retrieve the full lesion with its lesion class using the given lesion UID mitk::SemanticTypes::Lesion selectedLesion = mitk::GetLesionByUID(selectedLesionUID, m_SemanticRelations->GetAllLesionsOfCase(m_CaseID)); if (selectedLesion.UID.empty()) { // could not find lesion information for the selected lesion return; } if (m_DataStorage.IsExpired()) { return; } auto dataStorage = m_DataStorage.Lock(); QmitkSemanticRelationsNodeSelectionDialog* dialog = new QmitkSemanticRelationsNodeSelectionDialog(this, "Select segmentation to link to the selected lesion.", ""); dialog->SetDataStorage(dataStorage); dialog->setWindowTitle("Select segmentation node"); dialog->SetNodePredicate(mitk::NodePredicates::GetSegmentationPredicate()); dialog->SetSelectOnlyVisibleNodes(true); dialog->SetSelectionMode(QAbstractItemView::SingleSelection); dialog->SetCaseID(m_CaseID); int dialogReturnValue = dialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } auto nodes = dialog->GetSelectedNodes(); mitk::DataNode::Pointer selectedDataNode = nullptr; if (!nodes.isEmpty()) { // only single selection allowed selectedDataNode = nodes.front(); } if (nullptr == selectedDataNode) { QMessageBox msgBox; msgBox.setWindowTitle("No valid segmentation node selected."); msgBox.setText("In order to link the selected lesion to a segmentation, please specify a valid segmentation node."); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } if (false == mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(selectedDataNode)) { QMessageBox msgBox; msgBox.setWindowTitle("No segmentation selected"); msgBox.setText("In order to link the selected lesion to a segmentation, please specify a valid segmentation node."); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } mitk::BaseData* baseData = selectedDataNode->GetData(); if (nullptr == baseData) { QMessageBox msgBox; msgBox.setWindowTitle("No valid base data."); msgBox.setText("In order to link the selected lesion to a segmentation, please specify a valid segmentation node."); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } try { m_SemanticRelations->LinkSegmentationToLesion(selectedDataNode, selectedLesion); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox; msgBox.setWindowTitle("Could not link the selected lesion."); msgBox.setText("The program wasn't able to correctly link the selected lesion with the selected segmentation.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); } } void QmitkLesionInfoWidget::OnSetLesionName(const mitk::SemanticTypes::ID& selectedLesionUID) { // retrieve the full lesion with its lesion class using the given lesion UID mitk::SemanticTypes::Lesion selectedLesion = mitk::GetLesionByUID(selectedLesionUID, m_SemanticRelations->GetAllLesionsOfCase(m_CaseID)); if (selectedLesion.UID.empty()) { // could not find lesion information for the selected lesion return; } // use the lesion information to set the input text for the dialog QmitkLesionTextDialog* inputDialog = new QmitkLesionTextDialog(this); inputDialog->setWindowTitle("Set lesion name"); inputDialog->SetLineEditText(selectedLesion.name); int dialogReturnValue = inputDialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } std::string newLesionName = inputDialog->GetLineEditText().toStdString(); selectedLesion.name = newLesionName; m_SemanticRelations->OverwriteLesion(m_CaseID, selectedLesion); } void QmitkLesionInfoWidget::OnSetLesionClass(const mitk::SemanticTypes::ID& selectedLesionUID) { // retrieve the full lesion with its lesion class using the given lesion UID mitk::SemanticTypes::Lesion selectedLesion = mitk::GetLesionByUID(selectedLesionUID, m_SemanticRelations->GetAllLesionsOfCase(m_CaseID)); if (selectedLesion.UID.empty()) { // could not find lesion information for the selected lesion return; } // use the lesion information to set the input text for the dialog QmitkLesionTextDialog* inputDialog = new QmitkLesionTextDialog(this); inputDialog->setWindowTitle("Set lesion class"); inputDialog->SetLineEditText(selectedLesion.lesionClass.classType); // prepare the completer for the dialogs input text field mitk::LesionClassVector allLesionClasses = m_SemanticRelations->GetAllLesionClassesOfCase(m_CaseID); QStringList wordList; for (const auto& lesionClass : allLesionClasses) { wordList << QString::fromStdString(lesionClass.classType); } QCompleter* completer = new QCompleter(wordList, this); completer->setCaseSensitivity(Qt::CaseInsensitive); inputDialog->GetLineEdit()->setCompleter(completer); int dialogReturnValue = inputDialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } // retrieve the new input lesion class type and check for an already existing lesion class types std::string newLesionClassType = inputDialog->GetLineEditText().toStdString(); mitk::SemanticTypes::LesionClass existingLesionClass = mitk::FindExistingLesionClass(newLesionClassType, allLesionClasses); if (existingLesionClass.UID.empty()) { // could not find lesion class information for the new lesion class type // create a new lesion class for the selected lesion existingLesionClass = mitk::GenerateNewLesionClass(newLesionClassType); } selectedLesion.lesionClass = existingLesionClass; m_SemanticRelations->OverwriteLesion(m_CaseID, selectedLesion); } void QmitkLesionInfoWidget::OnRemoveLesion(const mitk::SemanticTypes::ID& selectedLesionUID) { if (m_CaseID.empty()) { QMessageBox msgBox; msgBox.setWindowTitle("No case ID set."); msgBox.setText("In order to remove a lesion, please specify the current case / patient."); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } // retrieve the full lesion with its lesion class using the given lesion UID mitk::SemanticTypes::Lesion selectedLesion = mitk::GetLesionByUID(selectedLesionUID, m_SemanticRelations->GetAllLesionsOfCase(m_CaseID)); if (selectedLesion.UID.empty()) { // could not find lesion information for the selected lesion return; } try { m_SemanticRelations->RemoveLesion(m_CaseID, selectedLesion); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox; msgBox.setWindowTitle("Could not remove the selected lesion."); msgBox.setText("The program wasn't able to correctly remove the selected lesion from the semantic relations model.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); } } - -void QmitkLesionInfoWidget::ResetLesionListWidget() -{ - m_Controls.lesionListWidget->clear(); - m_CurrentLesion.UID = ""; - m_CurrentLesion.name = ""; - - // create lesion list widget entries with the current lesions - mitk::SemanticRelations::LesionVector allLesionsOfCase = m_SemanticRelations->GetAllLesionsOfCase(m_CaseID); - if (allLesionsOfCase.empty()) - { - m_Controls.lesionListWidget->addItem("No lesions found"); - } - for (const auto& lesion : allLesionsOfCase) - { - // store the UID as 'UserRole' data in the widget item - QListWidgetItem* lesionItem = new QListWidgetItem; - lesionItem->setData(Qt::UserRole, QString::fromStdString(lesion.UID)); - - // use the lesion UID for the item text, if the lesion name is non-existent - if (lesion.name.empty()) - { - lesionItem->setText(QString::fromStdString(lesion.UID)); - } - else - { - lesionItem->setText(QString::fromStdString(lesion.name)); - } - - m_Controls.lesionListWidget->addItem(lesionItem); - } -} - -void QmitkLesionInfoWidget::ResetBackgroundColors() -{ - // reset all lesion list widget items to original background color - for (int i = 0; i < m_Controls.lesionListWidget->count(); ++i) - { - QListWidgetItem* item = m_Controls.lesionListWidget->item(i); - item->setBackground(DEFAULT_BACKGROUND_COLOR); - } -} - -void QmitkLesionInfoWidget::DarkenBackgroundColors() -{ - // reset all lesion list widget items to original background color - for (int i = 0; i < m_Controls.lesionListWidget->count(); ++i) - { - QListWidgetItem* item = m_Controls.lesionListWidget->item(i); - if (DEFAULT_BACKGROUND_COLOR != item->background()) - { - item->setBackground(CONNECTED_BACKGROUND_COLOR); - } - } -} diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.h index 7ede7961e7..b3ff3f79ac 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.h @@ -1,119 +1,113 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QMITKLESIONINFOWIDGET_H #define QMITKLESIONINFOWIDGET_H -// semantic relations UI module +// semantic relations plugin #include +// semantic relations UI module +#include + // semantic relations module #include #include // mitk #include // qt #include /* * @brief The QmitkLesionInfoWidget is a widget that shows and modifies the currently available lesion data of the semantic relations model. * * The widget provides a dialogs to add nodes from the data storage to the semantic relations model. * It provides functionality to create new lesions and link them with segmentation nodes. * * The QmitkLesionInfoWidget provides three QListWidgets, that show the lesion data and the referenced segmentation data, as * well as the connected image data, depending on the selected lesion. * * The QmitkLesionInfoWidget implements the 'ISemanticRelationsObserver', so that it is automatically updated, if the * semantic relations model changes. Updating means freshly getting all lesion data and filling the lesion-ListWidget with the lesion data. */ class QmitkLesionInfoWidget : public QWidget, public mitk::ISemanticRelationsObserver { Q_OBJECT public: static const QBrush DEFAULT_BACKGROUND_COLOR; static const QBrush SELECTED_BACKGROUND_COLOR; static const QBrush CONNECTED_BACKGROUND_COLOR; QmitkLesionInfoWidget::QmitkLesionInfoWidget(mitk::DataStorage* dataStorage, QWidget* parent = nullptr); ~QmitkLesionInfoWidget(); - void SetCurrentCaseID(const mitk::SemanticTypes::CaseID& caseID); + void SetCaseID(const mitk::SemanticTypes::CaseID& caseID); /* * @brief Updates the 'lesionListWidget' of the GUI with the current lesion-data from the semantic relations model. * * Overridden from 'ISemanticRelationsObserver'. * In order for the Update-function to be called, this widget has to be added as a observer of SemanticRelation * (e.g. m_SemanticRelations->AddObserver(m_LesionInfoWidget);) * * @par caseID The current case ID to identify the currently active patient / case. */ virtual void Update(const mitk::SemanticTypes::CaseID& caseID) override; const mitk::SemanticTypes::Lesion& GetSelectedLesion() const { return m_CurrentLesion; } - /* - * @brief Resets all items from the lesion list widget. - */ - void ResetLesionListWidget(); - /* - * @brief Resets the background color of all items in each list widget. - */ - void ResetBackgroundColors(); - - void DarkenBackgroundColors(); - Q_SIGNALS: void LesionChanged(const mitk::SemanticTypes::Lesion&); private Q_SLOTS: /* * @brief Generates a new, empty lesion to add to the semantic relations model for the current case ID. */ void OnAddLesionButtonClicked(); // slots for the mouse click events of the list widgets - void OnCurrentLesionItemChanged(QListWidgetItem*, QListWidgetItem*); - void OnLesionItemDoubleClicked(QListWidgetItem*); + void OnSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); + //void OnLesionItemDoubleClicked(QListWidgetItem*); void OnLesionListContextMenuRequested(const QPoint&); // slots for the context menu actions of the lesion list widget void OnLinkToSegmentation(const mitk::SemanticTypes::ID&); void OnSetLesionName(const mitk::SemanticTypes::ID&); void OnSetLesionClass(const mitk::SemanticTypes::ID&); void OnRemoveLesion(const mitk::SemanticTypes::ID&); private: - void Init(); + void Initialize(); void SetUpConnections(); Ui::QmitkLesionInfoWidgetControls m_Controls; + QmitkLesionTreeModel* m_StorageModel; + mitk::WeakPointer m_DataStorage; std::unique_ptr m_SemanticRelations; mitk::SemanticTypes::CaseID m_CaseID; mitk::SemanticTypes::Lesion m_CurrentLesion; }; #endif // QMITKLESIONINFOWIDGET_H diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidgetControls.ui b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidgetControls.ui index db08af5e22..35ace22701 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidgetControls.ui +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidgetControls.ui @@ -1,50 +1,50 @@ QmitkLesionInfoWidgetControls true 0 0 350 300 0 0 0 0 + + + + Available lesions: + + + - + Add new lesion - - - - Available lesions: - - - diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.cpp index f1d314d19f..9d480fbdc3 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.cpp @@ -1,264 +1,265 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // semantic relations plugin #include "QmitkSemanticRelationsView.h" #include "QmitkSemanticRelationsNodeSelectionDialog.h" #include "QmitkDataNodeAddToSemanticRelationsAction.h" #include "QmitkDataNodeRemoveFromSemanticRelationsAction.h" // semantic relations module #include #include // mitk qt widgets module #include #include // mitk multi label module #include // berry #include #include // qt #include +#include const std::string QmitkSemanticRelationsView::VIEW_ID = "org.mitk.views.semanticrelations"; void QmitkSemanticRelationsView::SetFocus() { // nothing here } void QmitkSemanticRelationsView::CreateQtPartControl(QWidget* parent) { // create GUI widgets m_Controls.setupUi(parent); // initialize the semantic relations m_SemanticRelations = std::make_unique(GetDataStorage()); m_LesionInfoWidget = new QmitkLesionInfoWidget(GetDataStorage(), parent); m_Controls.gridLayout->addWidget(m_LesionInfoWidget); m_PatientTableInspector = new QmitkPatientTableInspector(parent); m_PatientTableInspector->SetDataStorage(GetDataStorage()); m_Controls.gridLayout->addWidget(m_PatientTableInspector); QGridLayout* dndDataNodeWidgetLayout = new QGridLayout; dndDataNodeWidgetLayout->addWidget(m_PatientTableInspector, 0, 0); dndDataNodeWidgetLayout->setContentsMargins(0, 0, 0, 0); m_DnDDataNodeWidget = new QmitkDnDDataNodeWidget(parent); m_DnDDataNodeWidget->setLayout(dndDataNodeWidgetLayout); m_Controls.gridLayout->addWidget(m_DnDDataNodeWidget); m_ContextMenu = new QmitkSemanticRelationsContextMenu(GetSite(), m_PatientTableInspector); m_ContextMenu->SetDataStorage(GetDataStorage()); mitk::IRenderWindowPart* renderWindowPart = GetRenderWindowPart(); if (nullptr != renderWindowPart) { RenderWindowPartActivated(renderWindowPart); } SetUpConnections(); } void QmitkSemanticRelationsView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { // connect QmitkRenderWindows - underlying vtkRenderWindow is the same as "mitk::RenderingManager::GetInstance()->GetAllRegisteredRenderWindows()" QHash windowMap = renderWindowPart->GetQmitkRenderWindows(); QHash::Iterator it; mitk::BaseRenderer* baseRenderer = nullptr; RenderWindowLayerUtilities::RendererVector controlledRenderer; for (it = windowMap.begin(); it != windowMap.end(); ++it) { baseRenderer = mitk::BaseRenderer::GetInstance(it.value()->GetVtkRenderWindow()); if (nullptr != baseRenderer) { controlledRenderer.push_back(baseRenderer); } } m_ContextMenu->SetControlledRenderer(controlledRenderer); } void QmitkSemanticRelationsView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart) { // nothing here } void QmitkSemanticRelationsView::SetUpConnections() { connect(m_Controls.caseIDComboBox, static_cast(&QComboBox::currentIndexChanged), this, &QmitkSemanticRelationsView::OnCaseIDSelectionChanged); connect(m_LesionInfoWidget, &QmitkLesionInfoWidget::LesionChanged, this, &QmitkSemanticRelationsView::OnLesionChanged); connect(m_PatientTableInspector, &QmitkPatientTableInspector::DataNodeDoubleClicked, this, &QmitkSemanticRelationsView::OnDataNodeDoubleClicked); connect(m_DnDDataNodeWidget, &QmitkDnDDataNodeWidget::NodesDropped, this, &QmitkSemanticRelationsView::OnNodesAdded); connect(m_PatientTableInspector, &QmitkPatientTableInspector::OnContextMenuRequested, m_ContextMenu, &QmitkSemanticRelationsContextMenu::OnContextMenuRequested); connect(m_PatientTableInspector, &QmitkPatientTableInspector::OnNodeRemoved, this, &QmitkSemanticRelationsView::NodeRemoved); } QItemSelectionModel* QmitkSemanticRelationsView::GetDataNodeSelectionModel() const { return m_PatientTableInspector->GetSelectionModel(); } void QmitkSemanticRelationsView::NodeRemoved(const mitk::DataNode* dataNode) { if (nullptr == dataNode) { return; } if (m_SemanticRelations->InstanceExists(dataNode)) { RemoveFromSemanticRelationsAction::Run(m_SemanticRelations.get(), GetDataStorage(), dataNode); mitk::SemanticTypes::CaseID caseID = mitk::GetCaseIDFromDataNode(dataNode); RemoveFromComboBox(caseID); } } void QmitkSemanticRelationsView::OnLesionChanged(const mitk::SemanticTypes::Lesion& lesion) { m_PatientTableInspector->SetLesion(lesion); } void QmitkSemanticRelationsView::OnDataNodeDoubleClicked(const mitk::DataNode* dataNode) { if (nullptr == dataNode) { return; } if (mitk::NodePredicates::GetImagePredicate()->CheckNode(dataNode)) { OpenInEditor(dataNode); } else if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { JumpToPosition(dataNode); } } void QmitkSemanticRelationsView::OnCaseIDSelectionChanged(const QString& caseID) { - m_LesionInfoWidget->SetCurrentCaseID(caseID.toStdString()); + m_LesionInfoWidget->SetCaseID(caseID.toStdString()); m_PatientTableInspector->SetCaseID(caseID.toStdString()); } void QmitkSemanticRelationsView::OnNodesAdded(QmitkDnDDataNodeWidget* dnDDataNodeWidget, std::vector nodes) { mitk::SemanticTypes::CaseID caseID = ""; for (mitk::DataNode* dataNode : nodes) { AdddToSemanticRelationsAction::Run(m_SemanticRelations.get(), GetDataStorage(), dataNode); caseID = mitk::GetCaseIDFromDataNode(dataNode); AddToComboBox(caseID); } } void QmitkSemanticRelationsView::OnNodeRemoved(const mitk::DataNode* dataNode) { NodeRemoved(dataNode); } void QmitkSemanticRelationsView::AddToComboBox(const mitk::SemanticTypes::CaseID& caseID) { int foundIndex = m_Controls.caseIDComboBox->findText(QString::fromStdString(caseID)); if (-1 == foundIndex) { // add the caseID to the combo box, as it is not already contained m_Controls.caseIDComboBox->addItem(QString::fromStdString(caseID)); } } void QmitkSemanticRelationsView::RemoveFromComboBox(const mitk::SemanticTypes::CaseID& caseID) { std::vector allControlPoints = m_SemanticRelations->GetAllControlPointsOfCase(caseID); int foundIndex = m_Controls.caseIDComboBox->findText(QString::fromStdString(caseID)); if (allControlPoints.empty() && -1 != foundIndex) { // TODO: find new way to check for empty case id // caseID does not contain any control points and therefore no data // remove the caseID, if it is still contained m_Controls.caseIDComboBox->removeItem(foundIndex); } } void QmitkSemanticRelationsView::OpenInEditor(const mitk::DataNode* dataNode) { auto renderWindowPart = GetRenderWindowPart(); if (nullptr == renderWindowPart) { renderWindowPart = GetRenderWindowPart(QmitkAbstractView::BRING_TO_FRONT | QmitkAbstractView::OPEN); if (nullptr == renderWindowPart) { // no render window available return; } } auto image = dynamic_cast(dataNode->GetData()); if (nullptr != image) { mitk::RenderingManager::GetInstance()->InitializeViews(image->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); } } void QmitkSemanticRelationsView::JumpToPosition(const mitk::DataNode* dataNode) { if (nullptr == dataNode) { return; } mitk::LabelSetImage* labelSetImage = dynamic_cast(dataNode->GetData()); if (nullptr == labelSetImage) { return; } int activeLayer = labelSetImage->GetActiveLayer(); mitk::Label* activeLabel = labelSetImage->GetActiveLabel(activeLayer); labelSetImage->UpdateCenterOfMass(activeLabel->GetValue(), activeLayer); const mitk::Point3D& centerPosition = activeLabel->GetCenterOfMassCoordinates(); if (centerPosition.GetVnlVector().max_value() > 0.0) { auto renderWindowPart = GetRenderWindowPart(); if (nullptr == renderWindowPart) { renderWindowPart = GetRenderWindowPart(QmitkAbstractView::BRING_TO_FRONT | QmitkAbstractView::OPEN); if (nullptr == renderWindowPart) { // no render window available return; } } auto segmentation = dynamic_cast(dataNode->GetData()); if (nullptr != segmentation) { renderWindowPart->SetSelectedPosition(centerPosition); mitk::RenderingManager::GetInstance()->InitializeViews(segmentation->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); } } } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.h b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.h index 15bc8dd129..4bfd5f4ab3 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.h @@ -1,107 +1,109 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QMITKSEMANTICRELATIONSVIEW_H #define QMITKSEMANTICRELATIONSVIEW_H // semantic relations plugin #include "ui_QmitkSemanticRelationsControls.h" #include "QmitkLesionInfoWidget.h" #include "QmitkSemanticRelationsContextMenu.h" // semantic relations module #include #include // semantic relations UI module +#include #include // mitk gui common plugin #include // berry #include // mitk qt #include class QmitkDnDDataNodeWidget; class QMenu; /* * @brief The QmitkSemanticRelationsView is an MITK view to combine and show the widgets of the 'SemanticRelationsUI'-module and this semantic relations plugin. * * It allows the MITK user to see and modify the content of the SemanticRelations-session. * A combo box is used to select and show the current patient. */ class QmitkSemanticRelationsView : public QmitkAbstractView, public mitk::IRenderWindowPartListener { Q_OBJECT public: static const std::string VIEW_ID; protected: virtual void SetFocus() override; virtual void CreateQtPartControl(QWidget* parent) override; virtual void RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) override; virtual void RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart) override; private Q_SLOTS: void OnLesionChanged(const mitk::SemanticTypes::Lesion&); void OnDataNodeDoubleClicked(const mitk::DataNode*); void OnCaseIDSelectionChanged(const QString&); void OnNodesAdded(QmitkDnDDataNodeWidget*, std::vector); void OnNodeRemoved(const mitk::DataNode*); private: void SetUpConnections(); /** * @brief Provide a QItemSelectionModel, which supports the data role 'QmitkDataNodeRole' (\see QmitkRenderWindowDataModel). * * The provided QItemSelectionModel is used in the QmitkAbstractView-base class as the selection model of * the selection provider (\see QmitkAbstractView::SetSelectionProvider()). * The default selection provider is a QmitkDataNodeSelectionProvider. Each time a selection in the provided * QItemSeletionModel is changed, a selection changed event is fired. All plugins (views), that subclass the * QmitkAbstractView will be informed about the selection changed via the OnSelectionChanged-function. */ virtual QItemSelectionModel* GetDataNodeSelectionModel() const override; virtual void NodeRemoved(const mitk::DataNode* dataNode) override; void AddToComboBox(const mitk::SemanticTypes::CaseID& caseID); void RemoveFromComboBox(const mitk::SemanticTypes::CaseID& caseID); void OpenInEditor(const mitk::DataNode* dataNode); void JumpToPosition(const mitk::DataNode* dataNode); Ui::QmitkSemanticRelationsControls m_Controls; QmitkLesionInfoWidget* m_LesionInfoWidget; QmitkPatientTableInspector* m_PatientTableInspector; QmitkDnDDataNodeWidget* m_DnDDataNodeWidget; + QmitkLesionTreeModel* m_LesionTreeModel; QmitkSemanticRelationsContextMenu* m_ContextMenu; std::unique_ptr m_SemanticRelations; }; #endif // QMITKSEMANTICRELATIONSVIEW_H