diff --git a/Modules/SemanticRelations/include/mitkDICOMHelper.h b/Modules/SemanticRelations/include/mitkDICOMHelper.h index 74322c0caf..e1d1d047e6 100644 --- a/Modules/SemanticRelations/include/mitkDICOMHelper.h +++ b/Modules/SemanticRelations/include/mitkDICOMHelper.h @@ -1,96 +1,96 @@ /*=================================================================== 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 MITKDICOMHELPER_H #define MITKDICOMHELPER_H #include // semantic relations module #include "mitkSemanticTypes.h" // mitk core #include #include // c++ #include /* * @brief Provides helper functions to convert DICOM Tag information. * * In order to identify the patient of an image or segmentation or to set the control point of DICOM data, * these functions are used to retrieve the DICOM tags from the given data nodes and convert them into semantic types * that can be used by the SemanticRelations class. */ namespace mitk { /* - * @brief Extracts a specific DICOM tag (currently "0x0010, 0x0020": PatientID) from the node's base data + * @brief Extracts a specific DICOM tag from the node's base data * and returns the tag as a string. This tag string is used as an identifier for the patient (case). * * @pre The given data node or the node's base data has to be valid (!nullptr). - * @pre The node's base data has to have the "0x0010, 0x0020" DICOM Tag property set. + * @pre The node's base data has to have the specific DICOM Tag property set. * @throw mitk::Exception if the given data node, the node's base data or the extracted DICOM tag are invalid (==nullptr). * * @par dataNode The data node, of which the DICOM tag should be extracted. * * @return The extracted DICOM tag as string. * An empty string, if the DICOM tag can not be extracted (i.e. the data node or * the underlying base data is invalid or the DICOM tag does not exist for the given data node). */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::CaseID GetCaseIDFromDataNode(const mitk::DataNode* dataNode); /* * @brief Extracts a specific DICOM tag (currently "0x0020, 0x000e": SeriesInstanceUID) from the node's base data * and returns the tag as a string. This tag string is used as an identifier for the image instance. * * @pre The given data node or the node's base data has to be valid (!nullptr). * @pre The node's base data has to have the "0x0020, 0x000e" DICOM Tag property set. * @throw mitk::Exception if the given data node, the node's base data or the extracted DICOM tag are invalid (==nullptr). * * @par dataNode The data node, of which the DICOM tag should be extracted. * * @return The extracted DICOM tag as string. * An empty string, if the DICOM tag can not be extracted (i.e. the data node or * the underlying base data is invalid or the DICOM tag does not exist for the given data node). */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::ID GetIDFromDataNode(const mitk::DataNode* dataNode); /* * @brief Extracts a specific DICOM tag (currently "0x0008, 0x0022": AcquisitionDate) from the node's base data * and returns the tag as a string. This tag string is used as the date of the image data. * * @pre The given data node or the node's base data has to be valid (!nullptr). * @pre The node's base data has to have the "0x0008, 0x0022" DICOM Tag property set. * @throw mitk::Exception if the given data node, the node's base data or the extracted DICOM tag are invalid (==nullptr). * * @par dataNode The data node, of which the DICOM tag should be extracted. * * @return The extracted DICOM tag as string. * An empty string, if the DICOM tag can not be extracted (i.e. the data node or * the underlying base data is invalid or the DICOM tag does not exist for the given data node). */ MITKSEMANTICRELATIONS_EXPORT SemanticTypes::Date GetDICOMDateFromDataNode(const mitk::DataNode* dataNode); /* * @brief Removes leading and trailing whitespace from the given string. * * @par identifier The value of a DICOM tag. * * @return The trimmed DICOM tag */ MITKSEMANTICRELATIONS_EXPORT std::string TrimDICOM(const std::string& identifier); } // namespace mitk #endif // MITKDICOMHELPER_H diff --git a/Modules/SemanticRelations/src/mitkDICOMHelper.cpp b/Modules/SemanticRelations/src/mitkDICOMHelper.cpp index f39e4bb8ea..8197d63fed 100644 --- a/Modules/SemanticRelations/src/mitkDICOMHelper.cpp +++ b/Modules/SemanticRelations/src/mitkDICOMHelper.cpp @@ -1,138 +1,138 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // semantic relations module #include "mitkDICOMHelper.h" #include "mitkUIDGeneratorBoost.h" // mitk core #include // c++ #include mitk::SemanticTypes::Date GetDateFromString(const std::string& dateAsString); mitk::SemanticTypes::CaseID mitk::GetCaseIDFromDataNode(const mitk::DataNode* dataNode) { if (nullptr == dataNode) { mitkThrow() << "Not a valid data node."; } mitk::BaseData* baseData = dataNode->GetData(); if (nullptr == baseData) { mitkThrow() << "No valid base data."; } // extract suitable DICOM tag to use as the case id // two alternatives can be used: // - DICOM tag "0x0010, 0x0010" is PatientName // - DICOM tag "0x0010, 0x0020" is PatientID - // in the current implementation the PatientID (0x0010, 0x0020) is used - mitk::BaseProperty* dicomTag = baseData->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0010, 0x0020).c_str()); + // in the current implementation the PatientID (0x0010, 0x0010) is used + mitk::BaseProperty* dicomTag = baseData->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str()); if (nullptr == dicomTag) { mitkThrow() << "Not a valid DICOM property."; } std::string dicomTagAsString = dicomTag->GetValueAsString(); return dicomTagAsString; } mitk::SemanticTypes::ID mitk::GetIDFromDataNode(const mitk::DataNode* dataNode) { if (nullptr == dataNode) { mitkThrow() << "Not a valid data node."; } mitk::BaseData* baseData = dataNode->GetData(); if (nullptr == baseData) { mitkThrow() << "No valid base data."; } // extract suitable DICOM tag to use as the data node id // DICOM tag "0x0020, 0x000e" is SeriesInstanceUID mitk::BaseProperty* dicomTag = baseData->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str()); if (nullptr == dicomTag) { mitkThrow() << "Not a valid DICOM property."; } std::string dicomTagAsString = dicomTag->GetValueAsString(); return dicomTagAsString; } mitk::SemanticTypes::Date mitk::GetDICOMDateFromDataNode(const mitk::DataNode* dataNode) { if (nullptr == dataNode) { mitkThrow() << "Not a valid data node."; } mitk::BaseData* baseData = dataNode->GetData(); if (nullptr == baseData) { mitkThrow() << "No valid base data."; } // extract suitable DICOM tag to use as the data node id // DICOM tag "0x0008, 0x0022" is AcquisitionDate mitk::BaseProperty* acquisitionDateProperty = baseData->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0022).c_str()); if (nullptr == acquisitionDateProperty) { mitkThrow() << "Not a valid DICOM property."; } std::string acquisitionDateAsString = acquisitionDateProperty->GetValueAsString(); return GetDateFromString(acquisitionDateAsString); } std::string mitk::TrimDICOM(const std::string& identifier) { if (identifier.empty()) { return identifier; } // leading whitespace std::size_t first = identifier.find_first_not_of(' '); if (std::string::npos == first) { return ""; } // trailing whitespace std::size_t last = identifier.find_last_not_of(' '); return identifier.substr(first, last - first + 1); } mitk::SemanticTypes::Date GetDateFromString(const std::string& dateAsString) { if (dateAsString.size() != 8) // string does not represent a DICOM date { return mitk::SemanticTypes::Date(); } mitk::SemanticTypes::Date date; date.UID = mitk::UIDGeneratorBoost::GenerateUID(); // date expected to be YYYYMMDD (8 characters) date.year = std::strtoul(dateAsString.substr(0, 4).c_str(), nullptr, 10); date.month = std::strtoul(dateAsString.substr(4, 2).c_str(), nullptr, 10); date.day = std::strtoul(dateAsString.substr(6, 2).c_str(), nullptr, 10); return date; } diff --git a/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionDialog.cpp b/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionDialog.cpp index cdba488356..1406db1518 100644 --- a/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionDialog.cpp +++ b/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionDialog.cpp @@ -1,186 +1,186 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkNodeSelectionDialog.h" #include #include #include QmitkNodeSelectionDialog::QmitkNodeSelectionDialog(QWidget* parent, QString title, QString hint) : QDialog(parent), m_NodePredicate(nullptr), m_SelectOnlyVisibleNodes(false), m_SelectedNodes(NodeList()), m_SelectionMode(QAbstractItemView::SingleSelection) { m_Controls.setupUi(this); auto providers = mitk::DataStorageInspectorGenerator::GetProviders(); auto visibleProviders = mitk::GetVisibleDataStorageInspectors(); auto favoriteID = mitk::GetFavoriteDataStorageInspector(); if (visibleProviders.empty()) { MITK_DEBUG << "No presets for visible node selection inspectors available. Use fallback (show all available inspectors)"; unsigned int order = 0; for (auto proIter : providers) { visibleProviders.insert(std::make_pair(order, proIter.first)); } } int favIndex = 0; bool favoriteFound = false; for (auto proIter : visibleProviders) { auto finding = providers.find(proIter.second); if (finding != providers.end()) { auto inspector = finding->second->CreateInspector(); QString name = QString::fromStdString(finding->second->GetInspectorDisplayName()); QString desc = QString::fromStdString(finding->second->GetInspectorDescription()); AddPanel(inspector, name, desc); favoriteFound = favoriteFound || proIter.second == favoriteID; if (!favoriteFound) { ++favIndex; } } else { MITK_DEBUG << "No provider registered for inspector that is defined as visible in the preferences. Illegal inspector ID: " << proIter.second; } } m_Controls.tabWidget->setCurrentIndex(favIndex); this->setWindowTitle(title); this->setToolTip(hint); m_Controls.hint->setText(hint); m_Controls.hint->setVisible(!hint.isEmpty()); connect(m_Controls.buttonBox, SIGNAL(accepted()), this, SLOT(OnOK())); connect(m_Controls.buttonBox, SIGNAL(rejected()), this, SLOT(OnCancel())); } void QmitkNodeSelectionDialog::SetDataStorage(mitk::DataStorage* dataStorage) { if (m_DataStorage != dataStorage) { m_DataStorage = dataStorage; if (!m_DataStorage.IsExpired()) { for (auto panel : m_Panels) { panel->SetDataStorage(dataStorage); } } } -}; +} void QmitkNodeSelectionDialog::SetNodePredicate(mitk::NodePredicateBase* nodePredicate) { if (m_NodePredicate != nodePredicate) { m_NodePredicate = nodePredicate; for (auto panel : m_Panels) { panel->SetNodePredicate(m_NodePredicate); } } -}; +} mitk::NodePredicateBase* QmitkNodeSelectionDialog::GetNodePredicate() const { return m_NodePredicate; } QmitkNodeSelectionDialog::NodeList QmitkNodeSelectionDialog::GetSelectedNodes() const { return m_SelectedNodes; -}; +} void QmitkNodeSelectionDialog::SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes) { if (m_SelectOnlyVisibleNodes != selectOnlyVisibleNodes) { m_SelectOnlyVisibleNodes = selectOnlyVisibleNodes; for (auto panel : m_Panels) { panel->SetSelectOnlyVisibleNodes(m_SelectOnlyVisibleNodes); } } -}; +} void QmitkNodeSelectionDialog::SetCurrentSelection(NodeList selectedNodes) { m_SelectedNodes = selectedNodes; for (auto panel : m_Panels) { panel->SetCurrentSelection(selectedNodes); } -}; +} void QmitkNodeSelectionDialog::OnSelectionChanged(NodeList selectedNodes) { SetCurrentSelection(selectedNodes); emit CurrentSelectionChanged(selectedNodes); -}; +} void QmitkNodeSelectionDialog::AddPanel(QmitkAbstractDataStorageInspector* view, QString name, QString desc) { view->setParent(this); view->SetSelectionMode(m_SelectionMode); auto tabPanel = new QWidget(); tabPanel->setObjectName(QString("tab_")+name); tabPanel->setToolTip(desc); m_Controls.tabWidget->insertTab(m_Controls.tabWidget->count(), tabPanel, name); auto verticalLayout = new QVBoxLayout(tabPanel); verticalLayout->setSpacing(0); verticalLayout->setContentsMargins(0, 0, 0, 0); verticalLayout->addWidget(view); m_Panels.push_back(view); connect(view, &QmitkAbstractDataStorageInspector::CurrentSelectionChanged, this, &QmitkNodeSelectionDialog::OnSelectionChanged); -}; +} void QmitkNodeSelectionDialog::OnOK() { this->accept(); -}; +} void QmitkNodeSelectionDialog::OnCancel() { this->reject(); -}; +} void QmitkNodeSelectionDialog::SetSelectionMode(SelectionMode mode) { m_SelectionMode = mode; for (auto panel : m_Panels) { panel->SetSelectionMode(mode); } -}; +} QmitkNodeSelectionDialog::SelectionMode QmitkNodeSelectionDialog::GetSelectionMode() const { return m_SelectionMode; } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/CMakeLists.txt b/Plugins/org.mitk.gui.qt.semanticrelations/CMakeLists.txt index ade7a57843..2d9453bace 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/CMakeLists.txt +++ b/Plugins/org.mitk.gui.qt.semanticrelations/CMakeLists.txt @@ -1,7 +1,7 @@ project(org_mitk_gui_qt_semanticrelations) mitk_create_plugin( EXPORT_DIRECTIVE MITK_GUI_SEMANTICRELATIONS_EXPORT EXPORTED_INCLUDE_SUFFIXES src - MODULE_DEPENDS MitkQtWidgetsExt MitkPersistence MitkSemanticRelationsUI + MODULE_DEPENDS MitkPersistence MitkSemanticRelationsUI ) 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 8797c8af8b..699c8e77b9 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.cpp @@ -1,317 +1,288 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // semantic relations plugin #include "QmitkSemanticRelationsView.h" +#include "QmitkSemanticRelationsNodeSelectionDialog.h" // semantic relations module #include #include #include // blueberry #include #include // qt #include const std::string QmitkSemanticRelationsView::VIEW_ID = "org.mitk.views.semanticrelations"; QmitkSemanticRelationsView::QmitkSemanticRelationsView() - : m_PatientTableWidget(nullptr) - , m_SimpleDatamanagerWidget(nullptr) - , m_SelectPatientNodeDialog(nullptr) - , m_SimpleDatamanagerNodeDialog(nullptr) - , m_SemanticRelations(nullptr) + : m_SemanticRelations(nullptr) { // nothing here } void QmitkSemanticRelationsView::SetFocus() { // nothing here } void QmitkSemanticRelationsView::CreateQtPartControl(QWidget* parent) { // create GUI widgets + m_Parent = parent; m_Controls.setupUi(parent); // initialize the semantic relations m_SemanticRelations = std::make_unique(GetDataStorage()); connect(m_Controls.caseIDComboBox, SIGNAL(currentIndexChanged(const QString&)), SLOT(OnCaseIDSelectionChanged(const QString&))); connect(m_Controls.selectPatientNodePushButton, SIGNAL(clicked()), SLOT(OnSelectPatientNodeButtonClicked())); m_LesionInfoWidget = new QmitkLesionInfoWidget(GetDataStorage()); m_Controls.gridLayout->addWidget(m_LesionInfoWidget, 3, 0, 1, 3); connect(m_LesionInfoWidget, SIGNAL(JumpToPosition(const mitk::Point3D&)), SLOT(OnJumpToPosition(const mitk::Point3D&))); connect(m_LesionInfoWidget, SIGNAL(ImageRemoved(const mitk::DataNode*)), SLOT(OnImageRemoved(const mitk::DataNode*))); - m_PatientTableWidget = new QmitkPatientTableWidget(GetDataStorage()); - - m_SelectPatientNodeDialog = new QmitkSelectNodeDialog(parent); - m_SelectPatientNodeDialog->setWindowTitle("Select patient image node"); - m_SelectPatientNodeDialog->SetSelectionWidget(m_PatientTableWidget); - - m_SimpleDatamanagerWidget = new QmitkSimpleDatamanagerWidget(GetDataStorage()); - m_SimpleDatamanagerNodeDialog = new QmitkSelectNodeDialog(parent); - m_SimpleDatamanagerNodeDialog->setWindowTitle("Select node"); - m_SimpleDatamanagerNodeDialog->SetSelectionWidget(m_SimpleDatamanagerWidget); - // connect buttons to modify semantic relations connect(m_Controls.addLesionPushButton, SIGNAL(clicked()), SLOT(OnAddLesionButtonClicked())); connect(m_Controls.addSegmentationPushButton, SIGNAL(clicked()), SLOT(OnAddSegmentationButtonClicked())); connect(m_Controls.addImagePushButton, SIGNAL(clicked()), SLOT(OnAddImageButtonClicked())); } void QmitkSemanticRelationsView::NodeRemoved(const mitk::DataNode* node) { // ToDo: automatically remove node from storage, so that there will be no access anymore } void QmitkSemanticRelationsView::OnCaseIDSelectionChanged(const QString& caseID) { m_CaseID = caseID.toStdString(); m_LesionInfoWidget->SetCurrentCaseID(m_CaseID); - m_PatientTableWidget->SetCurrentCaseID(m_CaseID); } void QmitkSemanticRelationsView::OnSelectPatientNodeButtonClicked() { - int dialogReturnValue = m_SelectPatientNodeDialog->exec(); - if (QDialog::Rejected == dialogReturnValue) - { - return; - } - - mitk::DataNode* selectedDataNode = m_SelectPatientNodeDialog->GetSelectedDataNode(); - if (nullptr == selectedDataNode) - { - m_Controls.selectedPatientNodeLineEdit->clear(); - } - else + QmitkSemanticRelationsNodeSelectionDialog* dialog = new QmitkSemanticRelationsNodeSelectionDialog(m_Parent, "Select patient image to set a current patient", ""); + dialog->SetDataStorage(GetDataStorage()); + dialog->setWindowTitle("Select patient image node"); + dialog->SetNodePredicate(mitk::NodePredicates::GetImagePredicate()); + dialog->SetSelectOnlyVisibleNodes(true); + dialog->SetSelectionMode(QAbstractItemView::SingleSelection); + dialog->SetCaseID(m_CaseID); + + if (QDialog::Accepted == dialog->exec()) { - m_Controls.selectedPatientNodeLineEdit->setText(QString::fromStdString(selectedDataNode->GetName())); + auto nodes = dialog->GetSelectedNodes(); + for (mitk::DataNode* dataNode : nodes) + { + if (nullptr != dataNode) + { + m_Controls.selectedPatientNodeLineEdit->setText(QString::fromStdString(dataNode->GetName())); + //m_PatientInfoWidget->SetPatientInfo(node); + //m_PatientInfoWidget->show(); + } + else + { + m_Controls.selectedPatientNodeLineEdit->clear(); + } + } } - //m_PatientInfoWidget->SetPatientInfo(node); - //m_PatientInfoWidget->show(); } void QmitkSemanticRelationsView::OnJumpToPosition(const mitk::Point3D& position) { mitk::IRenderWindowPart* renderWindowPart = GetRenderWindowPart(); if (nullptr != renderWindowPart) { renderWindowPart->SetSelectedPosition(position); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSemanticRelationsView::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; newLesion.UID = mitk::UIDGeneratorBoost::GenerateUID(); newLesion.lesionClass = mitk::SemanticTypes::LesionClass(); newLesion.lesionClass.UID = mitk::UIDGeneratorBoost::GenerateUID(); try { m_SemanticRelations->AddLesion(m_CaseID, newLesion); } catch (mitk::SemanticRelationException& e) { MITK_INFO << "Could not add a new lesion. " << e; } } void QmitkSemanticRelationsView::OnAddSegmentationButtonClicked() { if (m_CaseID.empty()) { QMessageBox msgBox; msgBox.setWindowTitle("No case ID set."); msgBox.setText("In order to add a segmentation, please specify the current case / patient."); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); return; } - int dialogReturnValue = m_SimpleDatamanagerNodeDialog->exec(); - if (QDialog::Rejected == dialogReturnValue) - { - return; - } + QmitkSemanticRelationsNodeSelectionDialog* dialog = new QmitkSemanticRelationsNodeSelectionDialog(m_Parent, "Select segmentations to add to the semantic relations storage", ""); + dialog->SetDataStorage(GetDataStorage()); + dialog->setWindowTitle("Select segmentation node"); + dialog->SetNodePredicate(mitk::NodePredicates::GetSegmentationPredicate()); + dialog->SetSelectOnlyVisibleNodes(true); + dialog->SetSelectionMode(QAbstractItemView::MultiSelection); + dialog->SetCaseID(m_CaseID); - mitk::DataNode* selectedDataNode = m_SimpleDatamanagerNodeDialog->GetSelectedDataNode(); - if (nullptr == selectedDataNode) + if (QDialog::Accepted == dialog->exec()) { - QMessageBox msgBox; - msgBox.setWindowTitle("No valid segmentation node selected."); - msgBox.setText("In order to add a segmentation, please specify a valid segmentation node."); - msgBox.setIcon(QMessageBox::Warning); - msgBox.exec(); - return; - } - - if (!mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(selectedDataNode)) - { - QMessageBox msgBox; - msgBox.setWindowTitle("No segmentation selected"); - msgBox.setText("In order to add 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; - } - - // continue with valid segmentation data - // get parent node of the current segmentation node with the node predicate - mitk::DataStorage::SetOfObjects::ConstPointer parentNodes = GetDataStorage()->GetSources(selectedDataNode, mitk::NodePredicates::GetImagePredicate(), false); - - // check for already existing, identifying base properties - mitk::BaseProperty* caseIDProperty = baseData->GetProperty("DICOM.0010.0010"); - mitk::BaseProperty* nodeIDProperty = baseData->GetProperty("DICOM.0020.000E"); - if (nullptr == caseIDProperty && nullptr == nodeIDProperty) - { - MITK_INFO << "No DICOM tags for case and node identification found. Transferring DICOM tags from the parent node to the selected segmentation node."; - - mitk::SemanticTypes::CaseID caseID = mitk::GetCaseIDFromDataNode(parentNodes->front()); - mitk::SemanticTypes::ID nodeID = mitk::GetIDFromDataNode(parentNodes->front()); - // transfer DICOM tags to the segmentation node - mitk::StringProperty::Pointer caseIDTag = mitk::StringProperty::New(caseID); - baseData->SetProperty("DICOM.0010.0010", caseIDTag); // DICOM tag is "PatientName" - - // add UID to distinguish between different segmentations of the same parent node - mitk::StringProperty::Pointer nodeIDTag = mitk::StringProperty::New(nodeID + mitk::UIDGeneratorBoost::GenerateUID()); - baseData->SetProperty("DICOM.0020.000E", nodeIDTag); // DICOM tag is "SeriesInstanceUID" - } - - try - { - m_SemanticRelations->AddSegmentation(selectedDataNode, parentNodes->front()); - } - catch (mitk::Exception& e) - { - std::stringstream exceptionMessage; exceptionMessage << e; - QMessageBox msgBox; - msgBox.setWindowTitle("Could not add the selected segmentation."); - msgBox.setText("The program wasn't able to correctly add the selected segmentation.\n" - "Reason:\n" + QString::fromStdString(exceptionMessage.str())); - msgBox.setIcon(QMessageBox::Warning); - msgBox.exec(); + auto nodes = dialog->GetSelectedNodes(); + for (mitk::DataNode* dataNode : nodes) + { + if (nullptr == dataNode) + { + continue; + } + + mitk::BaseData* baseData = dataNode->GetData(); + if (nullptr == baseData) + { + continue; + } + + // continue with valid segmentation data + // get parent node of the current segmentation node with the node predicate + mitk::DataStorage::SetOfObjects::ConstPointer parentNodes = GetDataStorage()->GetSources(dataNode, mitk::NodePredicates::GetImagePredicate(), false); + + // check for already existing, identifying base properties + mitk::BaseProperty* caseIDProperty = baseData->GetProperty("DICOM.0010.0010"); + mitk::BaseProperty* nodeIDProperty = baseData->GetProperty("DICOM.0020.000E"); + if (nullptr == caseIDProperty && nullptr == nodeIDProperty) + { + MITK_INFO << "No DICOM tags for case and node identification found. Transferring DICOM tags from the parent node to the selected segmentation node."; + + mitk::SemanticTypes::CaseID caseID = mitk::GetCaseIDFromDataNode(parentNodes->front()); + mitk::SemanticTypes::ID nodeID = mitk::GetIDFromDataNode(parentNodes->front()); + // transfer DICOM tags to the segmentation node + mitk::StringProperty::Pointer caseIDTag = mitk::StringProperty::New(caseID); + baseData->SetProperty("DICOM.0010.0010", caseIDTag); // DICOM tag is "PatientName" + + // add UID to distinguish between different segmentations of the same parent node + mitk::StringProperty::Pointer nodeIDTag = mitk::StringProperty::New(nodeID + mitk::UIDGeneratorBoost::GenerateUID()); + baseData->SetProperty("DICOM.0020.000E", nodeIDTag); // DICOM tag is "SeriesInstanceUID" + } + + try + { + m_SemanticRelations->AddSegmentation(dataNode, parentNodes->front()); + } + catch (mitk::Exception& e) + { + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox; + msgBox.setWindowTitle("Could not add the selected segmentation."); + msgBox.setText("The program wasn't able to correctly add the selected segmentation.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str())); + msgBox.setIcon(QMessageBox::Warning); + msgBox.exec(); + } + } } } void QmitkSemanticRelationsView::OnAddImageButtonClicked() { - int dialogReturnValue = m_SimpleDatamanagerNodeDialog->exec(); - if (QDialog::Rejected == dialogReturnValue) - { - return; - } - - mitk::DataNode* selectedDataNode = m_SimpleDatamanagerNodeDialog->GetSelectedDataNode(); - if (nullptr == selectedDataNode) - { - QMessageBox msgBox; - msgBox.setWindowTitle("No valid image node selected."); - msgBox.setText("In order to add an image, please specify a valid image node."); - msgBox.setIcon(QMessageBox::Warning); - msgBox.exec(); - return; - } - - if (!mitk::NodePredicates::GetImagePredicate()->CheckNode(selectedDataNode)) + QmitkSemanticRelationsNodeSelectionDialog* dialog = new QmitkSemanticRelationsNodeSelectionDialog(m_Parent, "Select images to add to the semantic relations storage", ""); + dialog->SetDataStorage(GetDataStorage()); + dialog->setWindowTitle("Select image node"); + dialog->SetNodePredicate(mitk::NodePredicates::GetImagePredicate()); + dialog->SetSelectOnlyVisibleNodes(true); + dialog->SetSelectionMode(QAbstractItemView::MultiSelection); + dialog->SetCaseID(m_CaseID); + + if (QDialog::Accepted == dialog->exec()) { - QMessageBox msgBox; - msgBox.setWindowTitle("No image selected"); - msgBox.setText("In order to add an image, please specify a valid image node."); - msgBox.setIcon(QMessageBox::Warning); - msgBox.exec(); - return; - } - - try - { - // add the image to the semantic relations storage - m_SemanticRelations->AddImage(selectedDataNode); - m_PatientTableWidget->SetPixmapOfNode(selectedDataNode); - mitk::SemanticTypes::CaseID caseID = mitk::GetCaseIDFromDataNode(selectedDataNode); - AddToComboBox(caseID); - } - catch (mitk::Exception& e) - { - std::stringstream exceptionMessage; exceptionMessage << e; - QMessageBox msgBox; - msgBox.setWindowTitle("Could not add the selected image."); - msgBox.setText("The program wasn't able to correctly add the selected image.\n" - "Reason:\n" + QString::fromStdString(exceptionMessage.str())); - msgBox.setIcon(QMessageBox::Warning); - msgBox.exec(); + auto nodes = dialog->GetSelectedNodes(); + for (mitk::DataNode* dataNode : nodes) + { + if (nullptr != dataNode) + { + try + { + // add the image to the semantic relations storage + m_SemanticRelations->AddImage(dataNode); + mitk::SemanticTypes::CaseID caseID = mitk::GetCaseIDFromDataNode(dataNode); + AddToComboBox(caseID); + } + catch (mitk::Exception& e) + { + std::stringstream exceptionMessage; exceptionMessage << e; + QMessageBox msgBox; + msgBox.setWindowTitle("Could not add the selected image."); + msgBox.setText("The program wasn't able to correctly add the selected images.\n" + "Reason:\n" + QString::fromStdString(exceptionMessage.str())); + msgBox.setIcon(QMessageBox::Warning); + msgBox.exec(); + } + } + } } } void QmitkSemanticRelationsView::OnImageRemoved(const mitk::DataNode* imageNode) { - m_PatientTableWidget->DeletePixmapOfNode(imageNode); mitk::SemanticTypes::CaseID caseID = mitk::GetCaseIDFromDataNode(imageNode); RemoveFromComboBox(caseID); } 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); } } \ No newline at end of file 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 fcff8cac3c..9e6d3cdf8a 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.h +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkSemanticRelationsView.h @@ -1,97 +1,91 @@ /*=================================================================== 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" // semantic relations module #include #include // semantic relations UI module #include -#include -#include -#include // blueberry #include // mitk qt #include /* * @brief The QmitkSemanticRelationsView is a MITK view to combine and show the widgets of the 'SemanticRelationsUI'-module. * * It allows the MITK user to see and modify the content of the SemanticRelations-session. * It provides a dialog to add images from the data storage to the semantic relations model and a dialog to select * data nodes that were added to the semantic relations model before. * If provides functionality to create new lesions and link them with segmentation nodes. * A combo box is used to select and show the current patient. * * SIDE NOTE: Modifying the control points and information types of data in the semantic relations model is currently done * by using the right-click context-menu of the "PatientTableWidget" in the "Select Patient Node"-Dialog. * This is a leftover from when the widget was not used as a selection widget. Those functionality will be moved * to this main GUI soon. */ class QmitkSemanticRelationsView : public QmitkAbstractView { Q_OBJECT public: static const std::string VIEW_ID; QmitkSemanticRelationsView(); protected: virtual void SetFocus() override; virtual void CreateQtPartControl(QWidget* parent) override; private Q_SLOTS: void OnCaseIDSelectionChanged(const QString&); void OnSelectPatientNodeButtonClicked(); void OnJumpToPosition(const mitk::Point3D&); /* * @brief Generates a new, empty lesion to add to the semantic relations model for the current case ID. */ void OnAddLesionButtonClicked(); void OnAddSegmentationButtonClicked(); void OnAddImageButtonClicked(); void OnImageRemoved(const mitk::DataNode*); private: virtual void NodeRemoved(const mitk::DataNode* node) override; void AddToComboBox(const mitk::SemanticTypes::CaseID& caseID); void RemoveFromComboBox(const mitk::SemanticTypes::CaseID& caseID); + QWidget* m_Parent; Ui::QmitkSemanticRelationsControls m_Controls; QmitkLesionInfoWidget* m_LesionInfoWidget; - QmitkPatientTableWidget* m_PatientTableWidget; - QmitkSimpleDatamanagerWidget* m_SimpleDatamanagerWidget; - QmitkSelectNodeDialog* m_SelectPatientNodeDialog; - QmitkSelectNodeDialog* m_SimpleDatamanagerNodeDialog; mitk::SemanticTypes::CaseID m_CaseID; std::unique_ptr m_SemanticRelations; }; #endif // QMITKSEMANTICRELATIONSVIEW_H